Przeglądaj źródła

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

CrazyIter_Bin 3 miesięcy temu
rodzic
commit
1e376e468c
21 zmienionych plików z 1757 dodań i 679 usunięć
  1. 1 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/package.json
  2. 1 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/api/http.js
  3. 13 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/api/index.js
  4. 1 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/icon/no_data.svg
  5. 45 413
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/demo_index.html
  6. 11 75
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.css
  7. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.js
  8. 14 126
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.json
  9. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.ttf
  10. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff
  11. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff2
  12. 4 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/main.js
  13. 847 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/utils/evTools.js
  14. 4 20
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/utils/public.js
  15. 2 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/ActivityManage.less
  16. 176 35
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/ActivityManage.vue
  17. 264 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/TestPaper.less
  18. 315 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/TestPaper.vue
  19. 3 3
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/login/Admin.vue
  20. 46 4
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/login/Student.vue
  21. 9 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/vue.config.js

+ 1 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/package.json

@@ -18,6 +18,7 @@
     "element-ui": "^2.15.14",
     "element-ui": "^2.15.14",
     "js-audio-recorder": "^1.0.7",
     "js-audio-recorder": "^1.0.7",
     "jwt-decode": "^4.0.0",
     "jwt-decode": "^4.0.0",
+    "lodash": "^4.17.21",
     "qrcodejs2": "^0.0.2",
     "qrcodejs2": "^0.0.2",
     "vue": "^2.6.14",
     "vue": "^2.6.14",
     "vue-router": "^3.6.5",
     "vue-router": "^3.6.5",

+ 1 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/api/http.js

@@ -9,6 +9,7 @@ const NO_ACCESS_API = [
     '/index/login-check',
     '/index/login-check',
     '/index/list-schools',
     '/index/list-schools',
     '/index/bind-school',
     '/index/bind-school',
+    '/manage/get-activate-evaluation',
 ]
 ]
 
 
 // 需要携带access_token 不需要auth-token
 // 需要携带access_token 不需要auth-token

+ 13 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/api/index.js

@@ -82,6 +82,7 @@ export default {
      * @param {String} evaluationId - 评测id
      * @param {String} evaluationId - 评测id
      * @param {String} openCode - 开卷码
      * @param {String} openCode - 开卷码
      * @param {String} shortCode - 提取码
      * @param {String} shortCode - 提取码
+     * @param {String} settingId - 轮次设置id
      */
      */
     getExamRoundInfo: function (data) {
     getExamRoundInfo: function (data) {
         return post('/manage/load-evaluation-round', data)
         return post('/manage/load-evaluation-round', data)
@@ -101,6 +102,18 @@ export default {
     setExamRoundInfo: function (data) {
     setExamRoundInfo: function (data) {
         return post('/manage/setting-evaluation-round', data)
         return post('/manage/setting-evaluation-round', data)
     },
     },
+    /**
+     * 当前评测的开考设置列表信息
+     * @param {String} evaluationId - 评测id
+     * @param {String} openCode - 开卷码
+     * @param {String} shortCode - 提取码
+     */
+    getRoundList: function(data) {
+        return post('/manage/list-evaluation-round', data)
+    },
 
 
     // 学生页面
     // 学生页面
+    stuGetEvaluation: function(data) {
+        return post('/manage/get-activate-evaluation', data)
+    },
 }
 }

Plik diff jest za duży
+ 1 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/icon/no_data.svg


+ 45 - 413
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/demo_index.html

@@ -54,6 +54,18 @@
       <div class="content unicode" style="display: block;">
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           <ul class="icon_lists dib-box">
           
           
+            <li class="dib">
+              <span class="icon element-icons">&#xe615;</span>
+                <div class="name">07_箭头_向下</div>
+                <div class="code-name">&amp;#xe615;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon element-icons">&#xee1a;</span>
+                <div class="name">07_箭头_向上</div>
+                <div class="code-name">&amp;#xee1a;</div>
+              </li>
+          
             <li class="dib">
             <li class="dib">
               <span class="icon element-icons">&#xe614;</span>
               <span class="icon element-icons">&#xe614;</span>
                 <div class="name">管理中心</div>
                 <div class="name">管理中心</div>
@@ -72,126 +84,18 @@
                 <div class="code-name">&amp;#xe9d9;</div>
                 <div class="code-name">&amp;#xe9d9;</div>
               </li>
               </li>
           
           
-            <li class="dib">
-              <span class="icon element-icons">&#xe6b4;</span>
-                <div class="name">电脑</div>
-                <div class="code-name">&amp;#xe6b4;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe604;</span>
-                <div class="name">电脑</div>
-                <div class="code-name">&amp;#xe604;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe608;</span>
-                <div class="name">iconfont_disconnected</div>
-                <div class="code-name">&amp;#xe608;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe663;</span>
-                <div class="name">下载失败</div>
-                <div class="code-name">&amp;#xe663;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe7e8;</span>
-                <div class="name">云端-关闭-填充</div>
-                <div class="code-name">&amp;#xe7e8;</div>
-              </li>
-          
             <li class="dib">
             <li class="dib">
               <span class="icon element-icons">&#xe7e9;</span>
               <span class="icon element-icons">&#xe7e9;</span>
                 <div class="name">云端-关闭-线性</div>
                 <div class="name">云端-关闭-线性</div>
                 <div class="code-name">&amp;#xe7e9;</div>
                 <div class="code-name">&amp;#xe7e9;</div>
               </li>
               </li>
           
           
-            <li class="dib">
-              <span class="icon element-icons">&#xe617;</span>
-                <div class="name">云端勾</div>
-                <div class="code-name">&amp;#xe617;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe640;</span>
-                <div class="name">云端链接</div>
-                <div class="code-name">&amp;#xe640;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe6bf;</span>
-                <div class="name">云端</div>
-                <div class="code-name">&amp;#xe6bf;</div>
-              </li>
-          
             <li class="dib">
             <li class="dib">
               <span class="icon element-icons">&#xe60d;</span>
               <span class="icon element-icons">&#xe60d;</span>
                 <div class="name">云端资源</div>
                 <div class="name">云端资源</div>
                 <div class="code-name">&amp;#xe60d;</div>
                 <div class="code-name">&amp;#xe60d;</div>
               </li>
               </li>
           
           
-            <li class="dib">
-              <span class="icon element-icons">&#xee19;</span>
-                <div class="name">云端</div>
-                <div class="code-name">&amp;#xee19;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe638;</span>
-                <div class="name">云端存储</div>
-                <div class="code-name">&amp;#xe638;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe601;</span>
-                <div class="name">云端</div>
-                <div class="code-name">&amp;#xe601;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe603;</span>
-                <div class="name">云端连接</div>
-                <div class="code-name">&amp;#xe603;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe602;</span>
-                <div class="name">云端存储</div>
-                <div class="code-name">&amp;#xe602;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe731;</span>
-                <div class="name">云端默认</div>
-                <div class="code-name">&amp;#xe731;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe648;</span>
-                <div class="name">上传云端失败</div>
-                <div class="code-name">&amp;#xe648;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe64e;</span>
-                <div class="name">云端</div>
-                <div class="code-name">&amp;#xe64e;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe60f;</span>
-                <div class="name">云端,链接</div>
-                <div class="code-name">&amp;#xe60f;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe698;</span>
-                <div class="name">云端</div>
-                <div class="code-name">&amp;#xe698;</div>
-              </li>
-          
             <li class="dib">
             <li class="dib">
               <span class="icon element-icons">&#xe618;</span>
               <span class="icon element-icons">&#xe618;</span>
                 <div class="name">时间</div>
                 <div class="name">时间</div>
@@ -330,9 +234,9 @@
 <pre><code class="language-css"
 <pre><code class="language-css"
 >@font-face {
 >@font-face {
   font-family: 'element-icons';
   font-family: 'element-icons';
-  src: url('iconfont.woff2?t=1739527479205') format('woff2'),
-       url('iconfont.woff?t=1739527479205') format('woff'),
-       url('iconfont.ttf?t=1739527479205') format('truetype');
+  src: url('iconfont.woff2?t=1740223260776') format('woff2'),
+       url('iconfont.woff?t=1740223260776') format('woff'),
+       url('iconfont.ttf?t=1740223260776') format('truetype');
 }
 }
 </code></pre>
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -359,74 +263,47 @@
         <ul class="icon_lists dib-box">
         <ul class="icon_lists dib-box">
           
           
           <li class="dib">
           <li class="dib">
-            <span class="icon element-icons el-icon-guanlizhongxin"></span>
+            <span class="icon element-icons el-icon-jiantouxiangxia"></span>
             <div class="name">
             <div class="name">
-              管理中心
+              07_箭头_向下
             </div>
             </div>
-            <div class="code-name">.el-icon-guanlizhongxin
+            <div class="code-name">.el-icon-jiantouxiangxia
             </div>
             </div>
           </li>
           </li>
           
           
           <li class="dib">
           <li class="dib">
-            <span class="icon element-icons el-icon-a-ziyuan491"></span>
+            <span class="icon element-icons el-icon-jiantouxiangshang"></span>
             <div class="name">
             <div class="name">
-              工作台电脑
+              07_箭头_向上
             </div>
             </div>
-            <div class="code-name">.el-icon-a-ziyuan491
+            <div class="code-name">.el-icon-jiantouxiangshang
             </div>
             </div>
           </li>
           </li>
           
           
           <li class="dib">
           <li class="dib">
-            <span class="icon element-icons el-icon-mti-diannao"></span>
-            <div class="name">
-              mti-电脑
-            </div>
-            <div class="code-name">.el-icon-mti-diannao
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-diannao2"></span>
-            <div class="name">
-              电脑
-            </div>
-            <div class="code-name">.el-icon-diannao2
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-diannao1"></span>
-            <div class="name">
-              电脑
-            </div>
-            <div class="code-name">.el-icon-diannao1
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-iconfont_disconnected"></span>
+            <span class="icon element-icons el-icon-guanlizhongxin"></span>
             <div class="name">
             <div class="name">
-              iconfont_disconnected
+              管理中心
             </div>
             </div>
-            <div class="code-name">.el-icon-iconfont_disconnected
+            <div class="code-name">.el-icon-guanlizhongxin
             </div>
             </div>
           </li>
           </li>
           
           
           <li class="dib">
           <li class="dib">
-            <span class="icon element-icons el-icon-xiazaishibai"></span>
+            <span class="icon element-icons el-icon-a-ziyuan491"></span>
             <div class="name">
             <div class="name">
-              下载失败
+              工作台电脑
             </div>
             </div>
-            <div class="code-name">.el-icon-xiazaishibai
+            <div class="code-name">.el-icon-a-ziyuan491
             </div>
             </div>
           </li>
           </li>
           
           
           <li class="dib">
           <li class="dib">
-            <span class="icon element-icons el-icon-yunduan-guanbi-tianchong"></span>
+            <span class="icon element-icons el-icon-mti-diannao"></span>
             <div class="name">
             <div class="name">
-              云端-关闭-填充
+              mti-电脑
             </div>
             </div>
-            <div class="code-name">.el-icon-yunduan-guanbi-tianchong
+            <div class="code-name">.el-icon-mti-diannao
             </div>
             </div>
           </li>
           </li>
           
           
@@ -439,33 +316,6 @@
             </div>
             </div>
           </li>
           </li>
           
           
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduangou"></span>
-            <div class="name">
-              云端勾
-            </div>
-            <div class="code-name">.el-icon-yunduangou
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduanlianjie"></span>
-            <div class="name">
-              云端链接
-            </div>
-            <div class="code-name">.el-icon-yunduanlianjie
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduan"></span>
-            <div class="name">
-              云端
-            </div>
-            <div class="code-name">.el-icon-yunduan
-            </div>
-          </li>
-          
           <li class="dib">
           <li class="dib">
             <span class="icon element-icons el-icon-yunduanziyuan"></span>
             <span class="icon element-icons el-icon-yunduanziyuan"></span>
             <div class="name">
             <div class="name">
@@ -475,96 +325,6 @@
             </div>
             </div>
           </li>
           </li>
           
           
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduan1"></span>
-            <div class="name">
-              云端
-            </div>
-            <div class="code-name">.el-icon-yunduan1
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduancunchu"></span>
-            <div class="name">
-              云端存储
-            </div>
-            <div class="code-name">.el-icon-yunduancunchu
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduan2"></span>
-            <div class="name">
-              云端
-            </div>
-            <div class="code-name">.el-icon-yunduan2
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-a-huaban1fuben25"></span>
-            <div class="name">
-              云端连接
-            </div>
-            <div class="code-name">.el-icon-a-huaban1fuben25
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduancunchu1"></span>
-            <div class="name">
-              云端存储
-            </div>
-            <div class="code-name">.el-icon-yunduancunchu1
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduanmoren"></span>
-            <div class="name">
-              云端默认
-            </div>
-            <div class="code-name">.el-icon-yunduanmoren
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-shangchuanyunduanshibai"></span>
-            <div class="name">
-              上传云端失败
-            </div>
-            <div class="code-name">.el-icon-shangchuanyunduanshibai
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduan3"></span>
-            <div class="name">
-              云端
-            </div>
-            <div class="code-name">.el-icon-yunduan3
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-a-yunduanlianjie"></span>
-            <div class="name">
-              云端,链接
-            </div>
-            <div class="code-name">.el-icon-a-yunduanlianjie
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yunduan4"></span>
-            <div class="name">
-              云端
-            </div>
-            <div class="code-name">.el-icon-yunduan4
-            </div>
-          </li>
-          
           <li class="dib">
           <li class="dib">
             <span class="icon element-icons el-icon-shijian"></span>
             <span class="icon element-icons el-icon-shijian"></span>
             <div class="name">
             <div class="name">
@@ -774,66 +534,42 @@
           
           
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-guanlizhongxin"></use>
-                </svg>
-                <div class="name">管理中心</div>
-                <div class="code-name">#el-icon-guanlizhongxin</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-a-ziyuan491"></use>
+                  <use xlink:href="#el-icon-jiantouxiangxia"></use>
                 </svg>
                 </svg>
-                <div class="name">工作台电脑</div>
-                <div class="code-name">#el-icon-a-ziyuan491</div>
+                <div class="name">07_箭头_向下</div>
+                <div class="code-name">#el-icon-jiantouxiangxia</div>
             </li>
             </li>
           
           
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-mti-diannao"></use>
+                  <use xlink:href="#el-icon-jiantouxiangshang"></use>
                 </svg>
                 </svg>
-                <div class="name">mti-电脑</div>
-                <div class="code-name">#el-icon-mti-diannao</div>
+                <div class="name">07_箭头_向上</div>
+                <div class="code-name">#el-icon-jiantouxiangshang</div>
             </li>
             </li>
           
           
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-diannao2"></use>
-                </svg>
-                <div class="name">电脑</div>
-                <div class="code-name">#el-icon-diannao2</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-diannao1"></use>
-                </svg>
-                <div class="name">电脑</div>
-                <div class="code-name">#el-icon-diannao1</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-iconfont_disconnected"></use>
+                  <use xlink:href="#el-icon-guanlizhongxin"></use>
                 </svg>
                 </svg>
-                <div class="name">iconfont_disconnected</div>
-                <div class="code-name">#el-icon-iconfont_disconnected</div>
+                <div class="name">管理中心</div>
+                <div class="code-name">#el-icon-guanlizhongxin</div>
             </li>
             </li>
           
           
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-xiazaishibai"></use>
+                  <use xlink:href="#el-icon-a-ziyuan491"></use>
                 </svg>
                 </svg>
-                <div class="name">下载失败</div>
-                <div class="code-name">#el-icon-xiazaishibai</div>
+                <div class="name">工作台电脑</div>
+                <div class="code-name">#el-icon-a-ziyuan491</div>
             </li>
             </li>
           
           
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduan-guanbi-tianchong"></use>
+                  <use xlink:href="#el-icon-mti-diannao"></use>
                 </svg>
                 </svg>
-                <div class="name">云端-关闭-填充</div>
-                <div class="code-name">#el-icon-yunduan-guanbi-tianchong</div>
+                <div class="name">mti-电脑</div>
+                <div class="code-name">#el-icon-mti-diannao</div>
             </li>
             </li>
           
           
             <li class="dib">
             <li class="dib">
@@ -844,30 +580,6 @@
                 <div class="code-name">#el-icon-yunduan-guanbi-xianxing</div>
                 <div class="code-name">#el-icon-yunduan-guanbi-xianxing</div>
             </li>
             </li>
           
           
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduangou"></use>
-                </svg>
-                <div class="name">云端勾</div>
-                <div class="code-name">#el-icon-yunduangou</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduanlianjie"></use>
-                </svg>
-                <div class="name">云端链接</div>
-                <div class="code-name">#el-icon-yunduanlianjie</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduan"></use>
-                </svg>
-                <div class="name">云端</div>
-                <div class="code-name">#el-icon-yunduan</div>
-            </li>
-          
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#el-icon-yunduanziyuan"></use>
                   <use xlink:href="#el-icon-yunduanziyuan"></use>
@@ -876,86 +588,6 @@
                 <div class="code-name">#el-icon-yunduanziyuan</div>
                 <div class="code-name">#el-icon-yunduanziyuan</div>
             </li>
             </li>
           
           
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduan1"></use>
-                </svg>
-                <div class="name">云端</div>
-                <div class="code-name">#el-icon-yunduan1</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduancunchu"></use>
-                </svg>
-                <div class="name">云端存储</div>
-                <div class="code-name">#el-icon-yunduancunchu</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduan2"></use>
-                </svg>
-                <div class="name">云端</div>
-                <div class="code-name">#el-icon-yunduan2</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-a-huaban1fuben25"></use>
-                </svg>
-                <div class="name">云端连接</div>
-                <div class="code-name">#el-icon-a-huaban1fuben25</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduancunchu1"></use>
-                </svg>
-                <div class="name">云端存储</div>
-                <div class="code-name">#el-icon-yunduancunchu1</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduanmoren"></use>
-                </svg>
-                <div class="name">云端默认</div>
-                <div class="code-name">#el-icon-yunduanmoren</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-shangchuanyunduanshibai"></use>
-                </svg>
-                <div class="name">上传云端失败</div>
-                <div class="code-name">#el-icon-shangchuanyunduanshibai</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduan3"></use>
-                </svg>
-                <div class="name">云端</div>
-                <div class="code-name">#el-icon-yunduan3</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-a-yunduanlianjie"></use>
-                </svg>
-                <div class="name">云端,链接</div>
-                <div class="code-name">#el-icon-a-yunduanlianjie</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yunduan4"></use>
-                </svg>
-                <div class="name">云端</div>
-                <div class="code-name">#el-icon-yunduan4</div>
-            </li>
-          
             <li class="dib">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#el-icon-shijian"></use>
                   <use xlink:href="#el-icon-shijian"></use>

+ 11 - 75
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
 @font-face {
   font-family: "element-icons"; /* Project id 4795944 */
   font-family: "element-icons"; /* Project id 4795944 */
-  src: url('iconfont.woff2?t=1739527479205') format('woff2'),
-       url('iconfont.woff?t=1739527479205') format('woff'),
-       url('iconfont.ttf?t=1739527479205') format('truetype');
+  src: url('iconfont.woff2?t=1740223260776') format('woff2'),
+       url('iconfont.woff?t=1740223260776') format('woff'),
+       url('iconfont.ttf?t=1740223260776') format('truetype');
 }
 }
 
 
 .element-icons {
 .element-icons {
@@ -13,6 +13,14 @@
   -moz-osx-font-smoothing: grayscale;
   -moz-osx-font-smoothing: grayscale;
 }
 }
 
 
+.el-icon-jiantouxiangxia:before {
+  content: "\e615";
+}
+
+.el-icon-jiantouxiangshang:before {
+  content: "\ee1a";
+}
+
 .el-icon-guanlizhongxin:before {
 .el-icon-guanlizhongxin:before {
   content: "\e614";
   content: "\e614";
 }
 }
@@ -25,86 +33,14 @@
   content: "\e9d9";
   content: "\e9d9";
 }
 }
 
 
