zengyicheng 1 рік тому
батько
коміт
d635da805a
64 змінених файлів з 2513 додано та 68 видалено
  1. 0 0
      dist/index.html
  2. 1 1
      dist/report.html
  3. 0 0
      dist/static/css/app.dbec558a.css
  4. 0 0
      dist/static/css/chunk-087daf50.375177ba.css
  5. 0 0
      dist/static/css/chunk-09d82406.2f1ddd04.css
  6. 0 0
      dist/static/css/chunk-0f529362.9c7aa8ca.css
  7. 0 0
      dist/static/css/chunk-142465c5.3a3850ea.css
  8. 0 0
      dist/static/css/chunk-2722162e.59889eeb.css
  9. 0 1
      dist/static/css/chunk-2bcf596a.a0bfd533.css
  10. 0 0
      dist/static/css/chunk-3f704ab8.33132fd4.css
  11. 1 0
      dist/static/css/chunk-40de5f1e.c94776c5.css
  12. 1 0
      dist/static/css/chunk-5e1a2240.3e62abe1.css
  13. 0 1
      dist/static/css/chunk-7d027967.7f5e6538.css
  14. 0 0
      dist/static/css/chunk-vantUI.6c0b0952.css
  15. 0 0
      dist/static/css/chunk-vantUI.fbabdac4.css
  16. BIN
      dist/static/img/choose.d9e24381.png
  17. BIN
      dist/static/img/download.5a3bba5e.png
  18. BIN
      dist/static/img/isWord.a2e3b0c2.png
  19. BIN
      dist/static/img/word2.3b126dc8.png
  20. 0 0
      dist/static/js/app.154424ea.js
  21. 0 0
      dist/static/js/app.3f839ef3.js
  22. 0 0
      dist/static/js/chunk-01979dd3.30fb23f3.js
  23. 0 0
      dist/static/js/chunk-087daf50.7f338e84.js
  24. 0 0
      dist/static/js/chunk-09d82406.6f5277d5.js
  25. 0 0
      dist/static/js/chunk-0f529362.bd55f860.js
  26. 0 0
      dist/static/js/chunk-142465c5.ef5a3471.js
  27. 0 0
      dist/static/js/chunk-2722162e.d20ff9ee.js
  28. 0 1
      dist/static/js/chunk-2bcf596a.45b618af.js
  29. 0 0
      dist/static/js/chunk-3f704ab8.5e8e4bdb.js
  30. 1 0
      dist/static/js/chunk-40de5f1e.8723779d.js
  31. 0 0
      dist/static/js/chunk-5bd16f48.e0aa2b01.js
  32. 0 0
      dist/static/js/chunk-5e1a2240.f4f0ea17.js
  33. 0 0
      dist/static/js/chunk-6637cd0c.cf58617c.js
  34. 0 1
      dist/static/js/chunk-7d027967.7d60579c.js
  35. 0 0
      dist/static/js/chunk-c8697d60.00324023.js
  36. 0 0
      dist/static/js/chunk-libs.929f1c0b.js
  37. 0 0
      dist/static/js/chunk-vantUI.959a580b.js
  38. 0 0
      dist/static/js/chunk-vantUI.e6f750cb.js
  39. 12 9
      public/index.html
  40. 21 0
      src/api/testDetail.js
  41. 20 0
      src/api/testStudent.js
  42. BIN
      src/assets/images/fileIcon/deleteworks.png
  43. BIN
      src/assets/images/fileIcon/download.png
  44. BIN
      src/assets/images/fileIcon/isVideo.png
  45. BIN
      src/assets/images/fileIcon/isWord.png
  46. BIN
      src/assets/images/fileIcon/word2.png
  47. BIN
      src/assets/images/testStudent/choose.png
  48. BIN
      src/assets/images/testStudent/isMore.png
  49. BIN
      src/assets/images/testStudent/more.png
  50. 61 48
      src/router/router.config.js
  51. 2 2
      src/views/layouts/index.vue
  52. 167 0
      src/views/test/components/courseItem.vue
  53. 180 0
      src/views/test/components/typeFilter.vue
  54. 168 4
      src/views/test/index.vue
  55. 3 0
      src/views/testDetail/common/aws-sdk-2.235.1.min.js
  56. 198 0
      src/views/testDetail/components/choice.vue
  57. 570 0
      src/views/testDetail/components/file.vue
  58. 185 0
      src/views/testDetail/components/gap.vue
  59. 429 0
      src/views/testDetail/components/topic.vue
  60. 86 0
      src/views/testDetail/file/wOffice.vue
  61. 86 0
      src/views/testDetail/file/wPdf.vue
  62. 78 0
      src/views/testDetail/file/wPdf2.vue
  63. 132 0
      src/views/testDetail/file/wVideo.vue
  64. 111 0
      src/views/testDetail/index.vue

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/index.html


Різницю між файлами не показано, бо вона завелика
+ 1 - 1
dist/report.html


+ 0 - 0
dist/static/css/app.745921f5.css → dist/static/css/app.dbec558a.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-087daf50.375177ba.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-09d82406.2f1ddd04.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-0f529362.9c7aa8ca.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-142465c5.3a3850ea.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-2722162e.59889eeb.css


+ 0 - 1
dist/static/css/chunk-2bcf596a.a0bfd533.css

@@ -1 +0,0 @@
-h3[data-v-70f5745f]{margin:1.06667rem 0 0}ul[data-v-70f5745f]{list-style-type:none;padding:0}li[data-v-70f5745f]{display:inline-block;margin:0 .26667rem}a[data-v-70f5745f]{color:#42b983}.app-container[data-v-232f728e]{height:100vh}.app-container .layout-content[data-v-232f728e]{height:100%}.app-container .layout-footer[data-v-232f728e]{display:block;height:1.33333rem}

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-3f704ab8.33132fd4.css


+ 1 - 0
dist/static/css/chunk-40de5f1e.c94776c5.css

@@ -0,0 +1 @@
+.loginBox[data-v-02bea5b8]{position:relative;width:100vw;height:100vh;overflow:hidden}.loginBox>iframe[data-v-02bea5b8]{width:100%;height:100%;border:0}

+ 1 - 0
dist/static/css/chunk-5e1a2240.3e62abe1.css

@@ -0,0 +1 @@
+h3[data-v-543593c5]{margin:1.06667rem 0 0}ul[data-v-543593c5]{list-style-type:none;padding:0}li[data-v-543593c5]{display:inline-block;margin:0 .26667rem}a[data-v-543593c5]{color:#42b983}.app-container[data-v-1ada2324]{height:100vh}.app-container .layout-content[data-v-1ada2324]{height:calc(100% - 1.33333rem)}.app-container .layout-footer[data-v-1ada2324]{display:block;height:1.33333rem}

+ 0 - 1
dist/static/css/chunk-7d027967.7f5e6538.css

@@ -1 +0,0 @@
-.loginBox[data-v-4dc8186e]{position:relative;width:100vw;height:100vh;overflow:hidden}.loginBox>iframe[data-v-4dc8186e]{width:100%;height:100%;border:0}

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-vantUI.6c0b0952.css


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/css/chunk-vantUI.fbabdac4.css


BIN
dist/static/img/choose.d9e24381.png


BIN
dist/static/img/download.5a3bba5e.png


BIN
dist/static/img/isWord.a2e3b0c2.png


BIN
dist/static/img/word2.3b126dc8.png


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/app.154424ea.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/app.3f839ef3.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-01979dd3.30fb23f3.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-087daf50.7f338e84.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-09d82406.6f5277d5.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-0f529362.bd55f860.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-142465c5.ef5a3471.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-2722162e.d20ff9ee.js


+ 0 - 1
dist/static/js/chunk-2bcf596a.45b618af.js

@@ -1 +0,0 @@
-(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2bcf596a"],{"03af":function(e,t,a){},"2dee":function(e,t,a){},"64fe":function(e,t,a){"use strict";var n=a("2dee"),c=a.n(n);c.a},7431:function(e,t,a){"use strict";a.r(t);var n=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"app-container"},[a("div",{staticClass:"layout-content"},[e.$route.meta.keepAlive?a("keep-alive",[a("router-view")],1):a("router-view")],1),e._e()])},c=[],i=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",[a("van-tabbar",{attrs:{fixed:"",route:""},on:{change:e.handleChange},model:{value:e.active,callback:function(t){e.active=t},expression:"active"}},e._l(e.data,(function(t,n){return a("van-tabbar-item",{key:n,attrs:{to:t.to,icon:t.icon}},[e._v(" "+e._s(t.title)+" ")])})),1)],1)},o=[],r=(a("a9e3"),{name:"TabBar",props:{defaultActive:{type:Number,default:0},data:{type:Array,default:function(){return[]}}},data:function(){return{active:this.defaultActive}},methods:{handleChange:function(e){this.$emit("change",e)}}}),u=r,l=(a("cb41"),a("2877")),s=Object(l["a"])(u,i,o,!1,null,"70f5745f",null),f=s.exports,d={name:"AppLayout",data:function(){return{tabbars:[{title:"学习中心",to:{path:"/home"},icon:"home-o"}]}},components:{TabBar:f},methods:{handleChange:function(e){console.log("tab value:",e)}}},v=d,p=(a("64fe"),Object(l["a"])(v,n,c,!1,null,"232f728e",null));t["default"]=p.exports},cb41:function(e,t,a){"use strict";var n=a("03af"),c=a.n(n);c.a}}]);

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-3f704ab8.5e8e4bdb.js


+ 1 - 0
dist/static/js/chunk-40de5f1e.8723779d.js