-.el-icon-diannao2:before {
-  content: "\e6b4";
-}
-
-.el-icon-diannao1:before {
-  content: "\e604";
-}
-
-.el-icon-iconfont_disconnected:before {
-  content: "\e608";
-}
-
-.el-icon-xiazaishibai:before {
-  content: "\e663";
-}
-
-.el-icon-yunduan-guanbi-tianchong:before {
-  content: "\e7e8";
-}
-
 .el-icon-yunduan-guanbi-xianxing:before {
 .el-icon-yunduan-guanbi-xianxing:before {
   content: "\e7e9";
   content: "\e7e9";
 }
 }
 
 
-.el-icon-yunduangou:before {
-  content: "\e617";
-}
-
-.el-icon-yunduanlianjie:before {
-  content: "\e640";
-}
-
-.el-icon-yunduan:before {
-  content: "\e6bf";
-}
-
 .el-icon-yunduanziyuan:before {
 .el-icon-yunduanziyuan:before {
   content: "\e60d";
   content: "\e60d";
 }
 }
 
 
-.el-icon-yunduan1:before {
-  content: "\ee19";
-}
-
-.el-icon-yunduancunchu:before {
-  content: "\e638";
-}
-
-.el-icon-yunduan2:before {
-  content: "\e601";
-}
-
-.el-icon-a-huaban1fuben25:before {
-  content: "\e603";
-}
-
-.el-icon-yunduancunchu1:before {
-  content: "\e602";
-}
-
-.el-icon-yunduanmoren:before {
-  content: "\e731";
-}
-
-.el-icon-shangchuanyunduanshibai:before {
-  content: "\e648";
-}
-
-.el-icon-yunduan3:before {
-  content: "\e64e";
-}
-
-.el-icon-a-yunduanlianjie:before {
-  content: "\e60f";
-}
-
-.el-icon-yunduan4:before {
-  content: "\e698";
-}
-
 .el-icon-shijian:before {
 .el-icon-shijian:before {
   content: "\e618";
   content: "\e618";
 }
 }

Plik diff jest za duży
+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.js


+ 14 - 126
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.json

@@ -5,6 +5,20 @@
   "css_prefix_text": "el-icon-",
   "css_prefix_text": "el-icon-",
   "description": "",
   "description": "",
   "glyphs": [
   "glyphs": [
+    {
+      "icon_id": "609292",
+      "name": "07_箭头_向下",
+      "font_class": "jiantouxiangxia",
+      "unicode": "e615",
+      "unicode_decimal": 58901
+    },
+    {
+      "icon_id": "43421025",
+      "name": "07_箭头_向上",
+      "font_class": "jiantouxiangshang",
+      "unicode": "ee1a",
+      "unicode_decimal": 60954
+    },
     {
     {
       "icon_id": "26126920",
       "icon_id": "26126920",
       "name": "管理中心",
       "name": "管理中心",
@@ -26,41 +40,6 @@
       "unicode": "e9d9",
       "unicode": "e9d9",
       "unicode_decimal": 59865
       "unicode_decimal": 59865
     },
     },
-    {
-      "icon_id": "15659133",
-      "name": "电脑",
-      "font_class": "diannao2",
-      "unicode": "e6b4",
-      "unicode_decimal": 59060
-    },
-    {
-      "icon_id": "6850406",
-      "name": "电脑",
-      "font_class": "diannao1",
-      "unicode": "e604",
-      "unicode_decimal": 58884
-    },
-    {
-      "icon_id": "9232602",
-      "name": "iconfont_disconnected",
-      "font_class": "iconfont_disconnected",
-      "unicode": "e608",
-      "unicode_decimal": 58888
-    },
-    {
-      "icon_id": "10574966",
-      "name": "下载失败",
-      "font_class": "xiazaishibai",
-      "unicode": "e663",
-      "unicode_decimal": 58979
-    },
-    {
-      "icon_id": "12690964",
-      "name": "云端-关闭-填充",
-      "font_class": "yunduan-guanbi-tianchong",
-      "unicode": "e7e8",
-      "unicode_decimal": 59368
-    },
     {
     {
       "icon_id": "12690973",
       "icon_id": "12690973",
       "name": "云端-关闭-线性",
       "name": "云端-关闭-线性",
@@ -68,27 +47,6 @@
       "unicode": "e7e9",
       "unicode": "e7e9",
       "unicode_decimal": 59369
       "unicode_decimal": 59369
     },
     },
-    {
-      "icon_id": "13056815",
-      "name": "云端勾",
-      "font_class": "yunduangou",
-      "unicode": "e617",
-      "unicode_decimal": 58903
-    },
-    {
-      "icon_id": "14928240",
-      "name": "云端链接",
-      "font_class": "yunduanlianjie",
-      "unicode": "e640",
-      "unicode_decimal": 58944
-    },
-    {
-      "icon_id": "15034707",
-      "name": "云端",
-      "font_class": "yunduan",
-      "unicode": "e6bf",
-      "unicode_decimal": 59071
-    },
     {
     {
       "icon_id": "20904889",
       "icon_id": "20904889",
       "name": "云端资源",
       "name": "云端资源",
@@ -96,76 +54,6 @@
       "unicode": "e60d",
       "unicode": "e60d",
       "unicode_decimal": 58893
       "unicode_decimal": 58893
     },
     },
-    {
-      "icon_id": "27402410",
-      "name": "云端",
-      "font_class": "yunduan1",
-      "unicode": "ee19",
-      "unicode_decimal": 60953
-    },
-    {
-      "icon_id": "29534026",
-      "name": "云端存储",
-      "font_class": "yunduancunchu",
-      "unicode": "e638",
-      "unicode_decimal": 58936
-    },
-    {
-      "icon_id": "30284033",
-      "name": "云端",
-      "font_class": "yunduan2",
-      "unicode": "e601",
-      "unicode_decimal": 58881
-    },
-    {
-      "icon_id": "31987464",
-      "name": "云端连接",
-      "font_class": "a-huaban1fuben25",
-      "unicode": "e603",
-      "unicode_decimal": 58883
-    },
-    {
-      "icon_id": "32659898",
-      "name": "云端存储",
-      "font_class": "yunduancunchu1",
-      "unicode": "e602",
-      "unicode_decimal": 58882
-    },
-    {
-      "icon_id": "34234770",
-      "name": "云端默认",
-      "font_class": "yunduanmoren",
-      "unicode": "e731",
-      "unicode_decimal": 59185
-    },
-    {
-      "icon_id": "34808601",
-      "name": "上传云端失败",
-      "font_class": "shangchuanyunduanshibai",
-      "unicode": "e648",
-      "unicode_decimal": 58952
-    },
-    {
-      "icon_id": "36235763",
-      "name": "云端",
-      "font_class": "yunduan3",
-      "unicode": "e64e",
-      "unicode_decimal": 58958
-    },
-    {
-      "icon_id": "40478671",
-      "name": "云端,链接",
-      "font_class": "a-yunduanlianjie",
-      "unicode": "e60f",
-      "unicode_decimal": 58895
-    },
-    {
-      "icon_id": "41033647",
-      "name": "云端",
-      "font_class": "yunduan4",
-      "unicode": "e698",
-      "unicode_decimal": 59032
-    },
     {
     {
       "icon_id": "10352325",
       "icon_id": "10352325",
       "name": "时间",
       "name": "时间",

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.ttf


BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff


BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff2


+ 4 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/main.js

@@ -4,12 +4,14 @@ import router from './router/router'
 import apiTools from '@/api'
 import apiTools from '@/api'
 import { fetch, post } from '@/api/http'
 import { fetch, post } from '@/api/http'
 import tools from '@/utils/public.js'
 import tools from '@/utils/public.js'
+import evTools from '@/utils/evTools.js'
 
 
 import ElementUI from 'element-ui'
 import ElementUI from 'element-ui'
 import 'element-ui/lib/theme-chalk/index.css'
 import 'element-ui/lib/theme-chalk/index.css'
 import axios from 'axios'
 import axios from 'axios'
 import '@/assets/reset.css'
 import '@/assets/reset.css'
 import "@/assets/iconfont/iconfont.css"
 import "@/assets/iconfont/iconfont.css"
+import _ from 'lodash'
 
 
 import vuescroll from 'vuescroll/dist/vuescroll-native'
 import vuescroll from 'vuescroll/dist/vuescroll-native'
 
 
@@ -20,8 +22,10 @@ Vue.config.productionTip = false
 Vue.prototype.$api = apiTools
 Vue.prototype.$api = apiTools
 Vue.prototype.$axios = axios
 Vue.prototype.$axios = axios
 Vue.prototype.$tools = tools
 Vue.prototype.$tools = tools
+Vue.prototype.$evTools = evTools
 Vue.prototype.$post = post
 Vue.prototype.$post = post
 Vue.prototype.$get = fetch
 Vue.prototype.$get = fetch
+Vue.prototype._ = _
 
 
 // 定义vuescroll全局滚动条组件
 // 定义vuescroll全局滚动条组件
 Vue.component('vuescroll', vuescroll)
 Vue.component('vuescroll', vuescroll)

+ 847 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/utils/evTools.js

@@ -0,0 +1,847 @@
+import $tools from './public.js'
+import { app } from '@/main.js'
+
+
+export default {
+	/* 根据登录后的用户信息获取blobHOST域名 */
+	getBlobHost(url) {
+		let s = url || store.state.user.userProfile.blob_uri || store.state.user.studentProfile.blob_uri
+		let pattern = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/
+		return s.split('//')[0] + '//' + s.match(pattern)[0]
+	},
+	getAbilityDetailById(abilityId) {
+		return new Promise((r, j) => {
+			$api.ability.FindAbilityById({
+				"scope": "school",
+				"schoolCode": store.state.userInfo.schoolCode,
+				"abilityId": abilityId,
+				"standard": sessionStorage.getItem('standard')
+			}).then(res => {
+				if (!res.error) {
+					r(res.ability)
+				} else {
+					j(res.error)
+				}
+			}).catch(e => {
+				j(e)
+			})
+		})
+	},
+	/* 获取试题保存在Blob的JSON文件 */
+	/* 创建Blob试题格式 */
+	createBlobItem(item) {
+		return new Promise((r, j) => {
+			let itemJson = {
+				id: item.id,
+				pid: item.pid || null,
+				exercise: {
+					answer: item.answer,
+					explain: item.explain,
+					type: item.type,
+					answerType: item.answerType || 'text',
+					useAutoScore: item.useAutoScore || false,
+					answerLang: item.answerLang || 'en-US',
+					objective: this.getItemType(item.type),
+					opts: item.option ? item.option.length : 0,
+					knowledge: item.knowledge,
+					field: item.field,
+					level: item.level,
+					periodId: item.periodId,
+					gradeIds: item.gradeIds,
+					subjectId: item.subjectId,
+					children: item.children || [],
+					// scope:item.scope,
+					score: item.score || 0,
+					source: item.source || 0,
+					blankCount: item.blankCount || 1,
+					repair: item.repair,
+					createTime: new Date().getTime(),
+					creator: store.state.userInfo.TEAMModelId || 'null'
+				},
+				item: [{
+					type: 'Html',
+					uid: item.id,
+					question: item.question,
+					option: item.option
+				}]
+			}
+			r(itemJson)
+		})
+	},
+	/* 获取保存在COSMOS里面的试题格式 */
+	createCosmosItem(item, scope, code) {
+		return new Promise((r, j) => {
+			let cosmosItem = {
+				id: item.id,
+				pid: item.pid || null,
+				code: code || item.code,
+				scope: scope || item.scope,
+				score: item.score || 0,
+				source: item.source || 0,
+				type: item.type,
+				answer: item.answer || [],
+				answerType: item.answerType || 'text',
+				useAutoScore: item.useAutoScore || false,
+				answerLang: item.answerLang || 'en-US',
+				objective: this.getItemType(item.type),
+				question: this.getSimpleText(item.question),
+				knowledge: item.knowledge,
+				field: item.field,
+				level: item.level,
+				periodId: item.periodId,
+				gradeIds: item.gradeIds,
+				subjectId: item.subjectId,
+				repair: item.repair,
+				blankCount: item.blankCount || 1,
+				blob: item.blob,
+				createTime: new Date().getTime(),
+				creator: store.state.userInfo.TEAMModelId || 'null',
+				tags: item.tags || []
+			}
+			r(cosmosItem)
+		})
+	},
+	/* 生成试卷的index.json文件格式 */
+	createBlobPaper(paper, slides) {
+		return new Promise((r, j) => {
+			let paperItem = {
+				id: paper.id,
+				name: paper.name,
+				// code:paper.code,
+				// scope:paper.scope,
+				blob: paper.blob || '',
+				itemSort: paper.itemSort || 0,
+				isNumOption: paper.isNumOption || 0,
+				qamode: paper.qamode || 0,
+				multipleRule: paper.multipleRule,
+				attachments: paper.attachments || [],
+				tags: paper.tags || [],
+				slides: slides,
+				points: paper.points,
+				periodId: paper.periodId,
+				gradeIds: paper.gradeIds,
+				subjectId: paper.subjectId,
+				subjectName: paper.subjectName,
+				score: paper.score,
+				sheet: paper.sheet || null,
+				typeSummaryInfo: paper.typeSummaryInfo || null,
+				orderTemp: paper.orderTemp || 0,
+				secret: paper.secret || 0,
+				markModel: paper.markModel || 0,
+				creatorId: paper.creatorId || store.state.userInfo.TEAMModelId
+			}
+			r(paperItem)
+		})
+	},
+	/* 生成试卷保存在cosmos的数据结构 */
+	createCosmosPaper(paper) {
+		return new Promise((r, j) => {
+			let paperItem = {
+				id: paper.id,
+				name: paper.name,
+				code: paper.code,
+				blob: paper.blob,
+				tags: paper.tags || [],
+				qamode: paper.qamode || 0,
+				attachments: paper.attachments || [],
+				sheet: paper.sheet || null,
+				itemSort: paper.itemSort || 0,
+				isNumOption: paper.isNumOption || 0,
+				scope: paper.scope,
+				scoring: paper.scoring,
+				points: paper.points,
+				periodId: paper.periodId,
+				gradeIds: paper.gradeIds,
+				subjectId: paper.subjectId,
+				subjectName: paper.subjectName,
+				score: paper.score,
+				multipleRule: paper.multipleRule,
+				secret: paper.secret || 0,
+				markModel: paper.markModel || 0,
+				creatorId: paper.creatorId || store.state.userInfo.TEAMModelId
+			}
+			r(paperItem)
+		})
+	},
+	/* 根据醍摩豆ID获取对应用户Blob内部的完整试题 */
+	getFullItemByTmdId(tmdId, blob, inSyllabus) {
+		return new Promise(async (r, j) => {
+			let privateSas = await this.getBlobPrivateSas(tmdId)
+			let fullPath = this.getBlobHost() + '/' + tmdId + blob + privateSas
+			let jsonData = JSON.parse(await $tools.getFile(fullPath))
+			// 如果是综合题 那就拿到children里面的小题id集合 去换取小题的blobJSON文件 然后替换children的内容
+			if (jsonData.exercise.children.length && jsonData.exercise.type === 'compose') {
+				let childrenUrls;
+				if (inSyllabus) {
+					let syllabusPrefix = '/syllabus/' + blob.split('/')[2] + '/' + jsonData.id
+					childrenUrls = jsonData.exercise.children.map(i => this.getBlobHost() + '/' + tmdId + syllabusPrefix + '/' + i.id + '.json' + privateSas)
+				} else {
+					childrenUrls = jsonData.exercise.children.map(i => this.getBlobHost() + '/' + tmdId + '/item/' + i + '/' + i + '.json' + privateSas)
+				}
+				console.log(childrenUrls);
+				jsonData.exercise.children = await this.getFullChildren(childrenUrls, tmdId, inSyllabus)
+			}
+
+			// 调整渲染试题数据结构
+			jsonData.exercise.question = jsonData.item[0].question
+			jsonData.exercise.blob = fullPath
+			jsonData.exercise.code = tmdId
+			jsonData.exercise.option = jsonData.item[0].option
+			jsonData.exercise.id = jsonData.id
+			jsonData.exercise.scope = 'private'
+			jsonData.exercise.pid = jsonData.pid
+			jsonData.exercise = await this.doAddHost(jsonData.exercise, null, tmdId, inSyllabus)
+			r(jsonData.exercise)
+		})
+	},
+	/* 根据醍摩豆ID获取对应BLOB个人容器授权信息 */
+	getBlobPrivateSas(tmdId) {
+		return new Promise((r, j) => {
+			$api.blob.blobSasR({
+				name: tmdId,
+				role: 'teacher'
+			}).then(res => {
+				if (!res.error) {
+					r('?' + res.sas)
+				}
+			})
+		})
+	},
+
+	/* 根据醍摩豆ID获取对应BLOB个人容器授权信息 */
+	getBlobPrivateSasObj(tmdId) {
+		return new Promise((r, j) => {
+			$api.blob.blobSasR({
+				name: tmdId,
+				role: 'teacher'
+			}).then(res => {
+				if (!res.error) {
+					res.sas = '?' + res.sas
+					r(res)
+				}
+			})
+		})
+	},
+
+	/* 根据醍摩豆ID获取对应BLOB个人容器授权信息 */
+	getBlobSchoolSas(schoolCode) {
+		return new Promise((r, j) => {
+			$api.blob.blobSasR({
+				name: schoolCode,
+				role: 'school'
+			}).then(res => {
+				if (!res.error) {
+					r('?' + res.sas)
+				}
+			})
+		})
+	},
+	/* 获取完整的试题数据 */
+	getFullItem(list, examScope, inSyllabus) {
+		console.log('接受到的examScope', examScope)
+		return new Promise(async (resolve, reject) => {
+			if (list.length === 0) return
+			let promiseArr = []
+			console.log('getFullITEM接收到的list')
+			console.log(list)
+			for (let i = 0; i < list.length; i++) {
+				promiseArr.push(new Promise(async (r, j) => {
+					if (list[i].blob) {
+						let curScope = list[i].scope
+						const blobHost = curScope === 'school' ? JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri : JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
+						// 根据试题的Blob地址 去读取JSON文件
+						let sasString = curScope === 'school' ? await $tools.getSchoolSas() : await $tools.getPrivateSas()
+						try {
+							let jsonInfo = list[i].blob.includes('https://') ? await $tools.getFile(list[i].blob + sasString.sas) : await $tools.getFile(blobHost + list[i].blob + sasString.sas)
+							let jsonData = JSON.parse(jsonInfo)
+							// 如果是综合题 那就拿到children里面的小题id集合 去换取小题的blobJSON文件 然后替换children的内容
+							if (jsonData.exercise.children.length && jsonData.exercise.type === 'compose') {
+								let childrenUrls;
+								if (inSyllabus) {
+									let syllabusPrefix = '/syllabus/' + list[i].blob.split('/')[2] + '/' + list[i].id
+									childrenUrls = jsonData.exercise.children.map(i => blobHost + syllabusPrefix + '/' + i.id + '.json' + sasString.sas)
+								} else {
+									childrenUrls = jsonData.exercise.children.map(i => blobHost + '/item/' + i + '/' + i + '.json' + sasString.sas)
+								}
+								jsonData.exercise.children = await this.getFullChildren(childrenUrls, list[i].code, list[i].scope, inSyllabus)
+							}
+							// 调整渲染试题数据结构
+							jsonData.id = list[i].id
+							jsonData.exercise.question = jsonData.item[0].question
+							jsonData.exercise.createTime = list[i].createTime || 0
+							jsonData.exercise.blob = list[i].blob
+							jsonData.exercise.code = list[i].code
+							jsonData.exercise.scope = list[i].scope
+							jsonData.exercise.option = jsonData.item[0].option
+							jsonData.exercise.id = list[i].id
+							jsonData.exercise.pid = jsonData.pid
+							jsonData.exercise.tags = list[i]?.tags || []
+							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) {
+							console.log(e)
+							j(e)
+							// this.$Message.error(e)
+						}
+
+					} else {
+						r(null)
+					}
+				}))
+			}
+			Promise.allSettled(promiseArr).then(result => {
+				console.log('从Blob获取来的试题', result.filter(i => i.status === 'fulfilled').map(j => j.value))
+				resolve(result.filter(i => i.status === 'fulfilled').map(j => j.value))
+			}).catch(err => {
+				Message.error(app.$t('utils.fileReadFail'))
+				reject(err)
+			})
+		})
+	},
+	/* 保存综合题小题 */
+	saveChildren(children, containerClient) {
+		return new Promise((resolve, reject) => {
+			let promiseArr = []
+			let itemJsonFiles = []
+			children.forEach(exerciseItem => {
+				promiseArr.push(new Promise(async (r, j) => {
+					// 将当前的试题数据转化为BLOB内部的试题JSON格式
+					const itemJsonFile = await this.createBlobItem(exerciseItem)
+					const cosmosItem = await this.createCosmosItem(exerciseItem)
+					// 首先保存新题目的JSON文件到Blob 然后返回URL链接
+					let file = new File([JSON.stringify(itemJsonFile)], exerciseItem.id + ".json");
+					try {
+						// 等待上传blob的返回结果
+						let blobFile = await containerClient.upload(file, { path: 'item/' + exerciseItem.id })
+						if (blobFile.blob) {
+							// 保存试题JSON文件到试卷文件夹需要
+							itemJsonFiles.push(file)
+							// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+							cosmosItem.blob = blobFile.blob
+							// 保存当前试题到数据库
+							that.saveExercise(cosmosItem).then(res => {
+								r(res.itemInfo)
+							})
+						} else {
+							j(500)
+						}
+					} catch (e) {
+						j(500)
+					}
+				}))
+			})
+			Promise.all(promiseArr).then(result => {
+				if (result.length) {
+					resolve(itemJsonFiles)
+				} else {
+					resolve([])
+				}
+			})
+
+		})
+
+	},
+	/* 获取综合题子题的Blob数据 */
+	getFullChildren(urls, code, scope, inSyllabus) {
+		return new Promise((resolve, reject) => {
+			let promiseArr = []
+			urls.forEach(url => {
+				promiseArr.push(new Promise(async (r, j) => {
+					try {
+						let jsonData = JSON.parse(await $tools.getFile(url))
+						// 调整渲染试题数据结构
+						jsonData.exercise.question = jsonData.item[0].question
+						jsonData.exercise.blob = url
+						jsonData.exercise.code = code
+						jsonData.exercise.option = jsonData.item[0].option
+						jsonData.exercise.id = jsonData.id
+						jsonData.exercise.pid = jsonData.pid
+						jsonData.exercise.scope = scope || 'private'
+						jsonData.exercise = await this.doAddHost(jsonData.exercise, null, null, inSyllabus)
+						r(jsonData.exercise)
+					} catch (e) {
+						j(e)
+					}
+				}))
+			})
+
+			Promise.allSettled(promiseArr).then(result => {
+				if (result.length) {
+					// resolve(result)
+					resolve(result.filter(i => i.status === 'fulfilled').map(j => j.value))
+				} else {
+					resolve([])
+				}
+			})
+		})
+	},
+	/* 根据醍摩豆ID获取对应用户Blob内部的完整试卷 */
+	getFullPaperByTmdId(tmdId, blob, nodeId) {
+		return new Promise(async (r, j) => {
+			console.log('根据ID获取试题')
+			let privateSas = await this.getBlobPrivateSas(tmdId)
+			let fullPath = this.getBlobHost() + '/' + tmdId + blob + '/index.json' + privateSas
+			try {
+				let jsonInfo = await $tools.getFile(fullPath)
+				let jsonData = JSON.parse(jsonInfo)
+				jsonData.scope = 'private'
+				jsonData.code = tmdId
+				// 获取试卷包含的试题数据并包装好
+				if (jsonData.slides && jsonData.slides.length) {
+					let promiseArr = []
+					let allItems = []
+					jsonData.item = []
+					const path = this.getBlobHost() + '/' + tmdId + blob
+					jsonData.slides.forEach(async (item, index) => {
+						promiseArr.push(new Promise(async (resolve, reject) => {
+							try {
+								// 获取题目JSON并且包装成完整试题对象
+								let itemJson = JSON.parse(await $tools.getFile(path + '/' + item.url + privateSas))
+								itemJson.exercise.question = itemJson.item[0].question
+								itemJson.exercise.option = itemJson.item[0].option
+								itemJson.exercise.id = itemJson.id
+								itemJson.exercise.pid = itemJson.pid
+								itemJson.exercise.blob = path + '/' + item.url // 添加blob是方便在保存试卷是 refresh 与导入的试题区分开
+								itemJson.exercise.scope = 'private'
+								itemJson.exercise.score = item.scoring ? item.scoring.score : 0
+								try {
+									itemJson.exercise = await this.doAddHost(itemJson.exercise, { name: jsonData.name }, nodeId ? 'syllabus' : tmdId, nodeId) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+									resolve(itemJson.exercise)
+								} catch (e) {
+									reject(e)
+								}
+							} catch (e) {
+								reject(e)
+							}
+						}))
+					})
+
+					Promise.all(promiseArr).then(res => {
+						res.forEach((resItem, resIndex) => {
+							resItem.children = []
+							if (resItem.pid) {
+								let pItem = res.filter(i => i.id === resItem.pid)[0]
+								pItem.children.push(resItem)
+								pItem.score = pItem.score + resItem.score
+							}
+						})
+						jsonData.item = res.filter(i => !i.pid)
+						r(jsonData)
+					}).catch(e => {
+						// Message.error('试卷文件读取失败')
+						j(e)
+					})
+				}
+			} catch (e) {
+				console.log(e)
+				j(e)
+			}
+		})
+	},
+	/* 获取完整的试卷数据 */
+	getFullPaper(paper, examScope, nodeId) {
+		console.log(paper)
+		console.log(examScope)
+		console.log(nodeId)
+		let curScope = examScope || paper.examScope || paper.scope
+		return new Promise(async (r, j) => {
+			let blobHost = curScope === 'school' ? JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri : 
+			JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
+			let sasString = curScope === 'school' ? await $tools.getSchoolSas() : await $tools.getPrivateSas()
+			let privateSas = sasString.sas
+			// 如果是活動版sas拿法不同
+			if(paper.blob.indexOf("jointexam") != -1){	
+				if(paper.creatorId){
+					privateSas = await this.getBlobPrivateSas(paper.creatorId)
+				}else{
+					privateSas = await this.getBlobPrivateSas(paper.examId)
+				}		
+				
+				blobHost ="https://teammodel.blob.core.windows.net/"+paper.creatorId
+			}
+			
+			// 根据试卷的Blob地址 去读取JSON文件			
+			try {
+				let paperBlob = paper.blob 				
+				let jsonInfo = await $tools.getFile(blobHost + paperBlob + '/index.json' + privateSas)
+				let jsonData = JSON.parse(jsonInfo)
+				jsonData.scope = paper.scope
+				jsonData.code = paper.code
+				jsonData.sheet = paper.sheet || null
+				// 获取试卷包含的试题数据并包装好
+				if (jsonData.slides && jsonData.slides.length) {
+					let promiseArr = []
+					let allItems = []
+					jsonData.item = []
+					const path = blobHost + paper.blob
+					jsonData.slides.forEach(async (item, index) => {
+						promiseArr.push(new Promise(async (resolve, reject) => {
+							try {
+								// 获取题目JSON并且包装成完整试题对象
+								let itemJson = JSON.parse(await $tools.getFile(path + '/' + item.url + privateSas))
+								itemJson.exercise.question = itemJson.item[0].question
+								itemJson.exercise.option = itemJson.item[0].option
+								itemJson.exercise.id = itemJson.id
+								itemJson.exercise.pid = itemJson.pid
+								itemJson.exercise.scope = paper.scope
+								itemJson.exercise.blob = path + '/' + item.url // 添加blob是方便在保存试卷是 refresh 与导入的试题区分开
+								itemJson.exercise.score = item.scoring ? item.scoring.score : 0
+								try {
+									let p = nodeId ? { name: paper.name } : paper
+									itemJson.exercise = await this.doAddHost(itemJson.exercise, p, nodeId ? 'syllabus' : null, nodeId) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+									resolve(itemJson.exercise)
+								} catch (e) {
+									reject(e)
+								}
+							} catch (e) {
+								reject(e)
+							}
+						}))
+					})
+
+					Promise.allSettled(promiseArr).then(res => {
+						res = res.filter(i => i.status === 'fulfilled').map(j => j.value)
+						res.forEach((resItem, resIndex) => {
+							resItem.children = []
+							if (resItem.pid) {
+								let pItem = res.filter(i => i.id === resItem.pid)[0]
+								pItem.children.push(resItem)
+								pItem.score = pItem.score + resItem.score
+							}
+						})
+						jsonData.item = res.filter(i => !i.pid)
+						r(jsonData)
+					}).catch(e => {
+						// Message.error('试卷文件读取失败')
+						j(e)
+					})
+				}
+			} catch (e) {
+				j(e)
+			}
+		})
+	},
+	/* 获取完整的试卷数据 */
+	getStuPaper(paper, examScope) {
+		let curScope = examScope || paper.scope
+		console.log(...arguments);
+		return new Promise(async (r, j) => {
+			// let blobHost = this.getBlobHost()
+			// 根据试卷的Blob地址 去读取JSON文件
+			let cntr = paper.code
+			let sas = await $tools.getBlobSas(cntr)
+			let sasString = "?" + sas.sas
+			let blobHost = sas.url
+			let paperBlobPath = blobHost + '/' + cntr + paper.blob
+			let fullPath = paperBlobPath + '/index.json' + sasString
+			console.log(fullPath);
+			try {
+				let jsonInfo = await $tools.getFile(fullPath)
+				let jsonData = JSON.parse(jsonInfo)
+				jsonData.scope = curScope
+				jsonData.code = paper.code
+				jsonData.sheet = paper.sheet || null
+				paper.tags = paper.tags || jsonData.tags
+				// 获取试卷包含的试题数据并包装好
+				if (jsonData.slides && jsonData.slides.length) {
+					jsonData.item = []
+					let promiseArr = []
+					jsonData.slides.forEach((item, index) => {
+						promiseArr.push(new Promise(async (resolve, reject) => {
+							// 获取题目JSON并且包装成完整试题对象
+							let itemFullPath = paperBlobPath + '/' + item.url + sasString
+							let itemJson = JSON.parse(await $tools.getFile(itemFullPath))
+							itemJson.exercise.question = itemJson.item[0].question
+							itemJson.exercise.option = itemJson.item[0].option
+							itemJson.exercise.id = itemJson.id
+							itemJson.exercise.pid = itemJson.pid
+							itemJson.exercise.scope = curScope
+							itemJson.exercise.score = item.scoring ? item.scoring.score : 0
+							// jsonData.item.push(itemJson.exercise)
+							try {
+								itemJson.exercise = await this.doAddHost(itemJson.exercise, paper, paper.code, null, sasString)
+								resolve(itemJson.exercise)
+							} catch (e) {
+								reject(e)
+							}
+						}))
+					})
+					Promise.all(promiseArr).then(res => {
+						res.forEach((resItem, resIndex) => {
+							resItem.children = []
+							if (resItem.pid) {
+								let pItem = res.filter(i => i.id === resItem.pid)[0]
+								pItem.children.push(resItem)
+								pItem.score = pItem.score + resItem.score
+							}
+						})
+						jsonData.item = res.filter(i => !i.pid)
+						r(jsonData)
+					}).catch(e => {
+						j(e)
+					})
+				}
+			} catch (e) {
+				j(e)
+			}
+		})
+	},
+	// 艺术测评专用获取试卷方案
+	getStuPaperForArt(paper, examScope, sas) {
+		let curScope = examScope || paper.scope
+		console.log(...arguments);
+		return new Promise(async (r, j) => {
+			// let blobHost = this.getBlobHost()
+			// 根据试卷的Blob地址 去读取JSON文件
+			let cntr = paper.code
+			let sasString = "?" + sas.sas
+			let blobHost = sas.url
+			let paperBlobPath = blobHost + '/' + cntr + paper.blob
+			let fullPath = paperBlobPath + '/index.json' + sasString
+			console.log(fullPath);
+			try {
+				let jsonInfo = await $tools.getFile(fullPath)
+				let jsonData = JSON.parse(jsonInfo)
+				jsonData.scope = curScope
+				jsonData.code = paper.code
+				jsonData.sheet = paper.sheet || null
+				paper.tags = paper.tags || jsonData.tags
+				r(jsonData)
+				// 获取试卷包含的试题数据并包装好
+				// if (jsonData.slides && jsonData.slides.length) {
+				// 	jsonData.item = []
+				// 	let promiseArr = []
+				// 	jsonData.slides.forEach((item, index) => {
+				// 		promiseArr.push(new Promise(async (resolve, reject) => {
+				// 			// 获取题目JSON并且包装成完整试题对象
+				// 			let itemFullPath = paperBlobPath + '/' + item.url + sasString
+				// 			let itemJson = JSON.parse(await $tools.getFile(itemFullPath))
+				// 			itemJson.exercise.question = itemJson.item[0].question
+				// 			itemJson.exercise.option = itemJson.item[0].option
+				// 			itemJson.exercise.id = itemJson.id
+				// 			itemJson.exercise.pid = itemJson.pid
+				// 			itemJson.exercise.scope = curScope
+				// 			itemJson.exercise.score = item.scoring ? item.scoring.score : 0
+				// 			// jsonData.item.push(itemJson.exercise)
+				// 			try {
+				// 				itemJson.exercise = await this.doAddHost(itemJson.exercise, paper, paper.code, null, sasString)
+				// 				resolve(itemJson.exercise)
+				// 			} catch (e) {
+				// 				reject(e)
+				// 			}
+				// 		}))
+				// 	})
+				// 	Promise.all(promiseArr).then(res => {
+				// 		res.forEach((resItem, resIndex) => {
+				// 			resItem.children = []
+				// 			if (resItem.pid) {
+				// 				let pItem = res.filter(i => i.id === resItem.pid)[0]
+				// 				pItem.children.push(resItem)
+				// 				pItem.score = pItem.score + resItem.score
+				// 			}
+				// 		})
+				// 		jsonData.item = res.filter(i => !i.pid)
+				// 		r(jsonData)
+				// 	}).catch(e => {
+				// 		j(e)
+				// 	})
+				// }
+			} catch (e) {
+				j(e)
+			}
+		})
+	},
+
+	/* 获取完整的试卷数据 */
+	getComposeItem(paper) {
+		return new Promise(async (r, j) => {
+			console.log(paper);
+			// 根据试卷的Blob地址 去读取JSON文件
+			let cntr = paper.code
+			let sas = await $tools.getBlobSas(cntr)
+			let sasString = sas.sas
+			let fullPath = paper.blob + "?" + sasString
+			try {
+				let jsonInfo = await $tools.getFile(fullPath)
+				let jsonData = JSON.parse(jsonInfo)
+				// 获取试卷包含的试题数据并包装好
+				if (jsonData.length) {
+					r(jsonData)
+				}
+			} catch (e) {
+				j(e)
+			}
+		})
+	},
+	/* 提取富文本内容中的文本 */
+	getSimpleText(html) {
+		var r = /<\/?(img)[^>]*>/gi;
+		return html.replace(r, "");
+	},
+	/* 判断是否为客观题 */
+	getItemType(type) {
+		const objective = ['single', 'multiple', 'judge']
+		return objective.includes(type)
+	},
+	/* 获取img标签内的src */
+	getImgSrc(richtext) {
+		let imgList = [];
+		richtext.replace(/<video [^>]*src=['"]([^'"]+)[^>]*>/g, (match, capture) => {
+			imgList.push(capture);
+		});
+		return imgList;
+	},
+
+
+
+
+
+
+	getPaperInfo(examId, paperId) {
+		return new Promise(async (resolve, reject) => {
+			let url = `/package/${examId}/papers/${paperId}`
+            let indexUrl = url + '/index.json'
+            try {
+                let jsonInfo = await $tools.getFile(indexUrl)
+                let jsonData = JSON.parse(jsonInfo)
+                // 获取试卷包含的试题数据并包装好
+                if (jsonData.slides && jsonData.slides.length) {
+                    jsonData.item = []
+                    let promiseArr = []
+                    jsonData.slides.forEach((item, index) => {
+                        promiseArr.push(new Promise(async (resolve, reject) => {
+                            // 获取题目JSON并且包装成完整试题对象
+                            let itemFullPath = url + '/' + item.url
+                            let itemJson = JSON.parse(await $tools.getFile(itemFullPath))
+                            itemJson.exercise.question = itemJson.item[0].question
+                            itemJson.exercise.option = itemJson.item[0].option
+                            itemJson.exercise.id = itemJson.id
+                            itemJson.exercise.pid = itemJson.pid
+                            // itemJson.exercise.scope = curScope
+                            itemJson.exercise.score = item.scoring ? item.scoring.score : 0
+                            try {
+                                console.log('多媒体链接', await this.doAddHost(itemJson.exercise, url));
+                                // this.processNum++
+                                resolve(itemJson.exercise)
+                            } catch (e) {
+                                reject(e)
+                            }
+                        }))
+                    })
+                    Promise.all(promiseArr).then(res => {
+                        res.forEach((resItem, resIndex) => {
+                            resItem.children = []
+                            if (resItem.pid) {
+                                let pItem = res.filter(i => i.id === resItem.pid)[0]
+                                pItem.children.push(resItem)
+                                pItem.score = pItem.score + resItem.score
+                            }
+                        })
+                        jsonData.item = res.filter(i => !i.pid)
+                        console.log('题目详细内容', jsonData);
+                        /* this.paperList.push(jsonData)
+                        this.paperInfo = jsonData
+                        this.isShowPaper = true
+                        this.isLoading.close() */
+						resolve(jsonData)
+                    }).catch(e => {
+                        console.error('22222222222', e);
+						reject(undefined)
+                    })
+                }
+            } catch (error) {
+                console.error('33333333333333', error);
+				reject(undefined)
+                /* this.$message({
+                    message: '打开试卷失败',
+                    type: 'error'
+                }); */
+            }
+		})
+	},
+	/* 给富文本添加 cntr是防止读取的是其他用户的BLOB */
+	async doAddHost(exerciseItem, url) {
+		// console.log(exerciseItem, paperItem, cntr, nodeId, sasString)
+		if (exerciseItem.source && exerciseItem.source === 3) {
+			return exerciseItem
+		}
+		/* 如果操作的是试卷内的试题 则需要拿试卷的code来作为containerName */
+		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] : exerciseItem.answer,
+			explain: exerciseItem.explain,
+		}
+		isSubjective && delete richTextObj.answer
+		return new Promise((resolve, reject) => {
+			let promiseArr = []
+			// 遍历题目的所有富文本内容
+			for (let key in richTextObj) {
+				promiseArr.push(new Promise(async (r, j) => {
+					let videoSrcList = this.getRichTextSrc(richTextObj[key], 'video')
+					let audioSrcList = this.getRichTextSrc(richTextObj[key], 'audio')
+					let srcList = videoSrcList.concat(audioSrcList)
+					if (srcList.length) {
+						console.log('要添加list', srcList)
+						for (let i = 0; i < srcList.length; i++) {
+							let src = decodeURI(srcList[i])
+							let showSrc = src
+							let spStr = src.split('.')
+							if(spStr[spStr.length - 1] === 'MP4' || spStr[spStr.length - 1] === 'MP3') {
+								showSrc = src.split('.').slice(0, -1).join('.')
+								showSrc = showSrc + '_1.' + spStr[spStr.length - 1]
+							}
+							let blobUrl = url + '/' + showSrc
+							try {
+								richTextObj[key] = richTextObj[key].replaceAll(`src="${src}"`, `src="${blobUrl}"`);
+							} catch (e) {
+								j(500)
+							}
+						}
+						if (key === 'answer' && Array.isArray(exerciseItem.answer) && exerciseItem.answer.length) {
+							exerciseItem.answer[0] = richTextObj[key]
+						} else {
+							exerciseItem[key] = richTextObj[key]
+						}
+						r(200)
+					} else {
+						r(200)
+					}
+				}))
+			}
+			Promise.all(promiseArr).then(result => {
+				//console.log('添加HOST之后的',exerciseItem)
+				resolve(exerciseItem)
+			}).catch(e => {
+				reject(e)
+			})
+		})
+	},
+	/* 获取富文本的资源src数据 */
+	getRichTextSrc(richText, type) {
+		if (!richText) {
+			return []
+		}
+		var videoReg = /<video.*?(?:>|\/>)/gi;
+		var imgReg = /<img.*?(?:>|\/>)/gi;
+		var audioReg = /<audio.*?(?:>|\/>)/gi;
+		//匹配src属性
+		var srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i;
+		var arr = String(richText).match(type === 'img' ? imgReg : type === 'video' ? videoReg : audioReg);
+		var result = []
+		if (!arr || !arr.length) {
+			return []
+		} else {
+			for (var i = 0; i < arr.length; i++) {
+				var src = arr[i].match(srcReg);
+				//获取图片地址
+				if (src[1]) {
+					result.push(src[1])
+				}
+			}
+			return result
+		}
+
+	}
+}

+ 4 - 20
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/utils/public.js

@@ -710,23 +710,7 @@ export default {
 	/* 数字与中文转换 */
 	/* 数字与中文转换 */
 	getChineseByNum(num) {
 	getChineseByNum(num) {
 		num = Number(num)
 		num = Number(num)
-		var upperCaseNumber = [
-			app.$t('learnActivity.score.zero'),
-			app.$t('learnActivity.score.one'),
-			app.$t('learnActivity.score.two'),
-			app.$t('learnActivity.score.three'),
-			app.$t('learnActivity.score.four'),
-			app.$t('learnActivity.score.five'),
-			app.$t('learnActivity.score.six'),
-			app.$t('learnActivity.score.seven'),
-			app.$t('learnActivity.score.eight'),
-			app.$t('learnActivity.score.nine'),
-			app.$t('learnActivity.score.ten'),
-			app.$t('learnActivity.score.hundred'),
-			app.$t('learnActivity.score.thousand'),
-			app.$t('learnActivity.score.tenThd'),
-			app.$t('learnActivity.score.hMillion')
-		]
+		var upperCaseNumber = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '百', '千', '万', '亿']
 		var length = String(num).length
 		var length = String(num).length
 		if (length === 1) {
 		if (length === 1) {
 			return upperCaseNumber[num]
 			return upperCaseNumber[num]
@@ -734,10 +718,10 @@ export default {
 			if (num === 10) {
 			if (num === 10) {
 				return upperCaseNumber[num]
 				return upperCaseNumber[num]
 			} else if (num > 10 && num < 20) {
 			} else if (num > 10 && num < 20) {
-				return app.$t('learnActivity.score.ten') + upperCaseNumber[String(num).charAt(1)]
+				return '十' + upperCaseNumber[String(num).charAt(1)]
 			} else {
 			} else {
-				return upperCaseNumber[String(num).charAt(0)] + app.$t('learnActivity.score.ten') + upperCaseNumber[
-					String(num).charAt(1)].replace(app.$t('learnActivity.score.zero'), '')
+				return upperCaseNumber[String(num).charAt(0)] + '十' + upperCaseNumber[
+					String(num).charAt(1)].replace('零', '')
 			}
 			}
 		}
 		}
 	},
 	},

+ 2 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/ActivityManage.less

@@ -230,7 +230,8 @@
         }
         }
 
 
         .el-tab-pane {
         .el-tab-pane {
-            height: 100%;
+            margin: 0 15px 15px;
+            height: calc(100% - 30px);
         }
         }
     }
     }
 
 