@@ -0,0 +1 @@
+(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-40de5f1e"],{"08dc":function(e,t,n){},"9ed6":function(e,t,n){"use strict";n.r(t);var r=function(){var e=this;e._self._c;return e._m(0)},i=[function(){var e=this,t=e._self._c;return t("div",{staticClass:"loginBox"},[t("iframe",{attrs:{src:"//edu.cocorobo.cn/course/login?type=2",frameborder:"0"}})])}],c=n("c7eb"),o=n("1da1"),u=n("5530"),s=(n("14d9"),n("2f62")),a={data:function(){return{redirect:void 0,timer:null}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:Object(u["a"])(Object(u["a"])({},Object(s["b"])({login:"user/login"})),{},{handleLogin:function(){this.$router.push({path:this.redirect||"/"})},getLogin:function(){var e=this;return Object(o["a"])(Object(c["a"])().mark((function t(){var n;return Object(c["a"])().wrap((function(t){while(1)switch(t.prev=t.next){case 0:return t.next=2,e.login();case 2:n=t.sent,n&&e.$router.push({path:e.redirect||"/"});case 4:case"end":return t.stop()}}),t)})))()}}),beforeDestroy:function(){clearInterval(this.timer),this.timer=null},mounted:function(){var e=this;this.getLogin(),this.timer=setInterval((function(){e.getLogin()}),2e3)}},d=a,h=(n("d02e"),n("2877")),l=Object(h["a"])(d,r,i,!1,null,"02bea5b8",null);t["default"]=l.exports},d02e:function(e,t,n){"use strict";n("08dc")}}]);

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-5bd16f48.e0aa2b01.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-5e1a2240.f4f0ea17.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-6637cd0c.cf58617c.js


+ 0 - 1
dist/static/js/chunk-7d027967.7d60579c.js

@@ -1 +0,0 @@
-(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-7d027967"],{"6605e":function(e,t,r){},"9ed6":function(e,t,r){"use strict";r.r(t);var n=function(){var e=this,t=e.$createElement;e._self._c;return e._m(0)},i=[function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"loginBox"},[r("iframe",{attrs:{src:"//edu.cocorobo.cn/course/login?type=2",frameborder:"0"}})])}],c=(r("96cf"),r("1da1")),o=r("5530"),u=r("2f62"),a={data:function(){return{redirect:void 0,timer:null}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:Object(o["a"])(Object(o["a"])({},Object(u["b"])({login:"user/login"})),{},{handleLogin:function(){this.$router.push({path:this.redirect||"/"})},getLogin:function(){var e=this;return Object(c["a"])(regeneratorRuntime.mark((function t(){var r;return regeneratorRuntime.wrap((function(t){while(1)switch(t.prev=t.next){case 0:return t.next=2,e.login();case 2:r=t.sent,r&&e.$router.push({path:e.redirect||"/"});case 4:case"end":return t.stop()}}),t)})))()}}),beforeDestroy:function(){clearInterval(this.timer),this.timer=null},mounted:function(){var e=this;this.getLogin(),this.timer=setInterval((function(){e.getLogin()}),2e3)}},s=a,l=(r("b792"),r("2877")),d=Object(l["a"])(s,n,i,!1,null,"4dc8186e",null);t["default"]=d.exports},b792:function(e,t,r){"use strict";var n=r("6605e"),i=r.n(n);i.a}}]);

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-c8697d60.00324023.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-libs.929f1c0b.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-vantUI.959a580b.js


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
dist/static/js/chunk-vantUI.e6f750cb.js


+ 12 - 9
public/index.html

@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <html>
-  <head>
+
+<head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
@@ -10,9 +11,12 @@
       <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
       <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
 	  <% } %> -->
-    <title><%= webpackConfig.name %></title>
-  </head>
-  <body>
+    <title>
+        <%= webpackConfig.name %>
+    </title>
+</head>
+
+<body>
     <noscript>
       <strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
     </noscript>
@@ -23,10 +27,9 @@
       <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
     <% } %> -->
     <!-- built files will be auto injected -->
-  </body>
+</body>
+
 </html>
 <script>
-
-  document.domain = "cocorobo.cn"
-
-</script>
+    // document.domain = "cocorobo.cn"
+</script>

+ 21 - 0
src/api/testDetail.js

@@ -0,0 +1,21 @@
+// axios
+import request from '@/utils/request'
+// 获取问卷详情
+export function getTestCourseDetail(params) {
+    return request({
+        url: '/getTestCourseDetail',
+        method: 'get',
+        params,
+        hideloading: false
+    })
+}
+
+// 上传评测
+export function addTestWorks(data) {
+    return request({
+        url: '/addTestWorks',
+        method: 'post',
+        data,
+        hideloading: false
+    })
+}

+ 20 - 0
src/api/testStudent.js

@@ -0,0 +1,20 @@
+// axios
+import request from '@/utils/request'
+// 获取问卷类型
+export function selectTestType(params) {
+    return request({
+        url: '/selectTestType',
+        method: 'get',
+        params,
+        hideloading: false
+    })
+}
+// 查询问卷列表
+export function selectTesttCourseCenter(params) {
+    return request({
+        url: '/selectTesttCourseCenter',
+        method: 'get',
+        params,
+        hideloading: false
+    })
+}

BIN
src/assets/images/fileIcon/deleteworks.png


BIN
src/assets/images/fileIcon/download.png


BIN
src/assets/images/fileIcon/isVideo.png


BIN
src/assets/images/fileIcon/isWord.png


BIN
src/assets/images/fileIcon/word2.png


BIN
src/assets/images/testStudent/choose.png


BIN
src/assets/images/testStudent/isMore.png


BIN
src/assets/images/testStudent/more.png


+ 61 - 48
src/router/router.config.js

@@ -2,52 +2,65 @@
  * 基础路由
  * @type { *[] }
  */
-export const constantRouterMap = [
-  {
-    path: '/',
-    redirect: '/home'
-  },
-  {
-    path: '/login',
-    component: () => import('@/views/login/index'),
-    meta: {
-      title: '登录',
-      keepAlive: false
+export const constantRouterMap = [{
+        path: '/',
+        redirect: '/home'
+    },
+    {
+        path: '/login',
+        component: () =>
+            import ('@/views/login/index'),
+        meta: {
+            title: '登录',
+            keepAlive: false
+        }
+    },
+    {
+        path: '/course',
+        component: () =>
+            import ('@/views/course/index'),
+        meta: {
+            title: '课程',
+            keepAlive: false
+        }
+    },
+    {
+        path: '/',
+        component: () =>
+            import ('@/views/layouts'),
+        meta: { title: '课程中心', keepAlive: false },
+        children: [{
+                path: 'home',
+                name: 'home',
+                component: () =>
+                    import ('@/views/home/index'),
+                meta: { title: '课程中心', keepAlive: false }
+            },
+            {
+                path: 'test',
+                name: 'Test',
+                component: () =>
+                    import ('@/views/test/index'),
+                meta: { title: '评测中心', keepAlive: false }
+            }
+        ]
+    },
+    {
+        path: '/courseDetail',
+        component: () =>
+            import ('@/views/courseDetail/index'),
+        meta: {
+            title: '课程详情',
+            keepAlive: false
+        }
+    },
+    {
+        path: '/testDetail',
+        component: () =>
+            import ('@/views/testDetail/index'),
+        meta: {
+            title: '填写问卷',
+            keepAlive: false
+        }
     }
-  },
-  {
-    path: '/course',
-    component: () => import('@/views/course/index'),
-    meta: {
-      title: '课程',
-      keepAlive: false
-    }
-  },
-  {
-    path: '/',
-    component: () => import('@/views/layouts'),
-    meta: { title: '课程中心', keepAlive: false },
-    children: [
-      {
-        path: 'home',
-        name: 'home',
-        component: () => import('@/views/home/index'),
-        meta: { title: '课程中心', keepAlive: false }
-      },
-      {
-        path: 'test',
-        name: 'Test',
-        component: () => import('@/views/test/index'),
-        meta: { title: '评测中心', keepAlive: false }
-      }
-    ]
-  },
-  {
-    path: '/courseDetail',
-    component: () => import('@/views/courseDetail/index'),
-    meta: {
-      title: '课程详情',
-      keepAlive: false
-    }
-  }
-]
+]

+ 2 - 2
src/views/layouts/index.vue

@@ -52,8 +52,8 @@ export default {
 .app-container {
   height: 100vh;
   .layout-content {
-    // height: calc(100% - 50px);
-    height: calc(100%);
+    height: calc(100% - 50px);
+    // height: calc(100%);
   }
   .layout-footer {
     display: block;

+ 167 - 0
src/views/test/components/courseItem.vue

@@ -0,0 +1,167 @@
+<template>
+  <div @click="goTo(c.courseId)">
+    <div class="cDetail">
+      <div class="cLeft">
+        <div class="ctitle">{{ c.title }}</div>
+        <div class="cBottom">
+          <div class="test_btn">
+            <div class="test_o_btn"></div>
+            <div
+              class="test_type"
+              :class="{
+                is: (c.type == 1 && c.testCount > 1) || (c.type != 1 && c.testCount > 0),
+                no: c.testCount == 0,
+                isR: c.type == 3,
+                isS: c.type == 1 && c.testCount == 1
+              }"
+            >
+              <span>{{
+                c.testCount > 0
+                  ? c.type == 3
+                    ? '已批改'
+                    : c.type == 1 && c.testCount == 1
+                    ? '已保存'
+                    : '已完成'
+                  : '未完成'
+              }}</span>
+            </div>
+          </div>
+          <div class="cTime">创建时间:{{ c.time.split(' ')[0] }}</div>
+        </div>
+      </div>
+      <div class="cRight">
+        <div class="ctmNumber">{{ getNum(c.chapters) }}</div>
+        <div class="ctmNum">题目数量</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    c: {
+      type: Object
+    }
+  },
+  computed: {
+    getNum() {
+      return function (array) {
+        let _array = JSON.parse(array)
+        let num = 0
+        _array.forEach(el => {
+          if ((el.ttype == 3 || el.ttype == 2) && el.array.length > 0) {
+            el.array.forEach(item => {
+              if (item.ttype == 2 && item.array.length > 0) {
+                item.array.forEach(item2 => {
+                  if (item2.ttype == 1 && item2.json) {
+                    num++
+                  }
+                })
+              } else if (item.ttype == 1 && item.json) {
+                num++
+              }
+            })
+          } else if (el.ttype == 1 && el.json) {
+            num++
+          }
+        })
+        return num
+      }
+    }
+  },
+  methods: {
+    goTo(cid) {
+      // eslint-disable-next-line prettier/prettier
+      this.$router.push({ path: '/testDetail', query: { courseid: cid } })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.cDetail {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  .cLeft {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: nowrap;
+    justify-content: center;
+    align-items: flex-start;
+    width: 75%;
+    height: 100%;
+    border-right: 1px solid #e6e6e6;
+    .ctitle {
+      font-size: 20px;
+      font-weight: 500;
+      padding: 10px 0 20px 0;
+    }
+    .cBottom {
+      display: flex;
+      flex-direction: row;
+      flex-wrap: nowrap;
+      align-items: center;
+      .test_btn {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .test_o_btn {
+          display: flex;
+          align-items: center;
+        }
+        .test_type {
+          font-size: 12px;
+          border-radius: 5px;
+          border: 1.5px solid;
+          padding: 3px 8px;
+        }
+        .is {
+          color: rgb(57, 204, 127);
+          background-color: rgba(57, 204, 127, 0.1);
+        }
+        .no {
+          color: rgb(240, 66, 66);
+          background-color: rgba(240, 66, 66, 0.1);
+        }
+        .isR {
+          color: rgb(0, 98, 255);
+          background-color: rgba(0, 98, 255, 0.1);
+        }
+        .isS {
+          color: rgb(255, 173, 31);
+          background-color: rgba(255, 173, 31, 0.1);
+        }
+      }
+      .cTime {
+        color: #999;
+        font-size: 14px;
+        margin-left: 10px;
+      }
+    }
+  }
+  .cRight {
+    width: 25%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    flex-wrap: nowrap;
+    justify-content: center;
+    align-items: center;
+    .ctmNumber {
+      font-size: 20px;
+      color: #3681fc;
+      font-weight: bold;
+      padding: 0 0 10px 0;
+    }
+    .ctmNum {
+      font-size: 16px;
+      color: #999;
+    }
+  }
+}
+</style>

+ 180 - 0
src/views/test/components/typeFilter.vue

@@ -0,0 +1,180 @@
+<template>
+  <div class="classBox">
+    <div class="typeBox">
+      <div :class="type != 2 ? 'type' : 'istype'" @click="setType(2)">
+        <div>完成情况</div>
+        <div :class="type != 2 ? 'more' : 'isMore'"></div>
+      </div>
+      <div :class="type != 1 ? 'type' : 'istype'" @click="setType(1)" v-if="typeArray.length">
+        <div>问卷类型</div>
+        <div :class="type != 1 ? 'more' : 'isMore'"></div>
+      </div>
+    </div>
+    <div class="typeAllBox" v-if="type == 1">
+      <div class="allStatusType">
+        <div @click="chooseTypeArray(item.id)" class="status" v-for="(item, index) in typeArray" :key="index">
+          <div :style="{ color: typeCheck == item.id ? '#3681fc' : '#000' }">{{ item.name }}</div>
+          <div v-if="typeCheck == item.id" class="chooseIcon">
+            <img src="../../../assets/images/testStudent/choose.png" alt="" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="typeAllBox" v-if="type == 2">
+      <div class="allStatusType">
+        <div @click="chooseGroupA(item.id)" class="status" v-for="(item, index) in groupAType" :key="index">
+          <div :style="{ color: groupA == item.id ? '#3681fc' : '#000' }">{{ item.name }}</div>
+          <div v-if="groupA == item.id" class="chooseIcon">
+            <img src="../../../assets/images/testStudent/choose.png" alt="" />
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { selectTestType } from '@/api/testStudent'
+import { mapGetters } from 'vuex'
+export default {
+  data() {
+    return {
+      type: 0,
+      groupA: 0,
+      groupAType: [
+        { id: 0, name: '全部' },
+        { id: 1, name: '已完成' },
+        { id: 2, name: '未完成' }
+      ],
+      typeArray: [],
+      typeCheck: ''
+    }
+  },
+  computed: {
+    ...mapGetters(['userinfo'])
+  },
+  methods: {
+    setType(t) {
+      if ((t == 1 && this.type == 1) || (t == 2 && this.type == 2)) {
+        this.type = 0
+      } else {
+        this.type = t
+      }
+      this.getAll()
+      this.$forceUpdate()
+    },
+    chooseGroupA(id) {
+      this.groupA = id
+      this.getAll()
+      this.$forceUpdate()
+    },
+    chooseTypeArray(id) {
+      this.typeCheck = id
+      this.getAll()
+      this.$forceUpdate()
+    },
+    selectTestType() {
+      const params = {
+        oid: this.userinfo.organizeid && this.userinfo.organizeid !== '' ? this.userinfo.organizeid : ''
+      }
+      selectTestType(params)
+        .then(res => {
+          this.typeArray = res[0]
+        })
+        .catch(err => {
+          console.log(err)
+        })
+    },
+    getAll() {
+      this.$emit('getAll', this.groupA, this.typeCheck)
+    }
+  },
+  mounted() {
+    this.selectTestType()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.classBox {
+  height: 40px;
+  box-sizing: border-box;
+  position: relative;
+  background: #fff;
+  .typeBox {
+    display: flex;
+    position: relative;
+    flex-wrap: nowrap;
+    flex-direction: row;
+    align-items: center;
+    width: 100%;
+    z-index: 99;
+    height: 40px;
+    .type,
+    .istype {
+      width: calc(100% / 2);
+      display: flex;
+      flex-direction: row;
+      flex-wrap: nowrap;
+      align-items: center;
+      justify-content: center;
+      font-size: 16px;
+      cursor: pointer;
+      .more,
+      .isMore {
+        margin-left: 5px;
+        min-width: 10px;
+        width: 10px;
+        height: 10px;
+        background-image: url(../../../assets/images/testStudent/more.png);
+        background-size: 100% 100%;
+        > img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+      .isMore {
+        transform: rotate(180deg);
+        background-image: url(../../../assets/images/testStudent/isMore.png);
+        background-size: 100% 100%;
+      }
+    }
+    .istype {
+      color: #4f91fc;
+    }
+  }
+  .typeAllBox {
+    position: relative;
+    top: 0;
+    left: 0;
+    z-index: 9;
+    .allStatusType {
+      background: #fff;
+      .status {
+        position: relative;
+        width: 100%;
+        height: 30px;
+        display: flex;
+        flex-direction: row;
+        flex-wrap: nowrap;
+        align-items: center;
+        justify-content: space-between;
+        padding: 10px;
+        font-size: 16px;
+        .chooseIcon {
+          position: absolute;
+          right: 50px;
+          top: 18px;
+          width: 15px;
+          min-width: 15px;
+          height: 15px;
+          > img {
+            width: 100%;
+            height: 100%;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 168 - 4
src/views/test/index.vue

@@ -1,16 +1,180 @@
 <template>
-  <div>
-    评测
+  <div class="home-container">
+    <head-bar :isBack="false">
+      <template #title>
+        <div style="width: 90%">
+          <div class="rightSearch">
+            <div class="searchImg" @click="search">
+              <img src="../../assets/images/home/search.png" alt="" />
+            </div>
+            <el-input
+              v-model="courseName"
+              auto-complete="off"
+              placeholder="请输入项目名称..."
+              @input="search"
+            ></el-input>
+          </div>
+        </div>
+      </template>
+    </head-bar>
+    <div class="home-box">
+      <type-filter @getAll="search" :zoneClass.sync="zoneClass"> </type-filter>
+      <van-pull-refresh v-model="isLoading" @refresh="onRefresh" class="rheight">
+        <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" class="vheight">
+          <van-empty description="暂无问卷" v-if="!zoneClass.length" />
+          <course-item v-for="(c, cIndex) in zoneClass" :key="cIndex" class="courseItem" :c="c"></course-item>
+        </van-list>
+      </van-pull-refresh>
+    </div>
   </div>
 </template>
 
 <script>
+import { selectTesttCourseCenter } from '@/api/testStudent'
+import headBar from '@/components/headBar.vue'
+import { mapGetters } from 'vuex'
+import typeFilter from './components/typeFilter.vue'
+import courseItem from './components/courseItem.vue'
 export default {
   name: 'Test',
+  components: {
+    headBar,
+    typeFilter,
+    courseItem,
+  },
   data() {
-    return {}
+    return {
+      courseName: '',
+      zoneClass: [],
+      page: 1,
+      groupA: 0,
+      typeCheck: '',
+      isLoading: false,
+      loading: false,
+      finished: true
+    }
+  },
+  computed: {
+    ...mapGetters(['userinfo'])
+  },
+  methods: {
+    search(g,t) {
+      this.page = 1;
+      this.groupA = g;
+      this.typeCheck = t;
+      this.getCourse(true);
+    },
+    onRefresh() {
+      this.page = 1
+      this.finished = false
+      this.getCourse(true)
+    },
+    onLoad() {
+      this.page++
+      this.getCourse()
+    },
+    async getCourse(isRefresh){
+      var typeE = [];
+      const params = {
+        type: this.groupA,
+        uid: this.userinfo.userid,
+        oid: this.userinfo.organizeid,
+        org: this.userinfo.org,
+        typea: "",
+        typeb: "",
+        typec: "",
+        typed: "",
+        typef: this.typeCheck,
+        typeE: typeE.join(","),
+        cu: "",
+        cn: this.courseName,
+        page: this.page,
+        pageSize: 10,
+      }
+      const res = await selectTesttCourseCenter(params)
+      if (isRefresh) {
+        this.isLoading = false
+        // 下拉刷新
+        this.zoneClass = res[0]
+      } else {
+        this.loading = false
+        // 上拉加载
+        this.zoneClass = [...this.zoneClass, ...res[0]]
+      }
+      this.$emit('update:list', this.zoneClass)
+      if (res[0].length === 0) {
+        // 证明没有下一页数据了
+        this.finished = true
+      } else {
+        this.finished = false
+      }
+    },
+    
+  },
+  mounted() {
+    this.getCourse();
   }
 }
 </script>
 
-<style scoped></style>
+<style lang="scss" scoped>
+.home-container {
+  height: 100%;
+  overflow: hidden;
+  .home-box {
+    height: calc(100% - 55px);
+    margin-top: 55px;
+    overflow: hidden;
+    background: #f0f2f5;
+  }
+  .rheight {
+    height: calc(100% - 40px);
+    overflow: auto;
+    background: #e8ebf2;
+
+    /deep/.van-pull-refresh__track {
+      height: 100%;
+    }
+  }
+  .vheight {
+    padding: 10px 0;
+    // height: 100%;
+    // overflow: auto;
+  }
+  .courseItem {
+    background: #fff;
+    width: 90%;
+    margin: 0 auto 15px;
+    border-radius: 10px;
+    box-shadow: 0 0 10px 1px #dcdfe6;
+    padding: 10px;
+  }
+}
+
+/deep/.rightSearch {
+  display: flex;
+  flex-direction: row;
+  width: 100%;
+  height: 30px;
+  position: relative;
+
+  .searchImg {
+    width: 20px;
+    height: 20px;
+    position: absolute;
+    z-index: 9;
+    top: 5px;
+    left: 5px;
+    > img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  .el-input__inner {
+    width: 100%;
+    height: 30px;
+    text-indent: 15px;
+    line-height: 30px;
+  }
+}
+</style>

Різницю між файлами не показано, бо вона завелика
+ 3 - 0
src/views/testDetail/common/aws-sdk-2.235.1.min.js


+ 198 - 0
src/views/testDetail/components/choice.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="c_box">
+    <!-- <div class="mask"></div> -->
+    <div v-if="!checkJson">暂未设置题目</div>
+    <div v-else class="choice_box">
+      <div class="c_title">
+        <div class="title">
+          {{ tindex + 1 + '、' + `(${option[checkJson.type].name})` + checkJson.title
+          }}<span style="color: #efa030" v-if="checkJson.score">({{ '分值:' + checkJson.score + '分' }})</span>
+        </div>
+        <div class="p_box" v-if="isTeacher == 1 && checkJson.score">
+          <el-input v-model="score2" class="c_input" disabled></el-input><span style="margin: 0 10px">/</span
+          ><span>{{ checkJson.score }}分</span>
+        </div>
+        <div class="p_box" v-if="isTeacher == 2 && checkJson.score2">
+          <span>{{ checkJson.score2 }}分</span><span style="margin: 0 10px">/</span><span>{{ checkJson.score }}分</span>
+        </div>
+      </div>
+      <div class="choices">
+        <div class="choice" v-for="(item, index) in checkJson.array" :key="index">
+          <div class="choice_c" v-if="checkJson.type == 2">
+            <el-checkbox v-model="checkJson.answer2" :label="index" :disabled="checktype == 2"></el-checkbox
+            ><span :class="{ right: see && cJson.answer.indexOf(index) != -1 }" @click="check(checkJson.type, index)">{{
+              item.option
+            }}</span>
+          </div>
+          <div class="choice_c" v-if="checkJson.type == 1">
+            <el-radio v-model="checkJson.answer2[0]" :label="index" :disabled="checktype == 2"></el-radio
+            ><span :class="{ right: see && cJson.answer.indexOf(index) != -1 }" @click="check(checkJson.type, index)">{{
+              item.option
+            }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    tindex: {
+      type: Number
+    },
+    cJson: {
+      type: Object
+    },
+    checktype: {
+      type: Number,
+      default: 1
+    },
+    see: {
+      type: Boolean,
+      default: false
+    },
+    isTeacher: {
+      type: Number,
+      default: 2
+    }
+  },
+  data() {
+    return {
+      option: {
+        1: { name: '单选题' },
+        2: { name: '多选题' }
+      },
+      checkJson: undefined
+    }
+  },
+  computed: {
+    score2() {
+      let answer2 = this.checkJson.answer2 ? this.checkJson.answer2.sort().join(',') : ''
+      let answer = this.checkJson.answer ? this.checkJson.answer.sort().join(',') : ''
+      this.checkJson.score2 = answer2 == answer ? this.checkJson.score : 0
+      this.$forceUpdate()
+      return answer2 == answer ? this.checkJson.score : 0
+    }
+  },
+  watch: {
+    checkJson: {
+      handler(newValue) {
+        this.$emit('update:cJson', newValue)
+      },
+      deep: true
+    }
+  },
+  methods: {
+    depthCopy(s) {
+      return JSON.parse(JSON.stringify(s))
+    },
+    check(type, index) {
+      if (this.checktype == 2) {
+        return
+      }
+      if (type == 2) {
+        if (this.checkJson.answer2.indexOf(index) == -1) {
+          this.checkJson.answer2.push(index)
+        } else {
+          this.checkJson.answer2.splice(this.checkJson.answer2.indexOf(index), 1)
+        }
+      } else if (type == 1) {
+        this.checkJson.answer2[0] = index
+      }
+      this.$forceUpdate()
+    }
+  },
+  mounted() {
+    this.checkJson = this.cJson ? this.depthCopy(this.cJson) : undefined
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.c_box {
+  width: 100%;
+  position: relative;
+  .choice_box {
+    .c_title {
+      display: flex;
+      justify-content: space-between;
+      .title {
+        font-weight: bold;
+        width: 100%;
+        word-break: break-all;
+        font-size: 16px;
+      }
+      .p_box {
+        margin-left: 5px;
+        min-width: fit-content;
+        display: flex;
+        align-items: center;
+        .c_input {
+          width: 90px;
+        }
+        /deep/.c_input .el-input__inner {
+          padding: 0 5px;
+          text-align: right;
+        }
+      }
+    }
+
+    .choices {
+      margin-top: 10px;
+      padding: 0 10px;
+    }
+    .choice {
+      word-break: break-all;
+      .choice_c {
+        display: flex;
+        flex-direction: row;
+        align-items: baseline;
+        span {
+          /* margin-left: 10px; */
+          cursor: pointer;
+          font-size: 14px;
+        }
+        .el-checkbox {
+          margin-top: 4px;
+          margin-right: 10px;
+        }
+        .el-radio {
+          margin-top: 4px;
+          margin-right: 10px;
+        }
+        .right {
+          color: #efa030;
+        }
+        .right::after {
+          content: '(正确答案)';
+        }
+      }
+      /deep/.choice_c .el-checkbox__label {
+        display: none;
+      }
+      /deep/.choice_c .el-radio__label {
+        display: none;
+      }
+    }
+    /deep/.choice .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after {
+      border-color: #fff !important;
+    }
+    /deep/.choice .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner {
+      background-color: #409eff !important;
+      border-color: #409eff !important;
+    }
+    /deep/.choice .el-radio__input.is-disabled.is-checked .el-radio__inner {
+      background-color: #409eff !important;
+      border-color: #409eff !important;
+    }
+    /deep/.choice .el-radio__input.is-disabled.is-checked .el-radio__inner::after {
+      background-color: #fff !important;
+    }
+    .choice + .choice {
+      margin-top: 5px;
+    }
+  }
+}
+</style>

+ 570 - 0
src/views/testDetail/components/file.vue

@@ -0,0 +1,570 @@
+<template>
+  <div class="c_box">
+    <!-- <div class="mask"></div> -->
+    <div v-if="!checkJson">暂未设置题目</div>
+    <div v-else class="choice_box">
+      <!-- <div class="title"><div>{{ `(${option[checkJson.type].name})` }}</div><div v-html="checkJson.title"></div></div> -->
+      <div class="c_title">
+        <div class="title">
+          {{ tindex + 1 + '、' + `(${option[checkJson.type].name})` + checkJson.title
+          }}<span style="color: #efa030" v-if="checkJson.score">({{ '分值:' + checkJson.score + '分' }})</span>
+        </div>
+
+        <!-- </div><div v-html="checkJson.title"></div> -->
+        <div class="p_box" v-if="isTeacher == 1 && checkJson.score">
+          <el-input v-model="checkJson.score2" class="c_input" @change="numberPan" placeholder="请输入得分"></el-input
+          ><span style="margin: 0 10px">/</span><span>{{ checkJson.score }}分</span>
+        </div>
+        <div class="p_box" v-if="isTeacher == 2 && checkJson.score2">
+          <span>{{ checkJson.score2 }}分</span><span style="margin: 0 10px">/</span><span>{{ checkJson.score }}分</span>
+        </div>
+      </div>
+      <div class="choices">
+        <div class="binfo_input">
+          <div class="fileBox" v-if="checkJson.file && checkJson.file.length">
+            <div class="fileC">
+              <div
+                class="file"
+                v-for="(item, index) in checkJson.file"
+                :key="index"
+                v-loading="downLoading == item.url"
+                @click.stop="checkFile(item)"
+              >
+                <img
+                  class="del"
+                  src="../../../assets/images/fileIcon/deleteworks.png"
+                  @click.stop="delFile(index)"
+                  v-if="checktype == 1"
+                />
+                <img
+                  class="download"
+                  src="../../../assets/images/fileIcon/download.png"
+                  @click.stop="downloadFile(item)"
+                  :style="{ right: checktype != 1 ? '10px' : '45px' }"
+                />
+                <img class="img" :src="wordIcon" alt="" v-if="item.type == 1" />
+                <img class="img" :src="videoIcon" alt="" v-if="item.type == 2" />
+                <img class="img" :src="item.url" alt="" v-if="item.type == 3" />
+                <img class="img" :src="wordIcon" alt="" v-if="item.type == 4" />
+                <img class="img" :src="fileIcon" alt="" v-if="item.type == 5" />
+                <div class="name">
+                  <el-tooltip :content="item.name" placement="top" effect="dark">
+                    <span>{{ item.name }}</span>
+                  </el-tooltip>
+                </div>
+              </div>
+            </div>
+            <div class="btn" @click.stop="addImg($event)" v-if="checktype == 1">
+              <span>点击添加本地文件</span>
+              <input type="file" accept="*" style="display: none" @change="beforeUpload($event)" />
+            </div>
+          </div>
+          <div class="uploadQ" @click.stop="addImg($event)" v-else-if="checktype == 1">
+            <span>点击添加本地文件</span>
+            <input type="file" accept="*" style="display: none" @change="beforeUpload($event)" />
+          </div>
+          <div class="uploadQ" v-else>
+            <span>暂无添加附件</span>
+          </div>
+          <div v-if="proVisible" class="mask">
+            <div class="progressBox">
+              <div class="lbox"><img src="../../../assets/images/loading.gif" />上传中,请稍后</div>
+              <div style="margin-bottom: 10px">
+                <span>{{ isFinishSize }}M</span>
+                /
+                <span>{{ isAllSize }}M</span>
+              </div>
+              <el-progress
+                :text-inside="true"
+                :stroke-width="20"
+                :percentage="progress ? progress : 0"
+                style="width: 80%"
+              ></el-progress>
+            </div>
+          </div>
+        </div>
+        <!-- <textarea  :readonly="checktype == 2" rows="2" v-autoHeight="68" class="binfo_input binfo_textarea" cols v-model="checkJson.answer2"
+                    placeholder=""></textarea> -->
+      </div>
+    </div>
+    <wpdf :dialogVisiblePdf.sync="dialogVisiblePdf" :url="wurl"></wpdf>
+    <wVideo :dialogVisibleVideo.sync="dialogVisibleVideo" :url="wurl"></wVideo>
+    <wOffice :dialogVisibleOffice.sync="dialogVisibleOffice" :url="wurl"></wOffice>
+  </div>
+</template>
+
+<script>
+import '../common/aws-sdk-2.235.1.min.js'
+
+import videoIcon from '../../../assets/images/fileIcon/isVideo.png'
+import wordIcon from '../../../assets/images/fileIcon/isWord.png'
+import fileIcon from '../../../assets/images/fileIcon/word2.png'
+import wpdf from '../file/wPdf2.vue'
+import wVideo from '../file/wVideo.vue'
+import wOffice from '../file/wOffice.vue'
+export default {
+  components: {
+    wpdf,
+    wVideo,
+    wOffice
+  },
+  props: {
+    tindex: {
+      type: Number
+    },
+    cJson: {
+      type: Object
+    },
+    checktype: {
+      type: Number,
+      default: 1
+    },
+    see: {
+      type: Boolean,
+      default: false
+    },
+    isTeacher: {
+      type: Number,
+      default: 2
+    }
+  },
+  data() {
+    return {
+      option: {
+        1: { name: '附件' }
+      },
+      checkJson: undefined,
+      progress: 0,
+      isFinishSize: 0,
+      proVisible: false,
+      isAllSize: 0,
+      videoIcon: videoIcon,
+      wordIcon: wordIcon,
+      fileIcon: fileIcon,
+      downLoading: '',
+      dialogVisiblePdf: false,
+      dialogVisibleVideo: false,
+      dialogVisibleOffice: false,
+      wurl: ''
+    }
+  },
+  watch: {
+    checkJson: {
+      handler(newValue) {
+        this.$emit('update:cJson', newValue)
+      },
+      deep: true
+    }
+  },
+  methods: {
+    depthCopy(s) {
+      return JSON.parse(JSON.stringify(s))
+    },
+    addImg(e) {
+      var el = e.currentTarget
+      el.getElementsByTagName('input')[0].click()
+      e.target.value = ''
+    },
+    numberPan() {
+      if (/[^\d]/.test(this.checkJson.score2) || this.checkJson.score2 < 0) {
+        this.$message.error('请输入大于0的数字')
+        this.checkJson.score2 = ''
+      }
+      if (parseInt(this.checkJson.score2) > parseInt(this.checkJson.score)) {
+        this.$message.error('不能输入大于得分的数字')
+        this.checkJson.score2 = this.checkJson.score
+      }
+    },
+    beforeUpload(event, type) {
+      // const loading = this.openLoading();
+      var file = event.target.files[0]
+      var credentials = {
+        accessKeyId: 'AKIATLPEDU37QV5CHLMH',
+        secretAccessKey: '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'
+      } //秘钥形式的登录上传
+      window.AWS.config.update(credentials)
+      window.AWS.config.region = 'cn-northwest-1' //设置区域
+
+      var bucket = new window.AWS.S3({ params: { Bucket: 'ccrb' } }) //选择桶
+      var _this = this
+
+      _this.progress = 0
+      _this.proVisible = true
+      _this.isFinishSize = 0
+      _this.isAllSize = (file.size / 1024 / 1024).toFixed(2)
+      _this.$forceUpdate()
+      if (file) {
+        var params = {
+          Key:
+            file.name.split('.')[0] +
+            new Date().getTime() +
+            '.' +
+            file.name.split('.')[file.name.split('.').length - 1],
+          ContentType: file.type,
+          Body: file,
+          'Access-Control-Allow-Credentials': '*',
+          ACL: 'public-read'
+        } //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
+        var options = {
+          partSize: 2048 * 1024 * 1024,
+          queueSize: 2,
+          leavePartsOnError: true
+        }
+        bucket
+          .upload(params, options)
+          .on('httpUploadProgress', function (evt) {
+            //这里可以写进度条
+            // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+            _this.progress = parseInt((evt.loaded / evt.total) * 100)
+            _this.isFinishSize = (evt.loaded / 1024 / 1024).toFixed(2)
+            _this.$forceUpdate()
+          })
+          .send(function (err, data) {
+            _this.progress = 100
+            _this.isFinishSize = _this.isAllSize
+            _this.$forceUpdate()
+            setTimeout(() => {
+              _this.proVisible = false
+              _this.$forceUpdate()
+            }, 1000)
+            // loading.close();
+            if (err) {
+              _this.$message.error('上传失败')
+            } else {
+              let _type = 2
+              var imgA = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp', 'psd', 'svg', 'tiff']
+              var fileA = [
+                'DOC',
+                'DOCX',
+                'DOCM',
+                'DOTM',
+                'DOTX',
+                'PPTX',
+                'PPSX',
+                'PPT',
+                'PPS',
+                'PPTM',
+                'POTM',
+                'PPAM',
+                'POTX',
+                'PPSM',
+                'XLSX',
+                'XLS'
+              ]
+              var videoA = [
+                'AVI',
+                'NAVI',
+                'MPEG',
+                'ASF',
+                'MOV',
+                'WMV',
+                '3GP',
+                'RM',
+                'RMVB',
+                'FLV',
+                'F4V',
+                'H.264',
+                'H.265',
+                'REAL VIDEO',
+                'MKV',
+                'WebM',
+                'HDDVD',
+                'MP4',
+                'MPG',
+                'M4V',
+                'MGV',
+                'OGV',
+                'QTM',
+                'STR',
+                'AMC',
+                'DVX',
+                'EVO',
+                'DAT',
+                'OGG',
+                'OGM'
+              ]
+              if (
+                fileA.indexOf(data.Location.split('.')[data.Location.split('.').length - 1].toLocaleUpperCase()) != -1
+              ) {
+                _type = 1 //word 文件
+              } else if (
+                videoA.indexOf(data.Location.split('.')[data.Location.split('.').length - 1].toLocaleUpperCase()) != -1
+              ) {
+                _type = 2 //视频
+              } else if (
+                imgA.indexOf(data.Location.split('.')[data.Location.split('.').length - 1].toLocaleLowerCase()) != -1
+              ) {
+                _type = 3 //图片
+              } else if (
+                'pdf'.indexOf(data.Location.split('.')[data.Location.split('.').length - 1].toLocaleLowerCase()) != -1
+              ) {
+                _type = 4 //pdf
+              } else {
+                _type = 5 //文件
+              }
+              if (_this.checkJson.file) {
+                _this.checkJson.file.push({
+                  name: file.name,
+                  url: data.Location,
+                  type: _type
+                })
+              } else {
+                _this.checkJson.file = []
+                _this.checkJson.file.push({
+                  name: file.name,
+                  url: data.Location,
+                  type: _type
+                })
+              }
+              _this.$forceUpdate()
+
+              console.log(_this.checkJson)
+              console.log(data.Location)
+            }
+          })
+      }
+    },
+    downloadFile(f) {
+      var credentials = {
+        accessKeyId: 'AKIATLPEDU37QV5CHLMH',
+        secretAccessKey: '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'
+      } //秘钥形式的登录上传
+      window.AWS.config.update(credentials)
+      window.AWS.config.region = 'cn-northwest-1' //设置区域
+      let url2 = f.url
+      let _url2 = ''
+      if (url2.indexOf('https://view.officeapps.live.com/op/view.aspx?src=') != -1) {
+        _url2 = url2.split('https://view.officeapps.live.com/op/view.aspx?src=')[1]
+      } else {
+        _url2 = url2
+      }
+      let _this = this
+
+      _this.downLoading = _url2
+      var s3 = new window.AWS.S3({ params: { Bucket: 'ccrb' } })
+      let name = decodeURIComponent(_url2.split('https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/')[1])
+      var params = {
+        Bucket: 'ccrb',
+        Key: name
+      }
+      s3.getObject(params, function (err, data) {
+        _this.downLoading = ''
+        if (err) console.log(err, err.stack) // an error occurred
+        else {
+          let url = window.URL.createObjectURL(new Blob([data.Body]))
+          let a = document.createElement('a')
+          a.name = f.name
+          a.href = url
+          a.download = f.name
+          a.click()
+          console.log(data)
+        } // sxuccessful response
+      })
+    },
+    checkFile(item) {
+      if (item.type == 3) {
+        this.$hevueImgPreview(item.url)
+      } else if (item.type == 5) {
+        this.downloadFile(item)
+      } else if (item.type == 1) {
+        this.dialogVisibleOffice = true
+        this.wurl = item.url
+      } else if (item.type == 2) {
+        this.dialogVisibleVideo = true
+        this.wurl = item.url
+      } else if (item.type == 4) {
+        this.dialogVisiblePdf = true
+        this.wurl = item.url
+      }
+    },
+    delFile(index) {
+      this.checkJson.file.splice(index, 1);
+      this.$forceUpdate();
+    }
+  },
+  mounted() {
+    this.checkJson = this.cJson ? this.depthCopy(this.cJson) : undefined
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.c_box {
+  width: 100%;
+  position: relative;
+  .mask {
+    position: absolute;
+    height: 100%;
+    width: 100%;
+    z-index: 2;
+  }
+  .choice_box {
+    .c_title {
+      display: flex;
+      justify-content: space-between;
+      .title {
+        font-weight: bold;
+        width: 100%;
+        word-break: break-all;
+        font-size: 16px;
+      }
+      .p_box {
+        margin-left: 5px;
+        min-width: fit-content;
+        display: flex;
+        align-items: center;
+        .c_input {
+          width: 90px;
+        }
+        /deep/.c_input .el-input__inner {
+          padding: 0 5px;
+          text-align: right;
+        }
+      }
+    }
+
+    .choices {
+      margin-top: 10px;
+      .binfo_input {
+        width: 100%;
+        margin: 0;
+        padding: 10px;
+        display: block;
+        min-width: 0;
+        outline: none;
+        box-sizing: border-box;
+        background: none;
+        border: none;
+        border-radius: 5px;
+        background: #fff;
+        font-size: 16px;
+        resize: none;
+        font-family: 'Microsoft YaHei';
+        min-height: 120px;
+        /* border: 1px solid #3682fc00; */
+        border: 1.5px solid #e0e0e0;
+        position: relative;
+        .uploadQ {
+          border: 1.5px dashed #dfdfdf;
+          height: 120px;
+          width: 100%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background: rgb(249, 250, 251);
+          color: rgb(124, 124, 124);
+          border-radius: 5px;
+          cursor: pointer;
+        }
+        .mask {
+          //   background-color: rgb(0 0 0 / 30%);
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 100%;
+          height: 100%;
+          z-index: 90;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          .progressBox {
+            width: 300px;
+            height: 150px;
+            background: #fff;
+            border-radius: 10px;
+            box-shadow: 0 0 6px 1px #bfbfbf;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            flex-direction: column;
+            position: relative;
+            color: #6c6c6c;
+            .lbox {
+              height: 50px;
+              font-size: 19px;
+              display: flex;
+              align-items: center;
+              color: #747474;
+              img {
+                width: 40px;
+                margin-right: 20px;
+              }
+            }
+          }
+          /deep/.progressBox .el-progress-bar__outer {
+            background-color: #d1dfff !important;
+          }
+        }
+        .fileBox {
+          .fileC {
+            display: flex;
+            flex-wrap: wrap;
+            width: 100%;
+            cursor: pointer;
+            .file {
+              width: 200px;
+              height: 140px;
+              margin: 10px 10px 10px 0px;
+              border-radius: 15px;
+              box-shadow: rgb(223, 218, 218) 0px 0px 6px 1px;
+              overflow: hidden;
+              margin-right: 15px;
+              position: relative;
+              display: flex;
+              flex-direction: column;
+              .del {
+                position: absolute;
+                width: 25px;
+                top: 10px;
+                right: 10px;
+                cursor: pointer;
+              }
+              .img {
+                width: 100%;
+                height: calc(100% - 35px);
+                object-fit: cover;
+              }
+              .download {
+                position: absolute;
+                width: 25px;
+                top: 10px;
+                right: 35px;
+                cursor: pointer;
+                opacity: 0.8;
+              }
+              .name {
+                height: 35px;
+                width: 100%;
+                background: #f9f9f9;
+                display: flex;
+                align-items: center;
+                padding: 0 10px;
+                box-sizing: border-box;
+                span {
+                  display: block;
+                  text-overflow: ellipsis;
+                  max-width: 100%;
+                  white-space: nowrap;
+                  overflow: hidden;
+                }
+              }
+            }
+          }
+          .btn {
+            width: 100%;
+            height: 40px;
+            background: #007bff;
+            color: #fff;
+            border-radius: 15px;
+            margin-top: 10px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 185 - 0
src/views/testDetail/components/gap.vue

@@ -0,0 +1,185 @@
+<template>
+  <div class="c_box">
+    <!-- <div class="mask"></div> -->
+    <div v-if="!checkJson">暂未设置题目</div>
+    <div v-else class="choice_box">
+      <!-- <div class="title"><div>{{ `(${option[checkJson.type].name})` }}</div><div v-html="checkJson.title"></div></div> -->
+      <div class="c_title">
+        <div class="title">
+          {{ tindex + 1 + '、' + `(${option[checkJson.type].name})` + checkJson.title
+          }}<span v-if="see" style="color: #efa030"
+            >({{ checkJson.answer ? '参考答案:' + checkJson.answer : '暂无参考答案' }}
+            {{ cJson.score ? '分值:' + cJson.score + '分' : '' }})</span
+          >
+          <span style="color: #efa030" v-if="checkJson.score && !see">({{ '分值:' + checkJson.score + '分' }})</span>
+          <!-- </div><div v-html="checkJson.title"></div> -->
+        </div>
+        <div class="p_box" v-if="isTeacher == 1 && checkJson.score">
+          <el-input v-model="checkJson.score2" class="c_input" @change="numberPan" placeholder="请输入得分"></el-input
+          ><span style="margin: 0 10px">/</span><span>{{ checkJson.score }}分</span>
+        </div>
+        <div class="p_box" v-if="isTeacher == 2 && checkJson.score2">
+          <span>{{ checkJson.score2 }}分</span><span style="margin: 0 10px">/</span><span>{{ checkJson.score }}分</span>
+        </div>
+      </div>
+      <div class="choices">
+        <textarea
+          :readonly="checktype == 2"
+          rows="2"
+          v-autoHeight="68"
+          class="binfo_input binfo_textarea"
+          cols
+          v-model="checkJson.answer2"
+          placeholder=""
+        ></textarea>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    tindex: {
+      type: Number
+    },
+    cJson: {
+      type: Object
+    },
+    checktype: {
+      type: Number,
+      default: 1
+    },
+    see: {
+      type: Boolean,
+      default: false
+    },
+    isTeacher: {
+      type: Number,
+      default: 2
+    }
+  },
+  data() {
+    return {
+      option: {
+        1: { name: '问答题' }
+      },
+      checkJson: undefined
+    }
+  },
+  watch: {
+    checkJson: {
+      handler(newValue) {
+        this.$emit('update:cJson', newValue)
+      },
+      deep: true
+    }
+  },
+  directives: {
+    autoHeight: {
+      update(el, binding) {
+        const { value } = binding
+        if (value && typeof value === 'number') {
+          el.style.height = `${value}px`
+        } else {
+          el.style.height = 'auto'
+        }
+      },
+      componentUpdated(el) {
+        el.style.height = `${el.scrollHeight + 5}px`
+      }
+    }
+  },
+  methods: {
+    depthCopy(s) {
+      return JSON.parse(JSON.stringify(s))
+    },
+    numberPan() {
+      if (/[^\d]/.test(this.checkJson.score2) || parseInt(this.checkJson.score2) < 0) {
+        this.$message.error('请输入大于0的数字')
+        this.checkJson.score2 = ''
+      }
+
+      if (parseInt(this.checkJson.score2) > parseInt(this.checkJson.score)) {
+        this.$message.error('不能输入大于得分的数字')
+        this.checkJson.score2 = this.checkJson.score
+      }
+    }
+  },
+  mounted() {
+    this.checkJson = this.cJson ? this.depthCopy(this.cJson) : undefined
+    if (this.checkJson.answer2) {
+      setTimeout(() => {
+        this.checkJson.answer2 += '*0*%*'
+        setTimeout(() => {
+          this.checkJson.answer2 = this.checkJson.answer2.replaceAll('*0*%*', '')
+        }, 0)
+      }, 100)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.c_box {
+  width: 100%;
+  position: relative;
+  .choice_box {
+    .c_title {
+      display: flex;
+      justify-content: space-between;
+      .title {
+        font-weight: bold;
+        width: 100%;
+        word-break: break-all;
+        font-size: 16px;
+      }
+      .p_box {
+        margin-left: 5px;
+        min-width: fit-content;
+        display: flex;
+        align-items: center;
+        .c_input {
+          width: 90px;
+        }
+        /deep/.c_input .el-input__inner {
+          padding: 0 5px;
+          text-align: right;
+        }
+      }
+    }
+    .choices {
+      margin-top: 10px;
+      .binfo_input {
+        width: 100%;
+        margin: 0;
+        padding: 12px 14px;
+        display: block;
+        min-width: 0;
+        outline: none;
+        box-sizing: border-box;
+        background: none;
+        border: none;
+        border-radius: 4px;
+        background: #fff;
+        font-size: 16px;
+        resize: none;
+        font-family: 'Microsoft YaHei';
+        min-height: 48px;
+        /* border: 1px solid #3682fc00; */
+        border: 1.5px solid #cad1dc;
+      }
+      .binfo_textarea {
+        border: 1.5px solid #cad1dc;
+        font-size: 16px;
+        resize: none;
+        /* background: #f6f6f6; */
+        font-family: 'Microsoft YaHei';
+      }
+      .binfo_input:focus-visible {
+        border: 1.5px solid #3681fc !important;
+      }
+    }
+  }
+}
+</style>

+ 429 - 0
src/views/testDetail/components/topic.vue

@@ -0,0 +1,429 @@
+<template>
+  <div class="testBox">
+    <div class="c_box">
+      <div class="c_box_title">{{ title }}</div>
+      <div class="c_box_brief" v-if="brief">{{ brief }}</div>
+      <div class="c_box_score">
+        <span>总分:{{ score }}</span>
+        <span v-if="name" style="margin-left: 10px">答题人:{{ name }}</span>
+        <!-- <span style="margin-left: 10px">总得分:{{ score2 }}</span> -->
+      </div>
+      <div class="c_body" v-if="isloading">
+        <div v-if="type == 3">
+          <div v-for="(item, index) in checkArray[page].array" :key="index" class="check_box">
+            <div
+              class="title"
+              v-if="item.ttype != 1 || (!item.json && item.ttype == 1)"
+              :style="{ fontSize: item.ttype == 1 && '16px' }"
+            >
+              {{ selectType(item, index) }}
+            </div>
+            <div v-if="item.ttype == 1" class="answerBox">
+              <choiceV
+                :tindex="index"
+                :cJson.sync="item.json"
+                :checktype="checktype"
+                v-if="item.type == 1"
+                :see="see"
+                :isTeacher="isTeacher"
+              ></choiceV>
+              <gapV
+                :tindex="index"
+                :cJson.sync="item.json"
+                :checktype="checktype"
+                v-else-if="item.type == 3"
+                :see="see"
+                :isTeacher="isTeacher"
+              ></gapV>
+              <fileV
+                :tindex="index"
+                :cJson.sync="item.json"
+                :checktype="checktype"
+                v-else-if="item.type == 5"
+                :see="see"
+                :isTeacher="isTeacher"
+              ></fileV>
+            </div>
+            <div v-for="(item2, index2) in item.array" :key="`${index}-${index2}`" class="check_box_xia">
+              <div
+                class="title"
+                v-if="item2.ttype != 1 || (!item2.json && item2.ttype == 1)"
+                :style="{ fontSize: item2.ttype == 1 && '16px' }"
+              >
+                {{ selectType(item2, index2) }}
+              </div>
+              <div v-if="item2.ttype == 1" class="answerBox">
+                <choiceV
+                  :tindex="index2"
+                  :cJson.sync="item2.json"
+                  :checktype="checktype"
+                  v-if="item2.type == 1"
+                  :see="see"
+                  :isTeacher="isTeacher"
+                ></choiceV>
+                <gapV
+                  :tindex="index2"
+                  :cJson.sync="item2.json"
+                  :checktype="checktype"
+                  v-else-if="item2.type == 3"
+                  :see="see"
+                  :isTeacher="isTeacher"
+                ></gapV>
+                <fileV
+                  :tindex="index2"
+                  :cJson.sync="item2.json"
+                  :checktype="checktype"
+                  v-else-if="item2.type == 5"
+                  :see="see"
+                  :isTeacher="isTeacher"
+                ></fileV>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div v-else>
+          <div v-for="(item, index) in checkArray" :key="index" class="check_box">
+            <div
+              class="title"
+              v-if="item.ttype != 1 || (!item.json && item.ttype == 1)"
+              :style="{ fontSize: item.ttype == 1 && '16px' }"
+            >
+              {{ selectType(item, index) }}
+            </div>
+            <div v-if="item.ttype == 1" class="answerBox">
+              <choiceV
+                :tindex="index"
+                :cJson.sync="item.json"
+                :checktype="checktype"
+                v-if="item.type == 1"
+                :see="see"
+                :isTeacher="isTeacher"
+              ></choiceV>
+              <gapV
+                :tindex="index"
+                :cJson.sync="item.json"
+                :checktype="checktype"
+                v-else-if="item.type == 3"
+                :see="see"
+                :isTeacher="isTeacher"
+              ></gapV>
+              <fileV
+                :tindex="index"
+                :cJson.sync="item.json"
+                :checktype="checktype"
+                v-else-if="item.type == 5"
+                :see="see"
+                :isTeacher="isTeacher"
+              ></fileV>
+            </div>
+            <div v-for="(item2, index2) in item.array" :key="`${index}-${index2}`" class="check_box_xia">
+              <div
+                class="title"
+                v-if="item2.ttype != 1 || (!item2.json && item2.ttype == 1)"
+                :style="{ fontSize: item2.ttype == 1 && '16px' }"
+              >
+                {{ selectType(item2, index2) }}
+              </div>
+              <div v-if="item2.ttype == 1" class="answerBox">
+                <choiceV
+                  :tindex="index2"
+                  :cJson.sync="item2.json"
+                  :checktype="checktype"
+                  v-if="item2.type == 1"
+                  :see="see"
+                  :isTeacher="isTeacher"
+                ></choiceV>
+                <gapV
+                  :tindex="index2"
+                  :cJson.sync="item2.json"
+                  :checktype="checktype"
+                  v-else-if="item2.type == 3"
+                  :see="see"
+                  :isTeacher="isTeacher"
+                ></gapV>
+                <fileV
+                  :tindex="index2"
+                  :cJson.sync="item2.json"
+                  :checktype="checktype"
+                  v-else-if="item2.type == 5"
+                  :see="see"
+                  :isTeacher="isTeacher"
+                ></fileV>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div v-if="checkArray.length > 1 && type == 3" class="page">
+          <el-button type="primary" size="mini" :disabled="page == 0" @click="setPage('-1')">上一页</el-button>
+          <div class="p_page">
+            <span>{{ page + 1 }}</span
+            ><span>/</span><span>{{ checkArray.length }}</span>
+          </div>
+          <el-button type="primary" size="mini" :disabled="page == checkArray.length - 1" @click="setPage('1')"
+            >下一页</el-button
+          >
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import choiceV from './choice.vue'
+import gapV from './gap.vue'
+import fileV from './file.vue'
+export default {
+  props: {
+    cJson: {
+      type: Array
+    },
+    title: {
+      type: String
+    },
+    brief: {
+      type: String
+    },
+    checktype: {
+      type: Number,
+      default: 1
+    },
+    see: {
+      type: Boolean,
+      default: false
+    },
+    isTeacher: {
+      type: Number,
+      default: 2
+    },
+    name: {
+      type: String,
+      default: ''
+    }
+  },
+  components: {
+    choiceV,
+    gapV,
+    fileV
+  },
+  data() {
+    return {
+      checkArray: [],
+      type: 1,
+      page: 0,
+      options2: {
+        1: '选择题',
+        // 2: "问答题",
+        3: '问答题',
+        4: '添加文档',
+        5: '附件'
+      },
+      isloading: true,
+    }
+  },
+  computed: {
+    selectType() {
+      return function (item, index) {
+        if (item.ttype == 1) {
+          return index + 1 + '、' + this.options2[item.type]
+        } else if (item.ttype == 2) {
+          return `第${index + 1}组 (共${item.array.length}题)`
+        } else if (item.ttype == 3) {
+          return `分页${index + 1}`
+        }
+      }
+    },
+    score() {
+      let score = 0
+      this.checkArray.forEach(el => {
+        if ((el.ttype == 3 || el.ttype == 2) && el.array.length > 0) {
+          el.array.forEach(item => {
+            if (item.ttype == 2 && item.array.length > 0) {
+              item.array.forEach(item2 => {
+                if (item2.ttype == 1 && item2.json) {
+                  score += item2.json.score ? parseFloat(item2.json.score) : 0
+                }
+              })
+            } else if (item.ttype == 1 && item.json) {
+              score += item.json.score ? parseFloat(item.json.score) : 0
+            }
+          })
+        } else if (el.ttype == 1 && el.json) {
+          score += el.json.score ? parseFloat(el.json.score) : 0
+        }
+      })
+      return score > 0 ? score + '分' : '未设置分数'
+    },
+    // score2() {
+    //   let score = 0
+    //   // let type = 1
+    //   this.checkArray.forEach(el => {
+    //     if ((el.ttype == 3 || el.ttype == 2) && el.array.length > 0) {
+    //       el.array.forEach(item => {
+    //         if (item.ttype == 2 && item.array.length > 0) {
+    //           item.array.forEach(item2 => {
+    //             if (item2.ttype == 1 && item2.json) {
+    //               score += item2.json.score2 ? parseFloat(item2.json.score2) : 0
+    //             }
+    //           })
+    //         } else if (item.ttype == 1 && item.json) {
+    //           score += item.json.score2 ? parseFloat(item.json.score2) : 0
+    //         }
+    //       })
+    //     } else if (el.ttype == 1 && el.json) {
+    //       score += el.json.score2 ? parseFloat(el.json.score2) : 0
+    //     }
+    //   })
+    //   return score + '分'
+    // }
+  },
+  watch: {
+    cJson: {
+      handler(newVal, oldVal) {
+        console.log(newVal)
+        console.log(oldVal)
+        this.checkArray = this.setJson(this.depthCopy(newVal))
+      },
+      deep: true
+    }
+  },
+  methods: {
+    setPage(index){
+      this.isloading = false
+      if(index == '1'){
+        this.page++
+      }else if(index == '-1'){
+        this.page--
+      }
+      setTimeout(()=>{
+        this.isloading = true
+      }, 50)
+      this.$forceUpdate()
+    },
+    depthCopy(s) {
+      return JSON.parse(JSON.stringify(s))
+    },
+    setJson(json) {
+      if (json.length > 0) {
+        let _json = this.depthCopy(json)
+        this.type = _json[0].ttype
+        let checkArray = _json.filter(item => {
+          if (item.array) {
+            item.array = item.array.filter(item2 => {
+              if (item2.ttype == 1 && item2.json && !item2.json.answer2) {
+                item2.json.answer2 = []
+              }
+              if (item2.array) {
+                item2.array = item2.array.filter(item3 => {
+                  if (item3.ttype == 1 && item3.json && !item3.json.answer2) {
+                    item3.json.answer2 = []
+                  }
+                  return item3
+                })
+              }
+              return (item2.ttype != 1 && item2.array.length > 0) || item2.ttype == 1
+            })
+          }
+          if (item.ttype == 1 && item.json && !item.json.answer2) {
+            item.json.answer2 = []
+          }
+          console.log(item.array)
+          return (item.ttype != 1 && item.array.length > 0) || item.ttype == 1
+        })
+        console.log(checkArray)
+        return checkArray
+      } else {
+        return []
+      }
+    }
+  },
+  mounted() {
+    this.checkArray = this.setJson(this.depthCopy(this.cJson))
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.testBox {
+  margin: 0 0 20px 0;
+  min-height: 350px;
+  .c_box {
+    width: 95%;
+    margin: 0 auto;
+    background: #fff;
+    height: 100%;
+    .c_box_title {
+      width: 90%;
+      text-align: center;
+      font-size: 24px;
+      font-weight: bold;
+      word-break: break-all;
+      margin: 10px auto;
+    }
+
+    .c_box_brief {
+      width: 90%;
+      text-align: left;
+      font-size: 14px;
+      word-break: break-all;
+      margin: 10px auto;
+      color: #373737;
+    }
+
+    .c_box_score {
+      width: 90%;
+      text-align: center;
+      word-break: break-all;
+      margin: 0 auto 20px;
+      font-size: 16px;
+      color: #373737;
+    }
+
+    .c_body {
+      width: 90%;
+      margin: 0 auto;
+      .check_box {
+        margin-top: 10px;
+        padding-top: 10px;
+
+        .title {
+          font-size: 20px;
+          word-break: break-all;
+          font-weight: bold;
+        }
+        .answerBox {
+          margin-top: 10px;
+        }
+        .check_box_xia {
+          padding: 15px 0;
+          .title {
+            font-size: 18px;
+            font-weight: bold;
+          }
+          .answerBox {
+            margin-top: 10px;
+          }
+          .noanswerBox {
+            margin-top: 10px;
+          }
+        }
+        .check_box_xia + .check_box_xia {
+          border-top: 1px solid #eee;
+        }
+      }
+      .check_box:not(:first-child) {
+        border-top: 1px solid #eee;
+      }
+      .page {
+        margin: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        .p_page {
+          margin: 0 10px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 86 - 0
src/views/testDetail/file/wOffice.vue

@@ -0,0 +1,86 @@
+<template>
+  <el-dialog
+    title="文档查看"
+    :visible.sync="dialogVisibleOffice"
+    :append-to-body="true"
+    width="95%"
+    :before-close="handleClose"
+    class="dialog_diy"
+  >
+    <div style="height: 100%">
+      <iframe
+        ref="viframe"
+        style="width: 100%; height: 99%; border: none"
+        :src="'https://view.officeapps.live.com/op/view.aspx?src=' + encodeURIComponent(url)"
+      ></iframe>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="close()">关 闭</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  props: {
+    dialogVisibleOffice: {
+      type: Boolean,
+      default: false
+    },
+    url: {
+      type: String
+    }
+  },
+  data() {
+    return {}
+  },
+  methods: {
+    handleClose(done) {
+      this.close()
+      done()
+    },
+    close() {
+      this.$emit('update:dialogVisibleOffice', false)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+/deep/.dialog_diy .el-dialog {
+  height: 100%;
+  margin: 0 auto !important;
+}
+
+/deep/.dialog_diy .el-dialog__header {
+  background: #454545 !important;
+  padding: 15px 20px;
+}
+
+/deep/.dialog_diy .el-dialog__body {
+  height: calc(100% - 124px);
+  box-sizing: border-box;
+  padding: 0px;
+}
+
+/deep/.dialog_diy .el-dialog__title {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn {
+  top: 19px;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__body,
+/deep/.dialog_diy .el-dialog__footer {
+  background: #fafafa;
+}
+</style>

+ 86 - 0
src/views/testDetail/file/wPdf.vue

@@ -0,0 +1,86 @@
+<template>
+  <el-dialog
+    title="PDF查看"
+    :visible.sync="dialogVisiblePdf"
+    :append-to-body="true"
+    width="95%"
+    :before-close="handleClose"
+    class="dialog_diy"
+  >
+    <div style="height: 100%">
+      <iframe
+        ref="viframe"
+        style="width: 100%; height: 99%; border: none"
+        :src="'https://cloud.cocorobo.cn/pdf.js/web/viewer.html?file=' + encodeURIComponent(url)"
+      ></iframe>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="dialogVisiblePdf = false">关 闭</el-button>
+    </span>
+  </el-dialog>
+</template>
+  
+<script>
+export default {
+  props: {
+    dialogVisiblePdf: {
+      type: Boolean,
+      default: false
+    },
+    url: {
+      type: String
+    }
+  },
+  data() {
+    return {}
+  },
+  methods: {
+    handleClose(done) {
+      this.close()
+      done()
+    },
+    close() {
+      this.$emit('update:dialogVisiblePdf', false)
+    }
+  }
+}
+</script>
+  
+<style lang="scss" scoped>
+/deep/.dialog_diy .el-dialog {
+  height: 100%;
+  margin: 0 auto !important;
+}
+
+/deep/.dialog_diy .el-dialog__header {
+  background: #454545 !important;
+  padding: 15px 20px;
+}
+
+/deep/.dialog_diy .el-dialog__body {
+  height: calc(100% - 124px);
+  box-sizing: border-box;
+  padding: 0px;
+}
+
+/deep/.dialog_diy .el-dialog__title {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn {
+  top: 19px;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__body,
+/deep/.dialog_diy .el-dialog__footer {
+  background: #fafafa;
+}
+</style>

+ 78 - 0
src/views/testDetail/file/wPdf2.vue

@@ -0,0 +1,78 @@
+<template>
+    <el-dialog title="PDF查看" :visible.sync="dialogVisiblePdf" :append-to-body="true" width="95%" :before-close="handleClose"
+        class="dialog_diy">
+        <div style="height: 100%;">
+            <iframe ref="viframe" style="width: 100%; height: 99%; border: none"
+                :src="'https://cloud.cocorobo.cn/pdf.js/web/viewer.html?file=' + encodeURIComponent(url)"></iframe>
+        </div>
+        <span slot="footer" class="dialog-footer">
+            <el-button @click="dialogVisiblePdf = false;">关 闭</el-button>
+        </span>
+    </el-dialog>
+</template>
+  
+<script>
+export default {
+    props: {
+        dialogVisiblePdf: {
+            type: Boolean,
+            default: false
+        },
+        url: {
+            type: String,
+        }
+    },
+    data() {
+        return {
+        }
+    },
+    methods: {
+        handleClose(done) {
+            this.close()
+            done();
+        },
+        close() {
+            this.$emit('update:dialogVisiblePdf', false)
+        }
+    },
+}
+</script>
+  
+<style lang="scss" scoped>
+/deep/.dialog_diy .el-dialog {
+  height: 100%;
+  margin: 0 auto !important;
+}
+
+/deep/.dialog_diy .el-dialog__header {
+  background: #454545 !important;
+  padding: 15px 20px;
+}
+
+/deep/.dialog_diy .el-dialog__body {
+  height: calc(100% - 124px);
+  box-sizing: border-box;
+  padding: 0px;
+}
+
+/deep/.dialog_diy .el-dialog__title {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn {
+  top: 19px;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__body,
+/deep/.dialog_diy .el-dialog__footer {
+  background: #fafafa;
+}
+</style>

+ 132 - 0
src/views/testDetail/file/wVideo.vue

@@ -0,0 +1,132 @@
+<template>
+  <el-dialog
+    title="视频查看"
+    :visible.sync="dialogVisibleVideo"
+    :append-to-body="true"
+    width="95%"
+    :before-close="handleClose"
+    class="dialog_diy"
+  >
+    <div style="height: 100%; position: relative" v-if="dialogVisibleVideo">
+      <video-player
+        class="video-player vjs-custom-skin"
+        :playsinline="true"
+        :options="playerOptions"
+        @play="onPlayerPlay($event)"
+      ></video-player>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="close">关 闭</el-button>
+    </span>
+  </el-dialog>
+</template>
+  
+<script>
+export default {
+  props: {
+    dialogVisibleVideo: {
+      type: Boolean,
+      default: false
+    },
+    url: {
+      type: String
+    }
+  },
+  data() {
+    return {
+      playerOptions: {
+        playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
+        autoplay: false, //如果true,浏览器准备好时开始回放。
+        muted: false, // 默认情况下将会消除任何音频。
+        loop: false, // 导致视频一结束就重新开始。
+        preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
+        language: 'zh-CN',
+        aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
+        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
+        sources: [
+          {
+            type: 'video/mp4', //这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目   || "video/ogg"|| "video/webm"
+            src: '' //url地址require("../../assets/media/aaa.mp4")
+          }
+        ],
+        // poster: require("../../assets/tu31.png"), //你的封面地址
+        // poster: dataRes.imgUrl, //你的封面地址
+        notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。
+        controlBar: {
+          timeDivider: true, //当前时间和持续时间的分隔符
+          durationDisplay: true, //显示持续时间
+          remainingTimeDisplay: false, //是否显示剩余时间功能
+          fullscreenToggle: true //全屏按钮
+        }
+      }
+    }
+  },
+  watch: {
+    dialogVisibleVideo(newValue, oldValue) {
+      this.playerOptions.sources[0].src = this.url
+    }
+  },
+  methods: {
+    handleClose(done) {
+      this.close()
+      done()
+    },
+    close() {
+      this.$emit('update:dialogVisibleVideo', false)
+    },
+    onPlayerPlay() {}
+  },
+  mounted() {
+    this.playerOptions.sources[0].src = this.url
+  }
+}
+</script>
+  
+<style lang="scss" scoped>
+/deep/.dialog_diy .el-dialog {
+  height: 100%;
+  margin: 0 auto !important;
+}
+
+/deep/.dialog_diy .el-dialog__header {
+  background: #454545 !important;
+  padding: 15px 20px;
+}
+
+/deep/.dialog_diy .el-dialog__body {
+  height: calc(100% - 124px);
+  box-sizing: border-box;
+  padding: 0px;
+}
+
+/deep/.dialog_diy .el-dialog__title {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn {
+  top: 19px;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #fff;
+}
+
+/deep/.dialog_diy .el-dialog__body,
+/deep/.dialog_diy .el-dialog__footer {
+  background: #fafafa;
+}
+
+.video-player {
+  width: 100%;
+  height: 100%;
+}
+
+/deep/.video-player .video-js.vjs-fluid {
+  height: 100% !important;
+  padding: 0 !important;
+}
+</style>

+ 111 - 0
src/views/testDetail/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="home-container">
+    <head-bar @back="back">
+      <template #title>
+        <div class="navTitle">填写问卷</div>
+      </template>
+    </head-bar>
+    <div class="step_box">
+      <topicVue :cJson="cJson" :title="title" :brief="brief" ref="topicVue"></topicVue>
+      <div class="edit_top">
+        <div class="op_btn">
+          <el-button type="primary" size="small" @click="publish">提交</el-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getTestCourseDetail } from '@/api/testDetail'
+import { addTestWorks } from '@/api/testDetail'
+import headBar from '@/components/headBar.vue'
+import { mapGetters } from 'vuex'
+import topicVue from './components/topic.vue'
+export default {
+  components: {
+    headBar,
+    topicVue
+  },
+  data() {
+    return {
+      courseid: this.$route.query.courseid,
+      title: '',
+      brief: '',
+      cJson: []
+    }
+  },
+  computed: {
+    ...mapGetters(['userinfo'])
+  },
+  methods: {
+    back() {
+      this.$router.push({ path: '/test' })
+    },
+    getData() {
+      const params = {
+        cid: this.courseid
+      }
+      getTestCourseDetail(params)
+        .then(res => {
+          this.cJson = JSON.parse(res[0][0].chapters)
+          this.title = res[0][0].title
+          this.brief = res[0][0].brief
+        })
+        .catch(err => {
+          console.error(err)
+        })
+    },
+    publish() {
+      let cjson = this.$refs['topicVue'].checkArray
+      const params = [
+        {
+          uid: this.userinfo.userid,
+          cid: this.courseid,
+          cjson: JSON.stringify(cjson),
+          type: 2
+        }
+      ]
+      addTestWorks(params)
+        .then(res => {
+          this.$message.success('提交成功')
+          this.back()
+        })
+        .catch(err => {
+          console.error(err)
+        })
+    }
+  },
+  mounted() {
+    this.getData()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.navTitle {
+  font-size: 16px;
+  color: #fff;
+}
+.home-container {
+  height: 100%;
+  overflow: hidden;
+  .step_box {
+    margin-top: 1.5rem;
+    width: 100%;
+    height: calc(100vh - 1.5rem);
+    overflow: auto;
+    position: relative;
+    .edit_top {
+      width: 100%;
+      margin: 0 auto;
+      /deep/.el-button {
+        width: 100%;
+        height: 50px;
+        text-align: center;
+        font-size: 16px;
+      }
+    }
+  }
+}
+</style>

Деякі файли не було показано, через те що забагато файлів було змінено