+ 176 - 35
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/ActivityManage.vue

@@ -1,8 +1,9 @@
 <template>
 <template>
     <el-container>
     <el-container>
         <el-header>
         <el-header>
-            <div>
-                <!-- <img :src="" alt=""> -->
+            <div style="display: flex; justify-content: center; align-items: center;">
+                <img :src="userInfo.schPicture" alt="" style="width: 40px; height: 40px; margin-right: 10px;">
+                <span>{{ userInfo.schoolName }}</span>
             </div>
             </div>
             <!-- <div> -->
             <!-- <div> -->
                 <el-dropdown class="base-user-center" size="small">
                 <el-dropdown class="base-user-center" size="small">
@@ -92,40 +93,38 @@
                                     <div v-for="(item, index) in evaluationClient.subjects" :key="index">
                                     <div v-for="(item, index) in evaluationClient.subjects" :key="index">
                                         <p class="subject-name">{{ item.subjectName }}</p>
                                         <p class="subject-name">{{ item.subjectName }}</p>
                                         <div>
                                         <div>
-                                            <el-tag type="warning" v-for="(papers, pIndex) in item.papers" :key="pIndex">{{ papers.paperName }}(/package/评测id/paper/试卷id)</el-tag>
+                                            <el-tag type="warning" v-for="(papers, pIndex) in item.papers" :key="pIndex" @click="openPaper(index, pIndex)">{{ papers.paperName }}</el-tag>
                                         </div>
                                         </div>
                                     </div>
                                     </div>
                                 </div>
                                 </div>
                                 <div class="short-code">
                                 <div class="short-code">
                                     提取码:
                                     提取码:
                                     <span style="color: #24b880;">{{ evaluationClient.shortCode }}</span>
                                     <span style="color: #24b880;">{{ evaluationClient.shortCode }}</span>
-                                    <!-- <el-button size="mini" :type="evaluationClient.activate ? 'success' : 'danger'" @click="evaluationClient.activate = 1"
-                                                :icon="evaluationClient.activate ? 'el-icon-jihuo1' : 'el-icon-jihuo'"
-                                                :title="evaluationClient.activate ? '已激活' : '激活'" circle></el-button> -->
                                 </div>
                                 </div>
                             </vuescroll>
                             </vuescroll>
                         </el-tab-pane>
                         </el-tab-pane>
                         <el-tab-pane label="学生作答" name="student" class="answer-content">
                         <el-tab-pane label="学生作答" name="student" class="answer-content">
                             <div v-show="!isInputOpen" style="height: 100%;">
                             <div v-show="!isInputOpen" style="height: 100%;">
                                 <vuescroll>
                                 <vuescroll>
-                                    <!-- 学生作答
-                                活动列表
-                                活动信息(激活按钮、信息框、学生作答框)
-                                    信息框中展示基本信息、试卷下载情况等
-                                    学生作答框展示哪些电脑在作答,是否作答完成,设置评测考试时间等
-                                            在进入学生作答页面时,判断 激活字段 是否存在,不存在需要用户输入开卷码,才能展示相关信息(勾选一次作答的班级(多选),考试时长,)
-                                            学生端需教师点击开始考试后,才能进行作答 -->
-                                    <div style="border-bottom: 1px dashed #ccc; padding: 15px 10px;">
-                                        <div>
+                                    <div style="border-bottom: 1px dashed #ccc; padding: 15px 10px; position: relative;">
+                                        <div style="position: absolute; right: 0; top: 0;">
+                                            <el-button size="mini" @click="isRoundList = true">查看已考轮次</el-button>
+                                        </div>
+                                        <!-- <div>
                                             <div>参考人数:{{ 92 }}</div>
                                             <div>参考人数:{{ 92 }}</div>
                                             <div></div>
                                             <div></div>
                                             <div></div>
                                             <div></div>
-                                        </div>
+                                        </div> -->
                                         <div>
                                         <div>
                                             <div style="margin-right: 40px; margin-bottom: 20px;">设置作答班级:
                                             <div style="margin-right: 40px; margin-bottom: 20px;">设置作答班级:
                                                 <el-tooltip class="item" effect="light" placement="bottom-start">
                                                 <el-tooltip class="item" effect="light" placement="bottom-start">
                                                     <el-select v-model="setAnswerInfo.groupId" :disabled="!!setAnswerInfo.activate" size="small" clearable multiple collapse-tags placeholder="请选择">
                                                     <el-select v-model="setAnswerInfo.groupId" :disabled="!!setAnswerInfo.activate" size="small" clearable multiple collapse-tags placeholder="请选择">
-                                                        <el-option v-for="(item, index) in evaluationClient.grouplist" :key="index" :label="item.name" :value="item.id"></el-option>
+                                                        <el-option v-for="(item, index) in evaluationClient.grouplist" :key="index" :label="item.name" :value="item.id">
+                                                            <span>
+                                                                {{ item.name }}
+                                                                <el-tag v-if="item.isUesed" type="warning" size="mini" style="margin-left: 5px;">已考试</el-tag>
+                                                            </span>
+                                                        </el-option>
                                                     </el-select>
                                                     </el-select>
                                                     <template slot="content">
                                                     <template slot="content">
                                                         <div v-if="!selectGroup.length">未选择班级</div>
                                                         <div v-if="!selectGroup.length">未选择班级</div>
@@ -152,11 +151,11 @@
                                                     </el-radio>
                                                     </el-radio>
                                                 </el-radio-group>
                                                 </el-radio-group>
                                             </div>
                                             </div>
-                                            <div style="margin-right: 40px; margin-bottom: 20px;">设置作答时间:
-                                                <el-date-picker v-show="setAnswerInfo.countdownType === 1" v-model="setAnswerInfo.startTime" :disabled="!!setAnswerInfo.activate" type="datetime" placeholder="开考时间"></el-date-picker>
-                                                <el-date-picker v-show="setAnswerInfo.countdownType != 1" v-model="setAnswerInfo.answerTime" :disabled="!!setAnswerInfo.activate" size="small" type="datetimerange" start-placeholder="开考时间" end-placeholder="截至时间"></el-date-picker>
+                                            <div style="margin-right: 40px; margin-bottom: 20px;">设置考试时间:
+                                                <el-date-picker v-show="setAnswerInfo.countdownType === 1" v-model="setAnswerInfo.startTime" :disabled="!!setAnswerInfo.activate" :picker-options="pickerOptions" type="datetime" placeholder="开考时间"></el-date-picker>
+                                                <el-date-picker v-show="setAnswerInfo.countdownType != 1" v-model="setAnswerInfo.answerTime" :disabled="!!setAnswerInfo.activate" :picker-options="pickerOptions" size="small" type="datetimerange" start-placeholder="开考时间" end-placeholder="截至时间"></el-date-picker>
                                             </div>
                                             </div>
-                                            <div v-show="setAnswerInfo.countdownType" style="margin-right: 40px; margin-bottom: 20px;">考试时长:
+                                            <div v-show="setAnswerInfo.countdownType" style="margin-right: 40px; margin-bottom: 20px;">作答时长:
                                                 <el-input-number v-model="setAnswerInfo.hour" :disabled="!!setAnswerInfo.activate" :min="0" type="number" controls-position="right" placeholder="请输入内容" size="small" style="width: 100px;"></el-input-number>
                                                 <el-input-number v-model="setAnswerInfo.hour" :disabled="!!setAnswerInfo.activate" :min="0" type="number" controls-position="right" placeholder="请输入内容" size="small" style="width: 100px;"></el-input-number>
                                                 <span style="margin: 0 15px 0 10px;">时</span>
                                                 <span style="margin: 0 15px 0 10px;">时</span>
                                                 <el-input-number v-model="setAnswerInfo.minute" :disabled="!!setAnswerInfo.activate" :min="0" :max="59" type="number" controls-position="right" placeholder="请输入内容" size="small" style="width: 100px;"></el-input-number>
                                                 <el-input-number v-model="setAnswerInfo.minute" :disabled="!!setAnswerInfo.activate" :min="0" :max="59" type="number" controls-position="right" placeholder="请输入内容" size="small" style="width: 100px;"></el-input-number>
@@ -166,10 +165,9 @@
                                                 <el-button size="mini" :type="setAnswerInfo.activate ? 'info' : 'primary'" @click="startExam()" v-show="evaluationClient.progress === 'going'">{{ setAnswerInfo.activate ? '停止考试' : '开始考试'}}</el-button>
                                                 <el-button size="mini" :type="setAnswerInfo.activate ? 'info' : 'primary'" @click="startExam()" v-show="evaluationClient.progress === 'going'">{{ setAnswerInfo.activate ? '停止考试' : '开始考试'}}</el-button>
                                             </div>
                                             </div>
                                         </div>
                                         </div>
-                                        <div></div>
-                                        <div></div>
                                     </div>
                                     </div>
-                                    <div style="display: flex; flex-wrap: wrap; margin-top: 20px;">
+                                    <div v-if="!studentListShow.length" style="margin-top: 10%; font-size: 25px; text-align: center;">暂未开始考试</div>
+                                    <div style="display: flex; flex-wrap: wrap; margin-top: 20px;" v-else>
                                         <div v-for="(item, index) in studentListShow" :key="index" class="student-info" :style="{'border-color': item.isAnswer === 1 ? '#4393e4' : item.isAnswer === 2 ? '#53a929' : '#909399'}">
                                         <div v-for="(item, index) in studentListShow" :key="index" class="student-info" :style="{'border-color': item.isAnswer === 1 ? '#4393e4' : item.isAnswer === 2 ? '#53a929' : '#909399'}">
                                             <p class="name-info" :style="{'background-color': item.isAnswer === 1 ? '#4393e4' : item.isAnswer === 2 ? '#53a929' : '#909399'}">
                                             <p class="name-info" :style="{'background-color': item.isAnswer === 1 ? '#4393e4' : item.isAnswer === 2 ? '#53a929' : '#909399'}">
                                                 <i class="el-icon-s-custom" style="margin-right: 5px;"></i>
                                                 <i class="el-icon-s-custom" style="margin-right: 5px;"></i>
@@ -193,7 +191,7 @@
                                     </div>
                                     </div>
                                 </vuescroll>
                                 </vuescroll>
                             </div>
                             </div>
-                            <div v-show="isInputOpen">请输入开卷码,验证查看权限</div>
+                            <div v-show="isInputOpen" style="font-size: 25px; text-align: center; margin-top: 20%;">请输入开卷码,验证查看权限</div>
                         </el-tab-pane>
                         </el-tab-pane>
                     </el-tabs>
                     </el-tabs>
                 </template>
                 </template>
@@ -221,6 +219,21 @@
                 <el-button @click="isStartExam = false">取消</el-button>
                 <el-button @click="isStartExam = false">取消</el-button>
             </span>
             </span>
         </el-dialog>
         </el-dialog>
+        <el-dialog title="已考试的轮次信息" width="30%" :visible.sync="isRoundList">
+            <div v-for="(item, index) in roundList" :key="index" style="border-bottom: 1px dashed #ccc; padding: 5px 10px;">
+                <p>
+                    <i class="el-icon-time"></i>
+                    考试时间:{{ item.startTime }} - {{ item.endTime }}</p>
+                <p>考试班级:
+                    <span v-for="(group, gIndex) in item.groupList" :key="gIndex">{{ group.name }}</span>
+                </p>
+                <p style="display: flex;">
+                    <span style="margin-right: 30px;">时间类型:{{ !item.countdownType ? '无' : item.countdownType === 1 ? '统一作答时长' : '学生个人作答时长' }}</span>
+                    <span v-show="item.countdownType">作答时长:{{ item.duration }}</span>
+                </p>
+                <el-button @click="getExamRoundInfo(item.id, true)" style="float: right;">查看详情</el-button>
+            </div>
+        </el-dialog>
         <div class="open-evaluation" v-if="showErrorMsgs">
         <div class="open-evaluation" v-if="showErrorMsgs">
             <div class="info-box error-info" v-for="(item, index) in openErrorMsgs" :key="index">
             <div class="info-box error-info" v-for="(item, index) in openErrorMsgs" :key="index">
                 <p>
                 <p>
@@ -237,15 +250,21 @@
                 <span><i class="el-icon-close"></i></span>
                 <span><i class="el-icon-close"></i></span>
             </div> -->
             </div> -->
         </div>
         </div>
+        <el-dialog title="试卷详情" width="50%" :visible.sync="isShowPaper" class="test-paper">
+            <TestPaper v-if="isShowPaper" :paperInfo="paperInfo"></TestPaper>
+        </el-dialog>
     </el-container>
     </el-container>
 </template>
 </template>
 
 
 <script>
 <script>
 import {jwtDecode} from 'jwt-decode'
 import {jwtDecode} from 'jwt-decode'
 import { Loading } from 'element-ui'
 import { Loading } from 'element-ui'
+import TestPaper from './TestPaper.vue'
 
 
 export default {
 export default {
+    components: { TestPaper },
     data() {
     data() {
+        const that = this
         return {
         return {
             isLoading: undefined,
             isLoading: undefined,
             examList: [],
             examList: [],
@@ -261,6 +280,7 @@ export default {
             isStartExam: false, //开始考试
             isStartExam: false, //开始考试
             studentListShow: [],
             studentListShow: [],
             setAnswerInfo: {
             setAnswerInfo: {
+                id: '',
                 groupId: [],
                 groupId: [],
                 startTime: '',
                 startTime: '',
                 answerTime: [],
                 answerTime: [],
@@ -277,16 +297,32 @@ export default {
             deviceId: '',
             deviceId: '',
             openErrorMsgs: [],
             openErrorMsgs: [],
             showErrorMsgs: false,
             showErrorMsgs: false,
+            paperList: [],
+            paperInfo: undefined,
+            isShowPaper: false,
+            roundList: [], //开考的轮次信息
+            isRoundList: false,
+            pickerOptions: {
+                disabledDate(date) {
+                    return date.getTime() < that.evaluationClient.stime || date.getTime() > that.evaluationClient.etime
+                }
+            },
         }
         }
     },
     },
     created() {
     created() {
+        this.isLoading = Loading.service({
+            lock: true,
+            text: '加载中',
+            background: 'rgba(0, 0, 0, 0.7)'
+        })
         this.deviceId = localStorage.getItem('deviceId')
         this.deviceId = localStorage.getItem('deviceId')
         this.userInfo = jwtDecode(localStorage.getItem('auth_token'))
         this.userInfo = jwtDecode(localStorage.getItem('auth_token'))
         let schoolInfo = JSON.parse(localStorage.getItem('schoolInfo'))
         let schoolInfo = JSON.parse(localStorage.getItem('schoolInfo'))
         this.userInfo.schoolName = schoolInfo.name
         this.userInfo.schoolName = schoolInfo.name
-        this.userInfo.picture = schoolInfo.picture
+        this.userInfo.schPicture = schoolInfo.picture
         this.getActivityList()
         this.getActivityList()
         if(!this.deviceId) this.viewNetworkInfo()
         if(!this.deviceId) this.viewNetworkInfo()
+        this.isLoading.close()
     },
     },
     mounted() {
     mounted() {
     },
     },
@@ -311,7 +347,7 @@ export default {
                     return
                     return
                 }
                 }
                 if(!this.evaluationClient.openCode) this.isInputOpen = true
                 if(!this.evaluationClient.openCode) this.isInputOpen = true
-                else this.getExamRoundInfo()
+                else this.getRoundList()
             }
             }
         },
         },
         getNowTime() {
         getNowTime() {
@@ -661,6 +697,10 @@ export default {
                         item.startTime = this.$tools.getDate(item.stime, 'year')
                         item.startTime = this.$tools.getDate(item.stime, 'year')
                         item.endTime = this.$tools.getDate(item.etime, 'year')
                         item.endTime = this.$tools.getDate(item.etime, 'year')
                         item.progress = this.timeStatus(item.stime, item.etime)
                         item.progress = this.timeStatus(item.stime, item.etime)
+                        item.grouplist = item.grouplist.map(group => {
+                            group.isUesed = false
+                            return group
+                        })
                         return item
                         return item
                     })
                     })
                     this.onSelectAct(0)
                     this.onSelectAct(0)
@@ -718,7 +758,7 @@ export default {
             }
             }
             this.studentListShow = []
             this.studentListShow = []
             this.curIndex = index
             this.curIndex = index
-            this.evaluationClient = this.examList[index]
+            // this.evaluationClient = this.examList[index]
             let params = {
             let params = {
                 deviceId: this.deviceId,
                 deviceId: this.deviceId,
                 shortCode: this.examList[index].shortCode,
                 shortCode: this.examList[index].shortCode,
@@ -767,9 +807,13 @@ export default {
                 if(openCode && this.openCode === openCode) {
                 if(openCode && this.openCode === openCode) {
                     this.evaluationClient.openCode = openCode
                     this.evaluationClient.openCode = openCode
                     this.examList[this.curIndex].openCode = openCode
                     this.examList[this.curIndex].openCode = openCode
-                    this.getExamRoundInfo()
+                    this.getRoundList()
                     this.openCode = ''
                     this.openCode = ''
                     this.isInputOpen = false
                     this.isInputOpen = false
+                    this.$message({
+                        message: '检测成功',
+                        type: 'success'
+                    });
                 } else {
                 } else {
                     let params = {
                     let params = {
                         deviceId: this.deviceId,
                         deviceId: this.deviceId,
@@ -781,7 +825,7 @@ export default {
                         if(res.code === 200) {
                         if(res.code === 200) {
                             this.evaluationClient.openCode = this.openCode
                             this.evaluationClient.openCode = this.openCode
                             this.examList[this.curIndex].openCode = this.openCode
                             this.examList[this.curIndex].openCode = this.openCode
-                            this.getExamRoundInfo()
+                            this.getRoundList()
                             sessionStorage.setItem(this.evaluationClient.id, this.openCode)
                             sessionStorage.setItem(this.evaluationClient.id, this.openCode)
                             this.openCode = ''
                             this.openCode = ''
                         } else if(res.code === 1) {
                         } else if(res.code === 1) {
@@ -815,10 +859,22 @@ export default {
                 console.log('11111111111111');
                 console.log('11111111111111');
                 
                 
             }) */
             }) */
+            if(this.setAnswerInfo.countdownType && !this.setAnswerInfo.hour && !this.setAnswerInfo.minute || !this.setAnswerInfo.groupId.length || !this.setAnswerInfo.startTime && this.setAnswerInfo.answerTime.length != 2) {
+                this.$message({
+                    message: '请完善考试信息!',
+                    type: 'warning'
+                });
+                return
+            }
+            this.isLoading = Loading.service({
+                lock: true,
+                text: '加载中',
+                background: 'rgba(0, 0, 0, 0.7)'
+            })
             let params = {
             let params = {
                 evaluationId: this.evaluationClient.id,
                 evaluationId: this.evaluationClient.id,
                 groupList: [],
                 groupList: [],
-                activate: this.evaluationClient.activate ? 0 : 1,
+                activate: this.setAnswerInfo.activate ? 0 : 1,
                 countdownType: this.setAnswerInfo.countdownType,
                 countdownType: this.setAnswerInfo.countdownType,
                 countdown: '',
                 countdown: '',
                 startline: '',
                 startline: '',
@@ -861,6 +917,8 @@ export default {
                         type: 'warning'
                         type: 'warning'
                     });
                     });
                 }
                 }
+            }).finally(() => {
+                this.isLoading.close()
             })
             })
         },
         },
         updatePackage() {
         updatePackage() {
@@ -894,14 +952,53 @@ export default {
                 this.isLoading.close()
                 this.isLoading.close()
             })
             })
         },
         },
-        getExamRoundInfo() {
+        getRoundList() {
+            let params = {
+                evaluationId: this.evaluationClient.id,
+                openCode: this.evaluationClient.openCode,
+                shortCode: this.evaluationClient.shortCode,
+            }
+            this.$api.getRoundList(params).then(res => {
+                if(res.code === 200) {
+                    if(res.settings.length) {
+                        this.roundList = res.settings.map(item => {
+                            item.startTime = this.$tools.getDate(item.startline, 'year')
+                            item.endTime = this.$tools.getDate(item.deadline, 'year')
+                            let second = item.countdown / 1000 / 60
+                            let hour = Math.floor(second / 60)
+                            let minute = (second - hour * 60)
+                            item.duration = `${hour}时${minute}分`
+                            return item
+                        })
+                        let roundGroup = res.settings.map(item => item.groupList).flat();
+                        this.evaluationClient.grouplist.forEach(item => {
+                            if(roundGroup.find(group => group.id === item.id)) item.isUesed = true
+                        })
+                        let activateInfo = res.settings.find(item => item.activate === 1)
+                        if(activateInfo) this.getExamRoundInfo(activateInfo.id)
+                    }
+                }
+            })
+        },
+        getExamRoundInfo(settingId, isDialog) {
+            if(isDialog) {
+                this.isRoundList = false
+                if(this.setAnswerInfo.id === settingId) return
+                this.isLoading = Loading.service({
+                    lock: true,
+                    text: '加载中',
+                    background: 'rgba(0, 0, 0, 0.7)'
+                })
+            }
             let params = {
             let params = {
                 evaluationId: this.evaluationClient.id,
                 evaluationId: this.evaluationClient.id,
                 openCode: this.evaluationClient.openCode,
                 openCode: this.evaluationClient.openCode,
                 shortCode: this.evaluationClient.shortCode,
                 shortCode: this.evaluationClient.shortCode,
+                settingId,
             }
             }
             this.$api.getExamRoundInfo(params).then(res => {
             this.$api.getExamRoundInfo(params).then(res => {
                 if(res.code === 200) {
                 if(res.code === 200) {
+                    this.setAnswerInfo.id = res.setting.id
                     this.setAnswerInfo.groupId = res.setting.groupList.map(item => item.id)
                     this.setAnswerInfo.groupId = res.setting.groupList.map(item => item.id)
                     this.setAnswerInfo.activate = res.setting.activate
                     this.setAnswerInfo.activate = res.setting.activate
                     this.setAnswerInfo.countdownType = res.setting.countdownType
                     this.setAnswerInfo.countdownType = res.setting.countdownType
@@ -919,11 +1016,49 @@ export default {
                         return item
                         return item
                     })
                     })
                 }
                 }
+            }).finally(() => {
+                if(isDialog) this.isLoading.close()
             })
             })
         },
         },
         delInfo(index) {
         delInfo(index) {
             this.openErrorMsgs.splice(index, 1)
             this.openErrorMsgs.splice(index, 1)
         },
         },
+        async openPaper(index, paperIndex) {
+            if(this.needUpdate.status === 1) {
+                this.$message({
+                    message: '活动需更新,请先下载',
+                    type: 'warning'
+                });
+                return
+            }
+            if(!this.evaluationClient.openCode) {
+                this.isInputOpen = true
+                return
+            }
+            this.isLoading = Loading.service({
+                lock: true,
+                text: '获取试卷中...',
+                background: 'rgba(0, 0, 0, 0.7)'
+            })
+            this.paperInfo = this.paperList.find(item => item.id === this.evaluationClient.subjects[index].papers[paperIndex].paperId) || undefined
+            if(this.paperList.find(item => item.id === this.evaluationClient.subjects[index].papers[paperIndex].paperId)) {
+                this.isShowPaper = true
+                this.isLoading.close()
+                return
+            }
+            let paperInfo = await this.$evTools.getPaperInfo(this.evaluationClient.id, this.evaluationClient.subjects[index].papers[paperIndex].paperId)
+            if(!paperInfo) {
+                this.$message({
+                    message: '打开试卷失败',
+                    type: 'error'
+                });
+            } else {
+                this.paperList.push(paperInfo)
+                this.paperInfo = paperInfo
+                this.isShowPaper = true
+            }
+            this.isLoading.close()
+        },
         loginOut() {
         loginOut() {
             localStorage.removeItem('auth_token')
             localStorage.removeItem('auth_token')
 			localStorage.removeItem('expires_in')
 			localStorage.removeItem('expires_in')
@@ -937,7 +1072,7 @@ export default {
         selectGroup() {
         selectGroup() {
             return this.evaluationClient.grouplist.filter(item => this.setAnswerInfo.groupId.includes(item.id))
             return this.evaluationClient.grouplist.filter(item => this.setAnswerInfo.groupId.includes(item.id))
         },
         },
-    },
+    }
 }
 }
 </script>
 </script>
 
 
@@ -976,7 +1111,13 @@ export default {
 .el-tabs__header {
 .el-tabs__header {
     margin-bottom: 0;
     margin-bottom: 0;
 }
 }
-.el-tab-pane {
-    margin: 0 15px 15px;
+
+.test-paper {
+    height: 100%;
+
+    .el-dialog,
+    .el-dialog__body {
+        height: calc(100% - 120px);
+    }
 }
 }
 </style>
 </style>

+ 264 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/TestPaper.less

@@ -0,0 +1,264 @@
+.paper-body {
+    width: 98%;
+    height: 88%;
+    padding-left: 20px;
+    position: relative;
+
+    .back-to-top {
+        position: absolute;
+        right: 10px;
+        bottom: 10px;
+        height: 40px;
+        width: 40px;
+        background: #979797;
+        z-index: 99999;
+        border-radius: 50%;
+        cursor: pointer;
+        text-align: center;
+        line-height: 40px;
+
+        &:hover {
+            background: rgb(128, 128, 128);
+        }
+
+        .el-icon-arrow-up {
+            font-size: 22px;
+            color: white;
+        }
+    }
+
+    .paper-base-info {
+        position: sticky;
+        top: -2px;
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        background-color: #fff;
+        border-bottom: 1px dashed #cfcfcf;
+        z-index: 99;
+        padding: 10px 0 20px 0;
+
+        .base-info-btn:not(:last-child) {
+            margin-right: 10px;
+        }
+
+        .analysis-info {
+            font-weight: bold;
+            color: #69baec;
+            margin: 0 5px;
+        }
+
+        .base-info-item {
+            margin-right: 15px;
+        }
+    }
+
+
+    .components-el-container {
+        .paper-header {
+            margin-top: 20px;
+        }
+
+        /*向垂直水平居中*/
+        .flex-col-center {
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+        }
+
+        .paper-title {
+            font-size: 30px;
+            margin: 30px 0;
+            font-weight: bold;
+            vertical-align: middle;
+            text-align: center;
+            cursor: pointer;
+        }
+
+        .no-data-text {
+            width: 100%;
+            padding: 30px;
+            background: #fff;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            margin-top: 10px;
+            font-size: 16px;
+        }
+
+
+        .content-wrap {
+            position: relative;
+            width: 100%;
+            height: auto;
+            display: flex;
+            flex-direction: column;
+
+            .type-name {
+                font-size: 18px;
+                font-weight: bold;
+                margin-top: 20px;
+            }
+
+            .exercise-item {
+                position: relative;
+                width: calc(100% - 45px);
+                height: auto;
+                padding: 10px 20px 10px 20px;
+                margin-bottom: 10px;
+                margin-top: 30px;
+                font-size: 14px;
+                background: #fff;
+                border: 2px solid transparent;
+                cursor: pointer;
+
+                p {
+                    width: 92%;
+                }
+
+                &:hover {
+                    border: 2px solid #01b4ef;
+                    box-shadow: none !important;
+
+                    .item-tools-bind {
+                        display: unset;
+                    }
+                }
+
+                table,
+                td {
+                    border: 1px solid rgb(128, 128, 128);
+                    border-collapse: collapse;
+                    text-align: center;
+                    padding: 5px 10px;
+                }
+
+                .item-question {
+                    position: relative;
+                    cursor: pointer;
+
+                    .item-question-order {
+                        display: inline-block;
+                        vertical-align: top;
+                        width: 32px;
+                    }
+
+                    .item-question-text {
+                        display: inline-block;
+                        width: calc(90% - 30px);
+                    }
+                }
+
+                .item-options {
+                    margin-top: 10px;
+
+                    .item-option-content {
+                        margin: 7px 0;
+                    }
+
+                    .item-option-order {
+                        display: inline-block;
+                        vertical-align: top;
+                        width: 30px;
+                    }
+
+                    .item-option-text {
+                        display: inline-block;
+                        width: calc(100% - 30px);
+                    }
+                }
+
+                .item-btn-toggle {
+                    position: absolute;
+                    right: 10px;
+                    top: 8px;
+                    width: 15%;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                }
+
+                .toggle-area {
+                    border-top: 1px #c3c3c34d dashed;
+                    padding-top: 10px;
+                    margin-top: 10px;
+                }
+
+                .item-explain {
+                    margin-top: 10px;
+                    cursor: pointer;
+                    font-size: 14px;
+
+                    .explain-title {
+                        width: 12%;
+                        max-width: 100px;
+                        display: inline-block;
+                        color: rgb(16, 171, 231);
+                    }
+
+                    .item-explain-details {
+                        vertical-align: top;
+                        display: inline-block;
+                        width: calc(100% - 100px);
+
+                        // width: 90%;
+                        .item-point-tag {
+                            padding: 0 10px;
+                            border: 1px solid #d6d6d6;
+                            margin-left: 10px;
+                            border-radius: 4px;
+
+                            &:first-child {
+                                margin-left: 0;
+                            }
+                        }
+
+                        .repair-item {
+                            display: inline-flex;
+                            margin-right: 20px;
+                            color: #21b1ff;
+                            text-decoration: underline;
+                            align-items: flex-end;
+
+                            &-link {
+                                margin-left: 5px;
+                            }
+
+                        }
+                    }
+                }
+
+                .item-answer,
+                .item-explain {
+                    line-height: 26px;
+                }
+
+                img {
+                    vertical-align: middle;
+                    max-width: 100%;
+                }
+
+                video {
+                    max-width: 100%;
+                }
+
+                .item-answer-item {
+                    margin-left: 25px;
+                    padding: 0 25px;
+                    border-bottom: 2px solid rgb(128, 128, 128);
+
+                    p {
+                        display: inline-block;
+                    }
+
+                    &:first-child {
+                        margin-left: 0;
+                    }
+                }
+            }
+        }
+    }
+}

+ 315 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/admin/TestPaper.vue

@@ -0,0 +1,315 @@
+<template>
+    <div class="paper-body">
+        <div class="back-to-top flex-col-center" title="返回顶部" v-if="isShowBackToTop" @click="handleBackToTop">
+            <i class="el-icon-arrow-up" />
+        </div>
+        <vuescroll ref="paperRef" @handle-scroll="handleScroll">
+            <div class="paper-base-info">
+                <div style="display: flex;">
+                    <span class="base-info-item">总分:<span class="analysis-info" style="cursor: pointer;">{{ paperInfo.score }}</span>分</span>
+                    <!-- <span class="base-info-item" @click="onShowScoreTable" style="cursor: pointer;">已配:<span class="analysis-info">{{ allocatedScore || 0 }}</span>分</span> -->
+                    <span class="base-info-item">题量:<span class="analysis-info">{{ paperInfo.item ? paperInfo.item.length : 0 }}</span></span>
+                    <span class="base-info-item" style="display: flex;">难度:
+                        <el-rate v-model="paperInfo.item.length ? +paperDiff : 0" allow-half disabled></el-rate>
+                    </span>
+                </div>
+                <div>
+                    <el-button class="base-info-btn" type="primary" size="small" @click="onHandleToggle()" v-show="paperInfo.item.length">{{ isAllOpen ? '全部折叠' : '全部展开' }}</el-button>
+                    <el-button class="base-info-btn" type="primary" size="small" @click="onViewModelChange()" v-show="paperInfo.item.length">{{ viewModel === 'type' ? '题号排序' : '题型排序' }}</el-button>
+                </div>
+            </div>
+            <div class="components-el-container">
+                <div class="paper-header flex-col-center">
+                    <p class="paper-title">{{ paperInfo.name }}</p>
+                </div>
+                <div v-if="!exerciseList.length" class="no-data-text">
+                    <img src="@/assets/icon/no_data.svg" width="120" />
+                    <span style="margin-top: 15px; color: #808080">暂无数据</span>
+                </div>
+                <div class="content-wrap" ref="mathJaxContainer" v-else>
+                    <div class="list-view" :key="typeIndex" v-for="(typeItem, typeIndex) in listData">
+                        <p v-show="viewModel === 'type' && typeItem.list.length" class="type-name">
+                            {{ $tools.getChineseByNum(getLatestTypeIndex(typeItem.type) + 1) }}: {{ exersicesType[typeItem.type] }}
+                            <span style="font-size: 14px; font-weight: 600">(共{{ typeItem.list.length }}题,总计{{ typeItem.score || 0 }}分)</span>
+                        </p>
+                        <div v-for="(item, index) of typeItem.list" :key="index" class="exercise-item" :data-id="item.id">
+                            <div @click="onQuestionToggle(exerciseList.indexOf(item), item.id, $event, typeItem.list)" style="max-width: 85%">
+                                <!-- 题干部分 -->
+                                <div class="item-question">
+                                    <div v-if="viewModel === 'list'">
+                                        <div class="item-question-order">{{ index + 1 }} :</div>
+                                        <div class="item-question-text" v-html="item.question"></div>
+                                    </div>
+                                    <div v-else>
+                                        <div class="item-question-order">{{ exerciseList.indexOf(item) + 1 }} :</div>
+                                        <div class="item-question-text" v-html="item.question"></div>
+                                    </div>
+                                </div>
+                                <!-- 选项部分 -->
+                                <div v-for="(option, optionIndex) in item.option" :key="optionIndex" class="item-options">
+                                    <div class="item-option-content">
+                                        <div class="item-option-order">{{ String.fromCharCode(64 + parseInt(optionIndex + 1)) }} :</div>
+                                        <div class="item-option-text" v-html="option.value"></div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="exercise-item-children" v-if="item.children.length">
+                                <!-- <BaseChild :children="item.children" :totalScore="item.score" :isChangePaper="isChangePaper" :isShowScore="!isExamPaper" :canFix="canFix" @onEditChildFinish="onEditChildFinish"> </BaseChild> -->
+                            </div>
+                            <div class="item-btn-toggle">
+                                <span style="margin-right: 10px; font-size: 16px">
+                                    <span style="font-weight: bold; color: #0086e6">{{ item.score }}</span> 分
+                                </span>
+                                <i @click.stop="onQuestionToggle(exerciseList.indexOf(item), item.id, $event, typeItem.list)" :class="collapseList.indexOf(exerciseList.indexOf(item)) > -1 ? 'el-icon-jiantouxiangshang' : 'el-icon-jiantouxiangxia'" style="font-size: 20px;" v-if="item.type !== 'compose'" />
+                            </div>
+                            <!-- 答案以及解析 -->
+                            <transition name="slide" v-if="item.type !== 'compose'">
+                                <!-- <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area"> -->
+                                <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area">
+                                    <div>
+                                        <!-- 答案展示部分 -->
+                                        <div class="item-explain">
+                                            <span class="explain-title">【题型】</span>
+                                            <div class="item-explain-details">{{ exersicesType[item.type] }}</div>
+                                        </div>
+                                        <div class="item-explain">
+                                            <span class="explain-title">【答案】</span>
+                                            <div class="item-explain-details">
+                                                <!-- 问答题答案 -->
+                                                <div v-if="item.type === 'subjective' || item.type === 'complete' || item.type === 'connector' || item.type === 'correct'">
+                                                    <span v-for="(answer, index) in item.answer" :key="index" v-html="item.answer.length ? answer : '暂无'"></span>
+                                                </div>
+                                                <!-- 问答题答案 -->
+                                                <div v-else-if="item.type === 'judge'">
+                                                    <span>{{ item.answer.length ? (item.answer[0] === "A" ? '正确' : '错误') : '答案' }}</span>
+                                                </div>
+                                                <!-- 其余题型答案 -->
+                                                <div v-else>
+                                                    <span :class="[item.type === 'complete' ? 'item-answer-item' : '']" v-for="(answer, index) in item.answer" :key="index">{{ answer }}</span>
+                                                </div>
+                                            </div>
+                                        </div>
+                                        <!-- 解析部分 -->
+                                        <div class="item-explain">
+                                            <span class="explain-title">【解析】</span>
+                                            <div class="item-explain-details">
+                                                <span v-html="item.explain || '暂无'"></span>
+                                            </div>
+                                        </div>
+                                        <!-- 知识点部分 -->
+                                        <div class="item-explain">
+                                            <span class="explain-title">【知识点】</span>
+                                            <div class="item-explain-details">
+                                                <span v-if="!item.knowledge || !_.compact(item.knowledge).length">暂无</span>
+                                                <div v-else>
+                                                    <span v-for="(point, index) in item.knowledge" :key="index" class="item-point-tag">
+                                                        {{ point }}
+                                                    </span>
+                                                </div>
+                                            </div>
+                                        </div>
+                                        <!-- 认知层次部分 -->
+                                        <div class="item-explain">
+                                            <span class="explain-title">【认知层次】</span>
+                                            <div class="item-explain-details">
+                                                {{ item.field ? exersicesField[item.field - 1] : exersicesField[0] }}
+                                            </div>
+                                        </div>
+                                        <!-- 补救资源部分 -->
+                                        <div class="item-explain">
+                                            <span class="explain-title">【补救资源】</span>
+                                            <div class="item-explain-details">
+                                                <div v-if="item.repair && item.repair.length" style="display: flex; flex-wrap: wrap">
+                                                    <div class="repair-item" v-for="(link, index) in item.repair" :key="index">
+                                                        <img :src="$tools.getFileThum(link.type, link.name)" width="20" />
+                                                        <span class="repair-item-link" @click.stop="onRepairLinkClick(link)">{{ link.name }}</span>
+                                                    </div>
+                                                </div>
+                                                <span v-else>暂无</span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                            </transition>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </vuescroll>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        paperInfo: {
+            type: Object,
+            default: undefined
+        }
+    },
+    data() {
+        return {
+            isShowBackToTop: false,
+            typeList: ["single", "multiple", "judge", "complete", "subjective", "connector", "correct", "compose"],
+            exersicesType: {
+                single: '单选题',
+                multiple: '多选题',
+                judge: '判断题',
+                complete: '填空题',
+                subjective: '问答题',
+                connector: '连线题',
+                correct: '改错题',
+                compose: '综合题'
+            },
+            exersicesField: ['记忆', '理解', '应用', '分析', '评价', '创造'],
+            paperDiff: 0,
+            exerciseList: [],
+            groupList: [],
+            orderList: [],
+            isAllOpen: false,
+            viewModel: 'type',
+            collapseList: [],
+        }
+    },
+    mounted() {
+        this.viewModel = this.paperInfo.itemSort === 1 ? 'list' : 'type'
+        this.paperDiff = this.paperInfo.item ? this.handleDiffCalc(this.paperInfo.item) : 0
+
+        if (this.paperInfo.item && this.paperInfo.item.length) {
+            let that = this;
+            this.groupList = []; // 题型排序的数据
+            this.orderList = []; //顺序排列的数据
+            this.exerciseList = [];
+            that.typeSummaryInfo = this.paperInfo.typeSummaryInfo || that.typeSummaryInfo;
+            this.paperInfo = this.paperInfo;
+            this.multipleRule = this.paperInfo.multipleRule || 1; // 配分规则
+            if (this.paperInfo.item.length) {
+                this.paperInfo.item.forEach((i) => {
+                    if (!i.score) i.score = 0;
+                    // 如果有综合题 则将小题的分数进行累加作为综合题的分数
+                    if (i.type === "compose" && i.children.length) {
+                        i.score = i.children.reduce((a, b) => a + b.score, 0);
+                    }
+                });
+                // 给顺序题目排序
+                this.orderList.push({
+                    list: this.paperInfo.item
+                });
+                /* 处理试卷内题目按照题型排序 */
+                this.typeList.forEach((item) => {
+                    this._.mapKeys(this._.groupBy(this.paperInfo.item, "type"), function (value, key) {
+                        if (key === item) {
+                            /* 按照题型排序,并且计算每种题型的总分 */
+                            let typeInfo = {
+                                type: key,
+                                list: value,
+                                score: value.reduce((p, e) => p + e.score, 0)
+                            };
+                            that.groupList.push(typeInfo);
+                            that.exerciseList = that.exerciseList.concat(value);
+                        }
+                    });
+                });
+            }
+            // 重新赋值
+            // this.originData = this.exerciseList;
+            
+            /* window.MathJax.startup.promise.then(() => {
+                window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+            }) */
+        }
+    },
+    computed: {
+        listData() {
+            return this.viewModel === "type" ? this.groupList : this.orderList;
+        },
+    },
+    methods: {
+        /**
+         * 计算试卷题目平均难度
+         * @param arr 试题集合
+         */
+        handleDiffCalc(arr) {
+            let levelArr = arr.map(i => i.level)
+            return this._.meanBy(levelArr).toFixed(1)
+        },
+        /**
+         * 全部展开与全部折叠
+         */
+        onHandleToggle() {
+            if (this.isAllOpen) {
+                this.collapseList = [];
+            } else {
+                this.collapseList = [...this.exerciseList.keys()];
+            }
+            this.isAllOpen = !this.isAllOpen
+        },
+        onViewModelChange() {
+            this.viewModel = this.viewModel === 'type' ? 'list' : 'type'
+        },
+        /* 获取当前题型的说明文本 */
+        getTypeSummary(typeItem) {
+            let that = this;
+            return (typeItem) => {
+                if (that.typeSummaryInfo && that.typeSummaryInfo[typeItem.type]) {
+                    return that.typeSummaryInfo[typeItem.type];
+                } else {
+                    return `共 ${typeItem.list.length}题,总计${typeItem.score || 0}分`;
+                }
+            };
+        },
+        /**
+         * 题干展开与收缩
+         * @param index
+         * @param id
+         */
+        onQuestionToggle(index, id, e) {
+            e.stopPropagation();
+            this.curEditItemId = null;
+            let listIndex = this.collapseList.indexOf(index);
+            if (listIndex > -1) {
+                this.collapseList.splice(listIndex, 1);
+            } else {
+                /** 如果是首次展开 则需要获取详细数据 否则直接展开 */
+                if (!this.exerciseList[index].answer.length) {
+                    this.collapseList.push(index);
+                } else {
+                    this.collapseList.push(index);
+                }
+            }
+        },
+        /* 补救资源点击事件 */
+        onRepairLinkClick(link) {
+            window.open(/^(http:|https:)/i.test(link.blobUrl) ? link.blobUrl : "http://" + link.blobUrl);
+        },
+        getLatestTypeIndex(type) {
+            let arr = [];
+            this.groupList.forEach((i) => {
+                if (i.list.length) {
+                    arr.push(i.type);
+                }
+            });
+            return arr.indexOf(type);
+        },
+        // 判断容器滚动距离
+        handleScroll(vertical, horizontal, nativeEvent) {
+            this.isShowBackToTop = vertical.scrollTop > 400
+        },
+        handleBackToTop() {
+            console.log(this)
+            this.$nextTick(() => {
+                if (this.$refs.paperRef) {
+                    this.$refs['paperRef'].scrollTo({
+                        y: '0'
+                    },
+                    500
+                )}
+            })
+        },
+    }
+}
+</script>
+
+<style lang="less" scoped>
+@import './TestPaper.less';
+</style>

+ 3 - 3
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/login/Admin.vue

@@ -50,8 +50,8 @@
                     </div>
                     </div>
                     <div class="network-info">
                     <div class="network-info">
                         <span @click="showDevice()">
                         <span @click="showDevice()">
-                            <i v-show="hybridType" class="el-icon-success" style="color: #0eb90e;"></i>
-                            <i v-show="!hybridType" class="el-icon-error" style="color: #fb3636;"></i>
+                            <i v-show="hybridType" class="el-icon-yunduanziyuan" style="color: #0eb90e;"></i>
+                            <i v-show="!hybridType" class="el-icon-yunduan-guanbi-xianxing" style="color: #fb3636;"></i>
                             服务端信息
                             服务端信息
                         </span>
                         </span>
                     </div>
                     </div>
@@ -113,8 +113,8 @@
                 内的所有条款。如您同意以上协议内容,请点击“同意并继续”。
                 内的所有条款。如您同意以上协议内容,请点击“同意并继续”。
             </p>
             </p>
             <span slot="footer" class="dialog-footer">
             <span slot="footer" class="dialog-footer">
-                <el-button @click="isPrivacy = false">不同意</el-button>
                 <el-button type="primary" @click="toLogin(loginType)">同意并继续</el-button>
                 <el-button type="primary" @click="toLogin(loginType)">同意并继续</el-button>
+                <el-button @click="isPrivacy = false">不同意</el-button>
             </span>
             </span>
         </el-dialog>
         </el-dialog>
         <el-dialog title="请绑定学校" width="30%" :visible.sync="isBindSchool" :show-close="bindSchoolType" :close-on-press-escape="bindSchoolType" :close-on-click-modal="bindSchoolType">
         <el-dialog title="请绑定学校" width="30%" :visible.sync="isBindSchool" :show-close="bindSchoolType" :close-on-press-escape="bindSchoolType" :close-on-click-modal="bindSchoolType">

+ 46 - 4
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/view/login/Student.vue

@@ -3,11 +3,11 @@
         <!-- 学生端
         <!-- 学生端
         会获取到学校信息、正在进行的活动,此处会展示活动名称、学校信息、账号密码
         会获取到学校信息、正在进行的活动,此处会展示活动名称、学校信息、账号密码
         若没有活动,则不展示账号密码,提示没有活动 -->
         若没有活动,则不展示账号密码,提示没有活动 -->
-            <div class="activity-school">
-                <h2>艺术评测本地缓存25</h2>
+            <div class="activity-school" v-if="evaluationInfo">
+                <h2>{{ evaluationInfo.name }}</h2>
                 <p>
                 <p>
-                    <img :src="schoolInfo.picture" alt="">
-                    <span>{{ schoolInfo.name }}</span>
+                    <img :src="evaluationInfo.ownerPicture" alt="">
+                    <span>{{ evaluationInfo.ownerName }}</span>
                 </p>
                 </p>
             </div>
             </div>
         <div class="login-body">
         <div class="login-body">
@@ -36,6 +36,7 @@
 export default {
 export default {
     data() {
     data() {
         return {
         return {
+            evaluationInfo: undefined,
             loginForm: {
             loginForm: {
                 id: '',
                 id: '',
                 password: '',
                 password: '',
@@ -47,7 +48,48 @@ export default {
             },
             },
         }
         }
     },
     },
+    created() {
+        this.viewNetworkInfo()
+    },
     methods: {
     methods: {
+        async viewNetworkInfo() {
+            let params = {
+                fp: await this.$tools.getFingerprint()
+            }
+            this.$api.getDevice(params).then(res => {
+                /* res.data.server.host = []
+                res.data.server.uris.forEach(item => {
+                    res.data.server.networks.forEach(network => {
+                        // https://192.168.8.140:5001/login/student
+                        let url = `${item.protocol}://${network.ip}:${item.port}/login/student`
+                        res.data.server.host.push(url)
+                    })
+                })
+                res.data.server.shwoRam = (res.data.server.ram / 1024 / 1024 / 1024).toFixed(1)
+                res.data.server.cpuInfos.forEach(item => {
+                    item.showHZ = item.hz ? (item.hz / 1000) : 0
+                });
+                if(!res.data.server.school) this.isBindSchool = true
+                localStorage.setItem('deviceId', res.data.device)
+                localStorage.setItem('schoolInfo', JSON.stringify(res.data.server.school))
+                this.deviceInfo = res.data
+                this.hybridType = res.data?.hybrid */
+
+                this.stuGetEvaluation()
+            })
+        },
+        stuGetEvaluation() {
+            this.$api.stuGetEvaluation({}).then(res => {
+                if(res.code === 200) {
+                    this.evaluationInfo = res.evaluationClient
+                } else {
+                    this.$message({
+                        message: '当前无评测可以作答',
+                        type: 'warning'
+                    });
+                }
+            })
+        },
         toLogin() {},
         toLogin() {},
     }
     }
 }
 }

+ 9 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/vue.config.js

@@ -58,7 +58,15 @@ module.exports = defineConfig({
                 pathRewrite: {
                 pathRewrite: {
                     '^/api': ''
                     '^/api': ''
                 }
                 }
-            }
+            },
+            '/package': {
+                target: 'https://localhost:6001', //后端接口
+                secure: false,
+                changeOrigin: false,
+                pathRewrite: {
+                    '^/package': '/package'
+                }
+            },
         },
         },
         historyApiFallback: true,
         historyApiFallback: true,
     },
     },