Bladeren bron

BUG修复、部分页面新增

Koishi 2 jaren geleden
bovenliggende
commit
c57a42995f
34 gewijzigde bestanden met toevoegingen van 2328 en 1447 verwijderingen
  1. 1 1
      exam-06173-vue/.env.production
  2. 4 0
      exam-06173-vue/public/config/config.js
  3. 20 14
      exam-06173-vue/public/index.html
  4. 26 0
      exam-06173-vue/src/App.vue
  5. 1 1
      exam-06173-vue/src/api/archives/index.js
  6. 7 10
      exam-06173-vue/src/api/dashboard/index.js
  7. 21 0
      exam-06173-vue/src/api/exam/form.js
  8. BIN
      exam-06173-vue/src/assets/web/images/login3.png
  9. BIN
      exam-06173-vue/src/assets/web/images/login4.png
  10. 74 37
      exam-06173-vue/src/components/DepartRefs/index.vue
  11. 24 30
      exam-06173-vue/src/components/DicListSelect/index.vue
  12. 113 93
      exam-06173-vue/src/layout/components/WebHeader.vue
  13. 2 2
      exam-06173-vue/src/permission.js
  14. 8 0
      exam-06173-vue/src/router/index.js
  15. 1 0
      exam-06173-vue/src/store/modules/permission.js
  16. 1 1
      exam-06173-vue/src/views/admin/course/file/dir.vue
  17. 201 152
      exam-06173-vue/src/views/admin/course/file/index.vue
  18. 129 93
      exam-06173-vue/src/views/admin/course/form.vue
  19. 65 86
      exam-06173-vue/src/views/admin/course/index.vue
  20. 281 209
      exam-06173-vue/src/views/admin/exam/exam/form.vue
  21. 72 94
      exam-06173-vue/src/views/admin/exam/exam/index.vue
  22. 40 65
      exam-06173-vue/src/views/admin/exam/review/index.vue
  23. 323 226
      exam-06173-vue/src/views/admin/paper/paper/detail.vue
  24. 425 0
      exam-06173-vue/src/views/admin/paper/paper/view.vue
  25. 1 1
      exam-06173-vue/src/views/admin/repo/form.vue
  26. 7 3
      exam-06173-vue/src/views/admin/stat/total/detailBm.vue
  27. 5 2
      exam-06173-vue/src/views/admin/stat/total/detailZg.vue
  28. 28 0
      exam-06173-vue/src/views/checkPoint/index.vue
  29. 55 32
      exam-06173-vue/src/views/dashboard/index.vue
  30. 27 29
      exam-06173-vue/src/views/login/components/third-login.vue
  31. 124 78
      exam-06173-vue/src/views/login/index.vue
  32. 75 50
      exam-06173-vue/src/views/login/layout/LoginLayout.vue
  33. 86 68
      exam-06173-vue/src/views/web/course/list.vue
  34. 81 70
      exam-06173-vue/src/views/web/exam/list.vue

+ 1 - 1
exam-06173-vue/.env.production

@@ -2,4 +2,4 @@
 ENV = 'production'
 
 # 生产环境接口
-VUE_APP_BASE_API = 'http://123.60.213.70:8617'
+VUE_APP_BASE_API = 'http://10.155.32.18:8617'

+ 4 - 0
exam-06173-vue/public/config/config.js

@@ -0,0 +1,4 @@
+window.__MODE__ = {
+    // 是否在页面切换时显示当前页面 vue 文件所在路径
+    showPagePath: true
+};

+ 20 - 14
exam-06173-vue/public/index.html

@@ -1,16 +1,22 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-    <meta name="renderer" content="webkit">
-    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
-    <!-- <link rel="icon" href="<%= BASE_URL %>favicon.png"> -->
-    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <script async src="/tinymce/tinymce.min.js"></script>
-    <title><%= webpackConfig.name %></title>
-  </head>
-  <body>
-    <div id="app"></div>
-  </body>
-</html>
+
+<head>
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="renderer" content="webkit">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+  <!-- <link rel="icon" href="<%= BASE_URL %>favicon.png"> -->
+  <!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico"> -->
+  <script async src="/tinymce/tinymce.min.js"></script>
+  <script async src="/config/config.js"></script>
+  <title>
+    <%= webpackConfig.name %>
+  </title>
+</head>
+
+<body>
+  <div id="app"></div>
+</body>
+
+</html>

+ 26 - 0
exam-06173-vue/src/App.vue

@@ -5,8 +5,25 @@
 </template>
 
 <script>
+import { Message } from "element-ui";
 export default {
   name: "App",
+  watch: {
+    $route(r) {
+      if (window.__MODE__.showPagePath) {
+        this.$store.state.permission.addRoutes.forEach((pEle) => {
+          if (Array.isArray(pEle.children)) {
+            const findRes = pEle.children.find((cEle) => {
+              return cEle.name === r.name;
+            });
+            if (findRes) {
+              Message.success(`${findRes.meta.title} -> ${findRes.pagePath}`);
+            }
+          }
+        });
+      }
+    },
+  },
 };
 </script>
 
@@ -14,4 +31,13 @@ export default {
 body .el-scrollbar__wrap {
   overflow-x: hidden;
 }
+
+body .el-tabs .el-tabs__item {
+  font-size: 16px;
+  font-weight: 700;
+}
+
+body .el-select-dropdown .el-scrollbar__wrap {
+  overflow: scroll;
+}
 </style>

+ 1 - 1
exam-06173-vue/src/api/archives/index.js

@@ -54,5 +54,5 @@ export function getDepartZgKsDetail(data) {
  * @returns {Promise}
  */
 export function exportBmExcel(data) {
-    return download('/api/ec-depart-total-export', data, '部门统计-导出.xlsx')
+    return download('/api/total/ec/ec-depart-total-export', data, '部门统计-导出.xlsx')
 }

+ 7 - 10
exam-06173-vue/src/api/dashboard/index.js

@@ -1,9 +1,14 @@
 import { get, post } from '@/utils/request'
 
 /**
+ * 首页获取统计数值
+ */
+export function getCttTotal() {
+    return post('/api/course/course/ctt-total')
+}
+
+/**
  * 首页获取当前考试列表
- * @param examId
- * @returns {Promise}
  */
 export function getCurrentExam() {
     return get('/api/index/current/exam')
@@ -11,8 +16,6 @@ export function getCurrentExam() {
 
 /**
  * 首页获取部门培训课时排行
- * @param examId
- * @returns {Promise}
  */
 export function getDepartRank(data) {
     return post('/api/course/course/course-depart-rank', data)
@@ -20,8 +23,6 @@ export function getDepartRank(data) {
 
 /**
  * 首页获取部门考试及格率排行
- * @param examId
- * @returns {Promise}
  */
 export function getPassedRate(data) {
     return post('/api/exam/exam/depart-passed-rate', data)
@@ -29,8 +30,6 @@ export function getPassedRate(data) {
 
 /**
  * 首页获职工培训课时排行top20
- * @param examId
- * @returns {Promise}
  */
 export function getUserRank(data) {
     return post('/api/course/course/course-user-rank', data)
@@ -38,8 +37,6 @@ export function getUserRank(data) {
 
 /**
  * 首页获职考试及格率排行top20
- * @param examId
- * @returns {Promise}
  */
 export function getUserRate(data) {
     return post('/api/exam/exam/user-passed-rate', data)

+ 21 - 0
exam-06173-vue/src/api/exam/form.js

@@ -0,0 +1,21 @@
+import { post } from '@/utils/request'
+
+/**
+ * 获取课程列表
+ */
+export function getTableData() {
+    return post('/api/course/course/paging', {
+        current: 1,
+        size: 50000,
+        params: {
+            title: ''
+        }
+    })
+}
+
+/**
+ * 获取树形可用选项
+ */
+export function getTreeEnable(data) {
+    return post('/api/course/course/course-open-depart', data)
+}

BIN
exam-06173-vue/src/assets/web/images/login3.png


BIN
exam-06173-vue/src/assets/web/images/login4.png


+ 74 - 37
exam-06173-vue/src/components/DepartRefs/index.vue

@@ -1,18 +1,12 @@
 
 <template>
-
   <div>
-
-    <el-input
-      v-model="filterText"
-      placeholder="输入关键字进行过滤"
-    />
-
+    <el-input v-model="filterText" placeholder="输入关键字进行过滤" />
     <el-tree
       v-loading="treeLoading"
       ref="tree"
       :data="treeData"
-      :check-strictly="true"
+      :check-strictly="false"
       :default-checked-keys="deptCodes"
       :props="defaultProps"
       :filter-node-method="filterNode"
@@ -23,67 +17,110 @@
       node-key="deptCode"
       @check-change="handleCheckChange"
     />
-
   </div>
-
 </template>
 
 <script>
-import { fetchTree } from '@/api/sys/depart/depart'
+import { fetchTree } from "@/api/sys/depart/depart";
 export default {
-  name: 'DepartRefs',
+  name: "DepartRefs",
   props: {
-    value: Array
+    value: Array,
+    enableArray: {
+      type: Array,
+      default: () => [],
+    },
   },
   data() {
     return {
       defaultProps: {
-        label: 'deptName'
+        label: "deptName",
+        children: "children",
       },
-      filterText: '',
+      filterText: "",
       treeLoading: false,
       deptCodes: [],
-      treeData: []
-    }
+      treeData: [],
+      enableData: [],
+    };
   },
 
   watch: {
-
     filterText(val) {
-      this.$refs.tree.filter(val)
+      this.$refs.tree.filter(val);
     },
 
     value(val) {
-      this.deptCodes = val
-    }
+      this.deptCodes = val;
+      this.rinseData();
+    },
+
+    enableArray(val) {
+      this.enableData = val;
+      this.rinseData();
+    },
   },
   created() {
     // 初始化赋值
-    this.deptCodes = this.value
+    this.deptCodes = this.value;
+    this.enableData = this.enableArray;
 
-    fetchTree({}).then(response => {
-      this.treeData = response.data
-    })
+    fetchTree({}).then((response) => {
+      this.treeData = response.data;
+      this.rinseData();
+    });
   },
   methods: {
-
     handleCheckChange() {
       // 置空
-      const deptCodes = []
+      const deptCodes = [];
 
-      const nodes = this.$refs.tree.getCheckedNodes()
-      nodes.forEach(function(item) {
-        deptCodes.push(item.deptCode)
-      })
+      const nodes = this.$refs.tree.getCheckedNodes();
+      nodes.forEach((item) => {
+        deptCodes.push(item.deptCode);
+      });
 
-      this.$emit('input', deptCodes)
+      this.$emit("input", deptCodes);
     },
 
     filterNode(value, data) {
-      if (!value) return true
-      return data.deptName.indexOf(value) !== -1
-    }
+      if (!value) return true;
+      return data.deptName.indexOf(value) !== -1;
+    },
 
-  }
-}
+    rinseData() {
+      if (this.enableData.length) {
+        this.treeData.forEach((pEle) => {
+          let pEleFindRes = this.enableData.find((findEle) => {
+            return findEle === pEle.value;
+          });
+          if (!pEleFindRes) {
+            pEle.disabled = true;
+            for (let i = 0; i < this.deptCodes.length; i++) {
+              if (this.deptCodes[i] === pEle.value) {
+                this.deptCodes.splice(i, 1);
+              }
+            }
+          }
+          if (Array.isArray(pEle.children)) {
+            pEle.children.forEach((cEle) => {
+              let cEleFindRes = this.enableData.find((findEle) => {
+                return findEle === cEle.value;
+              });
+              if (!cEleFindRes) {
+                cEle.disabled = true;
+                for (let i = 0; i < this.deptCodes.length; i++) {
+                  if (this.deptCodes[i] === cEle.value) {
+                    this.deptCodes.splice(i, 1);
+                  }
+                }
+              }
+            });
+          }
+        });
+        this.$emit("input", this.deptCodes);
+      }
+    },
+  },
+};
 </script>

+ 24 - 30
exam-06173-vue/src/components/DicListSelect/index.vue

@@ -1,5 +1,4 @@
 <template>
-
   <el-select
     v-model="currentValue"
     :disabled="disabled"
@@ -10,71 +9,66 @@
     reserve-keyword
     clearable
     automatic-dropdown
-    class="filter-item"
+    popper-class="filter-item"
     @change="handlerChange"
   >
     <el-option
-      v-for="item in dataList"
-      :key="item.value"
+      v-for="(item, index) in dataList"
+      :key="index"
       :label="item.title"
       :value="item.value"
     />
   </el-select>
-
 </template>
 
 <script>
-
-import { fetchTree } from '@/api/sys/dict/value'
+import { fetchTree } from "@/api/sys/dict/value";
 
 export default {
-  name: 'DicListSelect',
+  name: "DicListSelect",
   props: {
     dicCode: String,
     title: String,
     value: String,
     disabled: Boolean,
-    excludes: Array
+    excludes: Array,
   },
   data() {
     return {
       // 下拉选项值
       dataList: [],
-      currentValue: []
-    }
+      currentValue: "",
+    };
   },
 
   watch: {
     // 检测查询变化
-    value: {
-      handler() {
-        this.currentValue = this.value
-      }
-    }
+    value(val) {
+      this.currentValue = val;
+    },
   },
   created() {
-    this.currentValue = this.value
-    this.fetchData()
+    this.currentValue = this.value;
+    this.fetchData();
   },
   methods: {
-
     fetchData() {
-      fetchTree({ dicCode: this.dicCode, excludes: this.excludes }).then(response => {
-        this.dataList = response.data
-      })
+      fetchTree({ dicCode: this.dicCode, excludes: this.excludes }).then(
+        (response) => {
+          this.dataList = response.data;
+        }
+      );
     },
     handlerChange(e) {
-      this.$emit('change', e)
-      this.$emit('input', e)
-    }
-  }
-}
+      this.$emit("change", e);
+      this.$emit("input", e);
+    },
+  },
+};
 </script>
 
 <style scoped>
-
-/deep/
-.el-form-item--medium .el-form-item__content{
+/deep/ .el-form-item--medium .el-form-item__content {
   line-height: 20px;
 }
 </style>

+ 113 - 93
exam-06173-vue/src/layout/components/WebHeader.vue

@@ -1,14 +1,24 @@
 <template>
-
   <el-row type="flex" class="header-bg" justify="center">
-
     <el-col :span="20" style="display: flex; align-items: center">
-
       <div class="col-logo">
         <a href="/">
           <div style="display: flex; flex-direction: row; align-items: center">
             <div v-if="siteData.frontLogo">
-              <img :src="siteData.frontLogo" :alt="siteData.siteName" style="height: 40px;">
+              <!-- <img
+                :src="siteData.frontLogo"
+                :alt="siteData.siteName"
+                style="height: 40px"
+              /> -->
+              <div class="logoBox">
+                <img
+                  v-if="siteData.backLogo"
+                  :src="siteData.backLogo"
+                  class="sidebar-logo"
+                  style="height: 40px"
+                />
+                <h1 class="sidebar-title">{{ siteData.siteName }}</h1>
+              </div>
             </div>
             <div v-else class="site-tt">{{ siteData.siteName }}</div>
           </div>
@@ -28,131 +38,120 @@
             {{ item.title }}
           </el-button>
         </div>
-
       </div>
 
       <div class="right-user">
         <div class="top-avatar">
-          <img v-if="avatar!=null && avatar!==''" :src="avatar">
-          <img v-else src="@/assets/web/avatar.png">
+          <img v-if="avatar != null && avatar !== ''" :src="avatar" />
+          <img v-else src="@/assets/web/avatar.png" />
         </div>
         <div>{{ realName }}</div>
-        <a v-if="roleType===2" class="alink" @click="toAdmin">管理中心</a>
+        <a v-if="roleType === 2" class="alink" @click="toAdmin">管理中心</a>
         <a @click="logout">退出</a>
       </div>
-
     </el-col>
-
   </el-row>
-
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import { mapGetters } from "vuex";
 
 export default {
-
   data() {
     return {
       menuList: [
         {
-          url: '/web/course',
-          title: '课程学习'
+          url: "/web/course",
+          title: "课程学习",
         },
         {
-          url: '/web/index',
-          title: '在线考试'
+          url: "/web/index",
+          title: "在线考试",
         },
         {
-          url: '/web/repo',
-          title: '题库训练'
+          url: "/web/repo",
+          title: "题库训练",
         },
         {
-          url: '/web/notice',
-          title: '系统公告'
+          url: "/web/notice",
+          title: "系统公告",
         },
         {
-          url: '/web/uc',
-          title: '用户中心'
-        }
+          url: "/web/uc",
+          title: "用户中心",
+        },
       ],
-      activeIndex: '/web/course'
-    }
+      activeIndex: "/web/course",
+    };
   },
   computed: {
-    ...mapGetters([
-      'avatar',
-      'realName',
-      'siteData',
-      'roleType'
-    ])
+    ...mapGetters(["avatar", "realName", "siteData", "roleType"]),
   },
   created() {
-    this.focusMenu()
+    this.focusMenu();
   },
 
   methods: {
-
     // 选定菜单
     focusMenu() {
-      const activeMenu = this.$route.meta.activeMenu
+      const activeMenu = this.$route.meta.activeMenu;
 
       if (activeMenu) {
-        this.activeIndex = activeMenu
-        return
+        this.activeIndex = activeMenu;
+        return;
       }
-      const path = this.$route.path.split('/')
-      const prefix = path[0] + '/' + path[1] + '/' + path[2]
-      console.log(prefix)
-      this.activeIndex = prefix
+      const path = this.$route.path.split("/");
+      const prefix = path[0] + "/" + path[1] + "/" + path[2];
+      console.log(prefix);
+      this.activeIndex = prefix;
     },
 
     isActive(url) {
       if (this.activeIndex === url) {
-        return 'nav active'
+        return "nav active";
       }
-      return 'nav'
+      return "nav";
     },
 
     navClick(url) {
-      this.activeIndex = url
-      this.$router.push({ path: url })
+      this.activeIndex = url;
+      this.$router.push({ path: url });
     },
 
     async logout() {
-      const that = this
-
-      this.$confirm('确实要退出吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'info'
-      }).then(() => {
-        that.$store.dispatch('user/logout').then(() => {
-          that.$router.push('/login')
-        })
-      }).catch(() => {
+      const that = this;
 
+      this.$confirm("确实要退出吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "info",
       })
+        .then(() => {
+          that.$store.dispatch("user/logout").then(() => {
+            that.$router.push("/login");
+          });
+        })
+        .catch(() => {});
     },
 
     handleUc() {
-      this.$router.push({ name: 'UserCenter' })
+      this.$router.push({ name: "UserCenter" });
     },
 
     toAdmin() {
-      this.$router.push({ path: '/admin/dashboard' })
-    }
-  }
-}
+      this.$router.push({ path: "/admin/dashboard" });
+    },
+  },
+};
 </script>
 
-<style scoped>
-  .header-bg{
-    height: 60px;
-    background: #4377fb;
-  }
+<style lang="scss" scoped>
+.header-bg {
+  height: 60px;
+  background: #4377fb;
+}
 
-.right-user{
+.right-user {
   display: flex;
   justify-content: flex-end;
   flex-direction: row;
@@ -161,41 +160,40 @@ export default {
   width: 250px;
 }
 
-.site-tt{
+.site-tt {
   font-weight: 700;
   font-size: 20px;
   color: #eee;
   flex-grow: 1;
   text-align: left;
-  padding-left: 10px
+  padding-left: 10px;
 }
 
-.right-user a, .right-user div{
+.right-user a,
+.right-user div {
   color: #efefef;
   font-size: 14px;
   font-weight: 500;
   margin-right: 10px;
 }
 
-.right-user a:last-child{
+.right-user a:last-child {
   margin-right: 0px !important;
 }
 
-.right-user a:hover{
+.right-user a:hover {
   color: #ffd550;
 }
 
-/deep/
-.alink {
-  color: #FFD550 !important;
+/deep/ .alink {
+  color: #ffd550 !important;
 }
 
-/deep/
-.alink:hover {
-  color: #F94E3E !important;
+/deep/ .alink:hover {
+  color: #f94e3e !important;
 }
 
-.nav{
+.nav {
   color: #fff;
   border: none;
   background: transparent;
@@ -205,17 +203,17 @@ export default {
   margin-right: 10px;
 }
 
-.active{
+.active {
   color: #000055;
-  background: #FFD550;
+  background: #ffd550;
 }
 
 .nav:hover {
   color: #000055;
-  background: #FFD550;
+  background: #ffd550;
 }
 
-.col-logo{
+.col-logo {
   display: flex;
   align-items: center;
   justify-content: flex-start;
@@ -223,31 +221,53 @@ export default {
   max-width: 200px;
 }
 
-.col-menu{
+.col-menu {
   flex-grow: 1;
   align-items: center;
   text-align: center;
 }
 
-/deep/
-.top-avatar{
+/deep/ .top-avatar {
   text-align: right;
   display: flex;
   align-items: center;
   margin-right: 5px !important;
-
 }
 
-/deep/
-.top-avatar div{
+/deep/ .top-avatar div {
   display: flex;
   align-items: center;
   margin-right: -10px !important;
 }
 
-/deep/
-.top-avatar img{
-  width: 30px; height: 30px; border-radius: 15px;
+/deep/ .top-avatar img {
+  width: 30px;
+  height: 30px;
+  border-radius: 15px;
 }
 
-</style>
+.logoBox {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  position: relative;
+
+  .sidebar-logo {
+    margin-right: 0px;
+  }
+
+  .sidebar-title {
+    position: absolute;
+    left: 48px;
+    display: inline-block;
+    margin: 0;
+    color: #fff;
+    font-weight: 600;
+    line-height: 50px;
+    font-size: 18px;
+    font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
+    vertical-align: middle;
+    white-space: nowrap;
+  }
+}
+</style>

+ 2 - 2
exam-06173-vue/src/permission.js

@@ -9,9 +9,9 @@ import getPageTitle from '@/utils/get-page-title'
 NProgress.configure({ showSpinner: false })
 
 // 白名单
-const whiteList = ['/login', '/face', '/register', '/forgot', '401', '404', '/cert', '/sync']
+const whiteList = ['/login', '/face', '/register', '/forgot', '401', '404', '/cert', '/sync', '/checkPoint']
 
-router.beforeEach(async(to, from, next) => {
+router.beforeEach(async (to, from, next) => {
   // 进度条
   NProgress.start()
 

+ 8 - 0
exam-06173-vue/src/router/index.js

@@ -52,6 +52,13 @@ export const constantRoutes = [
   },
 
   {
+    path: '/checkPoint',
+    component: () => import('@/views/checkPoint/index'),
+    hidden: true,
+    children: []
+  },
+
+  {
     path: '/face',
     name: 'FaceLogin',
     component: () => import('@/views/login/components/face-login'),
@@ -245,6 +252,7 @@ const createRouter = () => new Router({
   routes: constantRoutes
 })
 
+
 const router = createRouter()
 
 export function resetRouter() {

+ 1 - 0
exam-06173-vue/src/store/modules/permission.js

@@ -30,6 +30,7 @@ export function getAsyncRoutes(routes) {
       } else if (item.component === 'WebLayout') {
         newItem.component = WebLayout
       } else {
+        newItem.pagePath = item.component;
         newItem.component = loadView(item.component)
       }
     }

+ 1 - 1
exam-06173-vue/src/views/admin/course/file/dir.vue

@@ -25,7 +25,7 @@
             <el-table-column prop="fileType_dictText" label="课件类型" align="center" width="200px" />
             <el-table-column label="要求时长*分钟" align="center" width="200px">
               <template v-slot="scope">
-                <el-input-number v-model="scope.row.needLearn" :min="0" :max="99999" size="mini" />
+                <el-input-number v-model="scope.row.needLearn" :min="1" :max="99999" size="mini" />
               </template>
             </el-table-column>
 

+ 201 - 152
exam-06173-vue/src/views/admin/course/file/index.vue

@@ -1,11 +1,12 @@
 <template>
-
   <div>
-
     <div style="display: flex">
       <div style="width: 300px">
         <el-card>
-          <dic-tree v-model="listQuery.params.catId" dic-code="course_file_catalog" />
+          <dic-tree
+            v-model="listQuery.params.catId"
+            dic-code="course_file_catalog"
+          />
         </el-card>
       </div>
       <div style="flex-grow: 1; padding-left: 20px">
@@ -17,24 +18,34 @@
           @edit="handleEdit"
         >
           <template slot="filter-content">
-
-            <dic-list-select v-model="listQuery.params.fileType" dic-code="course_file_type" title="课件类型" />
-            <el-input v-model="listQuery.params.title" placeholder="搜索课件名称" style="width: 200px;" class="filter-item" />
-
+            <dic-list-select
+              v-model="listQuery.params.fileType"
+              dic-code="course_file_type"
+              title="课件类型"
+              class="filter-item"
+            />
+            <el-input
+              v-model="listQuery.params.title"
+              placeholder="搜索课件名称"
+              style="width: 200px"
+              class="filter-item"
+            />
           </template>
 
           <template slot="data-columns">
-
             <el-table-column
               label="课件名称"
               prop="title"
               show-overflow-tooltip
             >
-
               <template slot-scope="scope">
-                <detail-link :id="scope.row.id" :title="scope.row.title" permission="course:file:update" @click="handleEdit" />
+                <detail-link
+                  :id="scope.row.id"
+                  :title="scope.row.title"
+                  permission="course:file:update"
+                  @click="handleEdit"
+                />
               </template>
-
             </el-table-column>
 
             <el-table-column
@@ -63,14 +74,12 @@
               width="120px"
               show-overflow-tooltip
             >
-
               <template v-slot="scope">
-                <span v-if="scope.row.duration===0">无</span>
+                <span v-if="scope.row.duration === 0">无</span>
                 <span v-else>
-                  <sec-format :value="scope.row.duration"/>
+                  <sec-format :value="scope.row.duration" />
                 </span>
               </template>
-
             </el-table-column>
 
             <el-table-column
@@ -87,30 +96,41 @@
               width="180px"
             />
 
-            <el-table-column
-              label="操作"
-              align="center"
-              width="150px"
-            >
-
+            <el-table-column label="操作" align="center" width="150px">
               <template slot-scope="scope">
-                <el-link type="primary" icon="el-icon-view" style="margin-left: 10px" @click="handlePreview(scope.row)">在线预览</el-link>
+                <el-link
+                  type="primary"
+                  icon="el-icon-view"
+                  style="margin-left: 10px"
+                  @click="handlePreview(scope.row)"
+                  >在线预览</el-link
+                >
               </template>
-
             </el-table-column>
-
           </template>
-
         </data-table>
       </div>
     </div>
 
-    <el-dialog :close-on-click-modal="false" :visible.sync="dialogVisible" title="课件管理" width="50%">
-
-      <el-form ref="postForm" :model="postForm" :rules="rules" label-position="left" label-width="120px">
-
+    <el-dialog
+      :close-on-click-modal="false"
+      :visible.sync="dialogVisible"
+      title="课件管理"
+      width="50%"
+    >
+      <el-form
+        ref="postForm"
+        :model="postForm"
+        :rules="rules"
+        label-position="left"
+        label-width="120px"
+      >
         <el-form-item label="课件类型" prop="fileType">
-          <dic-list-select v-model="postForm.fileType" :disabled="isEdit" dic-code="course_file_type" />
+          <dic-list-select
+            v-model="postForm.fileType"
+            :disabled="isEdit"
+            dic-code="course_file_type"
+          />
         </el-form-item>
 
         <el-form-item label="课件名称" prop="title">
@@ -118,101 +138,129 @@
         </el-form-item>
 
         <el-form-item label="课件分类">
-          <dic-catalog-tree v-model="postForm.catId" dic-code="course_file_catalog" />
+          <dic-catalog-tree
+            v-model="postForm.catId"
+            dic-code="course_file_catalog"
+          />
         </el-form-item>
 
         <el-form-item v-if="postForm.fileType" label="上传文件" prop="fileUrl">
-          <file-upload v-model="postForm.fileUrl" :limit="1" :tips="tips" :accept="accept" list-type="file" />
+          <file-upload
+            v-model="postForm.fileUrl"
+            :limit="1"
+            :tips="tips"
+            :accept="accept"
+            list-type="file"
+          />
         </el-form-item>
 
-        <el-form-item v-if="postForm.fileType === '33' && postForm.fileUrl" label="视频时长">
+        <el-form-item
+          v-if="postForm.fileType === '33' && postForm.fileUrl"
+          label="视频时长"
+        >
           <el-input-number v-model="postForm.duration" /> 单位:秒
           <div style="color: #ff4949">
-            <small>MP4视频可以直接读取视频时长,其他格式视频将在提交后异步读取.</small>
+            <small
+              >MP4视频可以直接读取视频时长,其他格式视频将在提交后异步读取.</small
+            >
           </div>
         </el-form-item>
-
       </el-form>
 
       <div slot="footer" class="dialog-footer">
-        <el-alert
-          type="success"
-          style="margin-bottom: 20px"
-        >
+        <el-alert type="success" style="margin-bottom: 20px">
           课件保存后,您可以从列表来预览它!
         </el-alert>
         <el-button @click="dialogVisible = false">取 消</el-button>
-        <el-button :loading="loading" type="primary" @click="handleSave">确 定</el-button>
+        <el-button :loading="loading" type="primary" @click="handleSave"
+          >确 定</el-button
+        >
       </div>
-
     </el-dialog>
 
-    <el-dialog :close-on-click-modal="false" :visible.sync="previewVisible" title="文件预览" width="60%" @close="closePreview">
-
+    <el-dialog
+      :close-on-click-modal="false"
+      :visible.sync="previewVisible"
+      title="文件预览"
+      width="60%"
+      @close="closePreview"
+    >
       <div style="max-height: 650px; overflow-x: hidden; overflow-y: auto">
-        <div v-if="previewData.fileType==='33'">
-          <video-player v-model="previewData.viewUrl" :ref-id="previewData.id" />
+        <div v-if="previewData.fileType === '33'">
+          <video-player
+            v-model="previewData.viewUrl"
+            :ref-id="previewData.id"
+          />
         </div>
-        <pdf-reader v-if="previewData.fileType==='11'" :src="previewData.viewUrl" />
-        <pdf-reader v-if="previewData.fileType==='22'" :src="previewData.fileUrl" />
+        <pdf-reader
+          v-if="previewData.fileType === '11'"
+          :src="previewData.viewUrl"
+        />
+        <pdf-reader
+          v-if="previewData.fileType === '22'"
+          :src="previewData.fileUrl"
+        />
       </div>
     </el-dialog>
 
-    <div v-if="postForm.fileType === '33' && postForm.fileUrl" style="display: none">
-      <video-player v-model="postForm.fileUrl" @loaded="videoLoaded"/>
+    <div
+      v-if="postForm.fileType === '33' && postForm.fileUrl"
+      style="display: none"
+    >
+      <video-player v-model="postForm.fileUrl" @loaded="videoLoaded" />
     </div>
-
   </div>
-
 </template>
 
 <script>
-import DataTable from '@/components/DataTable'
-import DicListSelect from '@/components/DicListSelect'
-import { saveData, fetchDetail } from '@/api/course/file'
-import FileUpload from '@/components/FileUpload'
-import DicCatalogTree from '@/components/DicTreeSelect'
-import { fetchTree } from '@/api/sys/dict/value'
-import DicTree from '@/components/DicTree/index'
-import DetailLink from '@/components/DetailLink'
-import VideoPlayer from '@/components/VideoPlayer'
-import PdfReader from '@/components/PdfReader'
-import SecFormat from '@/components/SecFormat'
+import DataTable from "@/components/DataTable";
+import DicListSelect from "@/components/DicListSelect";
+import { saveData, fetchDetail } from "@/api/course/file";
+import FileUpload from "@/components/FileUpload";
+import DicCatalogTree from "@/components/DicTreeSelect";
+import { fetchTree } from "@/api/sys/dict/value";
+import DicTree from "@/components/DicTree/index";
+import DetailLink from "@/components/DetailLink";
+import VideoPlayer from "@/components/VideoPlayer";
+import PdfReader from "@/components/PdfReader";
+import SecFormat from "@/components/SecFormat";
 
 export default {
-  name: 'CourseFileList',
-  components: { SecFormat, PdfReader, VideoPlayer, DetailLink, DicTree, DicCatalogTree, FileUpload, DicListSelect, DataTable },
+  name: "CourseFileList",
+  components: {
+    SecFormat,
+    PdfReader,
+    VideoPlayer,
+    DetailLink,
+    DicTree,
+    DicCatalogTree,
+    FileUpload,
+    DicListSelect,
+    DataTable,
+  },
   data() {
     return {
-
       dialogVisible: false,
       fileList: [],
       isEdit: false,
 
-      tips: '',
-      accept: '',
+      tips: "",
+      accept: "",
       data: {},
 
       loading: false,
       postForm: {
-        fileType: '',
-        fileUrl: '',
-        needLearn: 0
+        fileType: "",
+        fileUrl: "",
+        needLearn: 0,
       },
 
       rules: {
+        title: [{ required: true, message: "课件名称不能为空!" }],
 
-        title: [
-          { required: true, message: '课件名称不能为空!' }
-        ],
+        fileType: [{ required: true, message: "课件类型不能为空!" }],
 
-        fileType: [
-          { required: true, message: '课件类型不能为空!' }
-        ],
-
-        fileUrl: [
-          { required: true, message: '课件文件必须上传!' }
-        ]
+        fileUrl: [{ required: true, message: "课件文件必须上传!" }],
       },
 
       listQuery: {
@@ -220,31 +268,30 @@ export default {
         size: 10,
         params: {
           courseId: this.courseId,
-          catId: null
-        }
+          catId: null,
+        },
       },
 
       options: {
-
         // 可批量操作
         multi: true,
 
         add: {
           enable: true,
-          permission: 'course:file:add'
+          permission: "course:file:add",
         },
         edit: {
           enable: true,
-          permission: 'course:file:update'
+          permission: "course:file:update",
         },
         delete: {
           enable: true,
-          permission: 'course:file:delete',
-          url: '/api/course/file/delete'
+          permission: "course:file:delete",
+          url: "/api/course/file/delete",
         },
 
         // 列表请求URL
-        listUrl: '/api/course/file/paging'
+        listUrl: "/api/course/file/paging",
       },
 
       previewVisible: false,
@@ -252,110 +299,112 @@ export default {
 
       treeData: [],
       defaultProps: {
-        children: 'children',
-        label: 'title'
-      }
-    }
+        children: "children",
+        label: "title",
+      },
+    };
   },
   watch: {
-    'postForm.fileType': {
+    "postForm.fileType": {
       handler(val) {
-        if (val === '11') {
-          this.tips = '包含doc,docx,xls,xlsx,ppt,pptx等Office类型文件'
-          this.accept = '.doc, .docx, .xls, .xlsx, .ppt, .pptx'
-          this.data = {}
-          this.previewData.fileType = '11'
+        if (val === "11") {
+          this.tips = "包含doc,docx,xls,xlsx,ppt,pptx等Office类型文件";
+          this.accept = ".doc, .docx, .xls, .xlsx, .ppt, .pptx";
+          this.data = {};
+          this.previewData.fileType = "11";
         }
-        if (val === '22') {
-          this.tips = 'PDF文件可以直接在线预览'
-          this.accept = '.pdf'
-          this.data = {}
-          this.previewData.fileType = '22'
+        if (val === "22") {
+          this.tips = "PDF文件可以直接在线预览";
+          this.accept = ".pdf";
+          this.data = {};
+          this.previewData.fileType = "22";
         }
-        if (val === '33') {
-          this.tips = '视频文件,尽可能使用mp4格式兼容性较好,也支持flv,mkv,rm,rmvb等格式'
-          this.accept = '.mp4, .mov, .flv, .mkv, .rm, .rmvb, .webm, .ogv, .avi'
-          this.data = {}
-          this.previewData.fileType = '33'
+        if (val === "33") {
+          this.tips =
+            "视频文件,尽可能使用mp4格式兼容性较好,也支持flv,mkv,rm,rmvb等格式";
+          this.accept = ".mp4, .mov, .flv, .mkv, .rm, .rmvb, .webm, .ogv, .avi";
+          this.data = {};
+          this.previewData.fileType = "33";
         }
-      }
-    }
+      },
+    },
   },
 
   created() {
-    const id = this.$route.params.courseId
+    const id = this.$route.params.courseId;
     if (id !== undefined) {
-      this.listQuery.params.courseId = id
+      this.listQuery.params.courseId = id;
     }
 
-    fetchTree({ dicCode: 'course_file_catalog' }).then(res => {
-      this.treeData = res.data
-    })
+    fetchTree({ dicCode: "course_file_catalog" }).then((res) => {
+      this.treeData = res.data;
+    });
   },
 
   methods: {
-
     handleAdd() {
-      this.postForm = { courseId: this.listQuery.params.courseId }
-      this.dialogVisible = true
-      this.isEdit = false
+      this.postForm = { courseId: this.listQuery.params.courseId };
+      this.dialogVisible = true;
+      this.isEdit = false;
     },
 
     handleEdit(id) {
       // 先清理表单
-      this.postForm = {}
-      this.isEdit = true
-      this.dialogVisible = true
-      fetchDetail(id).then(res => {
-        this.postForm = res.data
-      })
+      this.postForm = {};
+      this.isEdit = true;
+      this.dialogVisible = true;
+      fetchDetail(id).then((res) => {
+        this.postForm = res.data;
+      });
     },
 
     handleSave() {
       this.$refs.postForm.validate((valid) => {
         if (!valid) {
-          return
+          return;
         }
 
-        this.loading = true
+        this.loading = true;
+
+        saveData(this.postForm)
+          .then(() => {
+            this.$message({
+              type: "success",
+              message: "课件保存成功!",
+            });
+            this.dialogVisible = false;
+            this.$refs.pagingTable.getList();
 
-        saveData(this.postForm).then(() => {
-          this.$message({
-            type: 'success',
-            message: '课件保存成功!'
+            this.loading = false;
           })
-          this.dialogVisible = false
-          this.$refs.pagingTable.getList()
-
-          this.loading = false
-        }).catch(err => {
-          console.log(err)
-          this.loading = false
-        })
-      })
+          .catch((err) => {
+            console.log(err);
+            this.loading = false;
+          });
+      });
     },
 
     catalogSelected(node) {
-      console.log('node', node)
-      this.listQuery.params.catId = node.id
+      console.log("node", node);
+      this.listQuery.params.catId = node.id;
     },
 
     handlePreview(data) {
-      this.previewVisible = true
-      this.previewData.fileType = data.fileType
-      this.previewData.viewUrl = data.viewUrl
-      this.previewData.fileUrl = data.fileUrl
+      this.previewVisible = true;
+      this.previewData.fileType = data.fileType;
+      this.previewData.viewUrl = data.viewUrl;
+      this.previewData.fileUrl = data.fileUrl;
     },
 
     closePreview() {
-      this.previewData = {}
+      this.previewData = {};
     },
 
     // 读取视频长度
     videoLoaded(d) {
-      console.log('++++++视频长度' + d)
-      this.postForm['duration'] = parseInt(d)
-    }
-  }
-}
+      console.log("++++++视频长度" + d);
+      this.postForm["duration"] = parseInt(d);
+    },
+  },
+};
 </script>

+ 129 - 93
exam-06173-vue/src/views/admin/course/form.vue

@@ -1,12 +1,15 @@
 <template>
   <div>
-
-    <el-form ref="postForm" :model="postForm" :rules="rules" label-position="left" label-width="120px">
-
+    <el-form
+      ref="postForm"
+      :model="postForm"
+      :rules="rules"
+      label-position="left"
+      label-width="120px"
+    >
       <h3>课程描述</h3>
 
       <el-card>
-
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="课程名称" prop="title">
@@ -15,7 +18,10 @@
           </el-col>
           <el-col :span="12">
             <el-form-item label="课程分类">
-              <dic-catalog-tree v-model="postForm.catId" dic-code="course_catalog" />
+              <dic-catalog-tree
+                v-model="postForm.catId"
+                dic-code="course_catalog"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -37,9 +43,7 @@
           </el-col>
 
           <el-col v-if="postForm.timeLimit" :span="12">
-
             <el-form-item label="学习时间设置">
-
               <el-date-picker
                 v-model="dateValues"
                 style="margin-left: 10px"
@@ -50,23 +54,34 @@
                 start-placeholder="开始时间"
                 end-placeholder="结束时间"
               />
-
             </el-form-item>
           </el-col>
 
           <el-col :span="12">
             <el-form-item label="学完积分">
-              <el-input-number v-model="postForm.points" :min="0" :max="999999" /> <small>学完课程获得积分数量</small>
+              <el-input-number
+                v-model="postForm.points"
+                :min="0"
+                :max="999999"
+              />
+              <small>学完课程获得积分数量</small>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="课程课时">
-              <el-input-number v-model="postForm.periods" :min="0" :max="9999" />
+              <el-input-number
+                v-model="postForm.periods"
+                :min="1"
+                :max="9999"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="24">
             <el-form-item label="课程封面">
-              <file-upload v-model="postForm.cover" tips="请上传大于2:1的宽图" />
+              <file-upload
+                v-model="postForm.cover"
+                tips="请上传大于2:1的宽图"
+              />
             </el-form-item>
           </el-col>
 
@@ -81,36 +96,29 @@
       <h3>权限配置</h3>
       <el-card>
         <el-radio-group v-model="postForm.openType" size="small">
-          <el-radio :label="1">完全公开</el-radio>
+          <!-- <el-radio :label="1">完全公开</el-radio> -->
           <el-radio :label="2">指定部门</el-radio>
           <el-radio :label="3">指定人员</el-radio>
         </el-radio-group>
 
         <div style="padding-top: 20px">
-
-          <div v-if="postForm.openType===1">
-            <el-alert
-              title="开放的,任何人都可以参与学习!"
-              type="warning"
-            />
+          <div v-if="postForm.openType === 1">
+            <el-alert title="开放的,任何人都可以参与学习!" type="warning" />
           </div>
 
-          <div v-if="postForm.openType==2">
+          <div v-if="postForm.openType == 2">
             <depart-refs v-model="postForm.deptCodes" />
           </div>
 
-          <div v-if="postForm.openType==3">
+          <div v-if="postForm.openType == 3">
             <user-refs v-model="postForm.personList" />
           </div>
-
         </div>
-
       </el-card>
 
       <h3>学习防作弊</h3>
 
       <el-card>
-
         <el-form-item label="弹窗防呆校验">
           <el-switch
             v-model="postForm.checkOn"
@@ -119,7 +127,13 @@
           />
           <span v-if="postForm.checkOn">
             &nbsp;每隔&nbsp;
-            <el-input-number v-model="postForm.checkSec" :min="1" :max="99999" label="分钟" size="mini" />
+            <el-input-number
+              v-model="postForm.checkSec"
+              :min="1"
+              :max="99999"
+              label="分钟"
+              size="mini"
+            />
             &nbsp;分钟出验证码,输入正确才可继续学习&nbsp;
           </span>
         </el-form-item>
@@ -143,11 +157,17 @@
         </el-form-item>
 
         <el-form-item label="每日可学时段">
-          <time-range-picker v-model="postForm.dayRule"/>
+          <time-range-picker v-model="postForm.dayRule" />
         </el-form-item>
 
         <el-form-item label="每日学时上限">
-          <el-input-number v-model="postForm.dayLimit" :min="0" :max="1440" size="mini" /> <small>单位:分钟,本门课程每日最大累计学时,超出后无法学习</small>
+          <el-input-number
+            v-model="postForm.dayLimit"
+            :min="0"
+            :max="1440"
+            size="mini"
+          />
+          <small>单位:分钟,本门课程每日最大累计学时,超出后无法学习</small>
         </el-form-item>
 
         <el-form-item label="人脸识别学习">
@@ -156,8 +176,7 @@
             active-color="#13ce66"
             inactive-color="#ff4949"
           />
-          <span>&nbsp;每个课件学习前都需要进行人脸识别&nbsp;
-          </span>
+          <span>&nbsp;每个课件学习前都需要进行人脸识别&nbsp; </span>
         </el-form-item>
 
         <el-form-item label="摄像头抓拍">
@@ -167,8 +186,15 @@
             inactive-color="#ff4949"
           />
 
-          <span v-if="postForm.faceCam">&nbsp;学员开始学习后,每&nbsp;
-            <el-input-number v-model="postForm.faceInterval" :min="1" :max="30" label="分钟" size="mini" />
+          <span v-if="postForm.faceCam"
+            >&nbsp;学员开始学习后,每&nbsp;
+            <el-input-number
+              v-model="postForm.faceInterval"
+              :min="1"
+              :max="30"
+              label="分钟"
+              size="mini"
+            />
             &nbsp;分钟抓拍一张学员照片&nbsp;(仅PC)
           </span>
         </el-form-item>
@@ -179,14 +205,35 @@
             active-color="#13ce66"
             inactive-color="#ff4949"
           />
-          对抓拍图片进行人脸识别,如果连续识别失败超过 <el-input-number v-model="postForm.faceChance" :min="0" :max="99" label="次数" size="mini" /> 次就退出学习(仅PC)
+          对抓拍图片进行人脸识别,如果连续识别失败超过
+          <el-input-number
+            v-model="postForm.faceChance"
+            :min="0"
+            :max="99"
+            label="次数"
+            size="mini"
+          />
+          次就退出学习(仅PC)
         </el-form-item>
-
       </el-card>
 
       <el-row>
         <el-col>
-          <h3>直播日程  <div style="line-height: 32px; font-size: 14px; color: #ff3333; float: right"><a href="https://obsproject.com/zh-cn/download" target="_blank">直播推荐使用OBS进行推流,点此下载</a></div></h3>
+          <h3>
+            直播日程
+            <div
+              style="
+                line-height: 32px;
+                font-size: 14px;
+                color: #ff3333;
+                float: right;
+              "
+            >
+              <a href="https://obsproject.com/zh-cn/download" target="_blank"
+                >直播推荐使用OBS进行推流,点此下载</a
+              >
+            </div>
+          </h3>
 
           <el-card>
             <live-list v-model="postForm.liveList" />
@@ -196,13 +243,11 @@
 
       <el-row>
         <el-col>
-
           <h3>课件列表</h3>
 
           <el-card>
             <dir-list v-model="postForm.dirList" />
           </el-card>
-
         </el-col>
       </el-row>
 
@@ -210,23 +255,20 @@
         <el-button type="primary" @click="submitForm">保存</el-button>
         <el-button type="info" @click="onCancel">返回</el-button>
       </div>
-
     </el-form>
-
   </div>
 </template>
 
 <script>
-
-import { fetchDetail, saveData } from '@/api/course/course'
-import DicCatalogTree from '@/components/DicTreeSelect'
-import Tinymce from '@/components/Tinymce'
-import FileUpload from '@/components/FileUpload'
-import UserRefs from '@/components/UserRefs'
-import DepartRefs from '@/components/DepartRefs'
-import LiveList from '@/views/admin/course/live'
-import DirList from '@/views/admin/course/file/dir'
-import TimeRangePicker from '@/components/TimeRangePicker'
+import { fetchDetail, saveData } from "@/api/course/course";
+import DicCatalogTree from "@/components/DicTreeSelect";
+import Tinymce from "@/components/Tinymce";
+import FileUpload from "@/components/FileUpload";
+import UserRefs from "@/components/UserRefs";
+import DepartRefs from "@/components/DepartRefs";
+import LiveList from "@/views/admin/course/live";
+import DirList from "@/views/admin/course/file/dir";
+import TimeRangePicker from "@/components/TimeRangePicker";
 
 export default {
   components: {
@@ -237,7 +279,7 @@ export default {
     UserRefs,
     FileUpload,
     Tinymce,
-    DicCatalogTree
+    DicCatalogTree,
   },
   data() {
     return {
@@ -246,11 +288,11 @@ export default {
       dateValues: [],
       curIndex: 0,
       postForm: {
-        content: '',
-        catId: '',
+        content: "",
+        catId: "",
         videoDrag: false,
         stepLock: false,
-        lecturerId: '',
+        lecturerId: "",
         periods: 0,
         isMust: false,
         openType: 1,
@@ -258,82 +300,76 @@ export default {
         points: 0,
         dirList: [],
         personList: [],
-        liveList: []
+        liveList: [],
       },
       tableData: [
-        { title: '默认文件夹', fileList: [{ title: '第一个文件夹', fileType: '视频文件' }] }
+        {
+          title: "默认文件夹",
+          fileList: [{ title: "第一个文件夹", fileType: "视频文件" }],
+        },
       ],
       loading: false,
       rules: {
+        title: [{ required: true, message: "课程名称不能为空!" }],
 
-        title: [
-          { required: true, message: '课程名称不能为空!' }
-        ],
-
-        content: [
-          { required: true, message: '课程描述不能为空!' }
-        ]
-      }
-    }
+        content: [{ required: true, message: "课程描述不能为空!" }],
+      },
+    };
   },
 
   watch: {
     dateValues: {
       handler() {
-        this.postForm.startTime = this.dateValues[0]
-        this.postForm.endTime = this.dateValues[1]
-      }
-    }
+        this.postForm.startTime = this.dateValues[0];
+        this.postForm.endTime = this.dateValues[1];
+      },
+    },
   },
 
   created() {
-    const id = this.$route.params.id
+    const id = this.$route.params.id;
     if (id !== undefined) {
-      this.fetchData(id)
+      this.fetchData(id);
     }
   },
   methods: {
-
     fetchData(id) {
-      fetchDetail(id).then(response => {
-        this.postForm = response.data
+      fetchDetail(id).then((response) => {
+        this.postForm = response.data;
         if (this.postForm.startTime && this.postForm.endTime) {
-          this.dateValues[0] = this.postForm.startTime
-          this.dateValues[1] = this.postForm.endTime
+          this.dateValues[0] = this.postForm.startTime;
+          this.dateValues[1] = this.postForm.endTime;
         }
-        this.$refs.editor.setContent(this.postForm.content)
-      })
+        this.$refs.editor.setContent(this.postForm.content);
+      });
     },
     submitForm() {
-      console.log(JSON.stringify(this.postForm))
-
       this.$refs.postForm.validate((valid) => {
         if (!valid) {
-          return
+          return;
         }
 
         saveData(this.postForm).then(() => {
           this.$notify({
-            title: '成功',
-            message: '课程保存成功!',
-            type: 'success',
-            duration: 2000
-          })
-
-          this.$router.push({ name: 'ListCourse' })
-        })
-      })
+            title: "成功",
+            message: "课程保存成功!",
+            type: "success",
+            duration: 2000,
+          });
+
+          this.$router.push({ name: "ListCourse" });
+        });
+      });
     },
 
     onCancel() {
-      this.$router.push({ name: 'ListCourse' })
+      this.$router.push({ name: "ListCourse" });
     },
 
     // 部门开放选择
     treeChange(codes) {
-      this.postForm.deptCodes = codes
-    }
-
-  }
-}
+      this.postForm.deptCodes = codes;
+    },
+  },
+};
 </script>

+ 65 - 86
exam-06173-vue/src/views/admin/course/index.vue

@@ -1,37 +1,39 @@
 <template>
-
-  <data-table
-    ref="pagingTable"
-    :options="options"
-    :list-query="listQuery"
-  >
-
+  <data-table ref="pagingTable" :options="options" :list-query="listQuery">
     <template slot="filter-content">
-
-      <dic-list-select v-model="listQuery.params.isMust" dic-code="course_need" />
-      <dic-catalog-tree v-model="listQuery.params.catId" dic-code="course_catalog" width="200" class="filter-item" style="margin-top: 3px" />
-      <el-input v-model="listQuery.params.title" placeholder="搜索课程" style="width: 200px;" class="filter-item" />
+      <dic-list-select
+        v-model="listQuery.params.isMust"
+        dic-code="course_need"
+        class="filter-item"
+      />
+      <dic-catalog-tree
+        v-model="listQuery.params.catId"
+        dic-code="course_catalog"
+        width="200"
+        class="filter-item"
+        style="margin-top: 3px"
+      />
+      <el-input
+        v-model="listQuery.params.title"
+        placeholder="搜索课程"
+        style="width: 200px"
+        class="filter-item"
+      />
     </template>
 
     <template slot="data-columns">
-
-      <el-table-column
-        label="课程名称"
-        prop="title"
-        show-overflow-tooltip
-      >
-
+      <el-table-column label="课程名称" prop="title" show-overflow-tooltip>
         <template slot-scope="scope">
-          <detail-link :id="scope.row.id" :title="scope.row.title" to="UpdateCourse" permission="course:update" />
+          <detail-link
+            :id="scope.row.id"
+            :title="scope.row.title"
+            to="UpdateCourse"
+            permission="course:update"
+          />
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="分类"
-        prop="catId_dictText"
-        align="center"
-      />
+      <el-table-column label="分类" prop="catId_dictText" align="center" />
 
       <el-table-column
         label="开放类型"
@@ -39,107 +41,84 @@
         align="center"
       />
 
-      <el-table-column
-        label="课时"
-        prop="periods"
-        align="center"
-      />
+      <el-table-column label="课时" prop="periods" align="center" />
 
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-      />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
 
-      <el-table-column
-        align="center"
-        label="状态"
-        prop="state_dictText"
-      />
-
-      <el-table-column
-        label="操作"
-        align="center"
-        width="160px"
-      >
+      <el-table-column align="center" label="状态" prop="state_dictText" />
 
+      <el-table-column label="操作" align="center" width="160px">
         <template slot-scope="scope">
-
-          <router-link :to="{ name: 'CourseStat', params: { courseId: scope.row.id }}" style="margin-left: 10px">
+          <router-link
+            :to="{ name: 'CourseStat', params: { courseId: scope.row.id } }"
+            style="margin-left: 10px"
+          >
             <el-link type="primary" icon="el-icon-pie-chart">统计分析</el-link>
           </router-link>
-
         </template>
-
       </el-table-column>
-
     </template>
-
   </data-table>
-
 </template>
 
 <script>
-import DataTable from '@/components/DataTable'
-import DicCatalogTree from '@/components/DicTreeSelect'
-import DicListSelect from '@/components/DicListSelect'
-import DetailLink from '@/components/DetailLink'
+import DataTable from "@/components/DataTable";
+import DicCatalogTree from "@/components/DicTreeSelect";
+import DicListSelect from "@/components/DicListSelect";
+import DetailLink from "@/components/DetailLink";
 
 export default {
-  name: 'QuList',
+  name: "QuList",
   components: { DetailLink, DicListSelect, DicCatalogTree, DataTable },
   data() {
     return {
-
       listQuery: {
         current: 1,
         size: 10,
         params: {
-          title: ''
-        }
+          title: "",
+        },
       },
 
       options: {
-
         // 可批量操作
         multi: true,
 
         add: {
           enable: !this.selectMode,
-          permission: 'course:add',
-          router: { name: 'AddCourse' }
+          permission: "course:add",
+          router: { name: "AddCourse" },
         },
         edit: {
           enable: true,
-          permission: 'course:update',
-          router: { name: 'UpdateCourse', params: { id: '$id' }}
+          permission: "course:update",
+          router: { name: "UpdateCourse", params: { id: "$id" } },
         },
         delete: {
           enable: true,
-          permission: 'course:delete',
-          url: '/api/course/course/delete'
+          permission: "course:delete",
+          url: "/api/course/course/delete",
         },
         // 列表请求URL
-        listUrl: '/api/course/course/paging',
+        listUrl: "/api/course/course/paging",
         // 批量操作列表
         multiActions: [
           {
-            value: 'enable',
-            label: '启用',
-            permission: 'course:update',
-            url: '/api/course/course/state'
-          }, {
-            value: 'disable',
-            label: '禁用',
-            permission: 'course:update',
-            url: '/api/course/course/state'
-          }
-        ]
-      }
-    }
+            value: "enable",
+            label: "启用",
+            permission: "course:update",
+            url: "/api/course/course/state",
+          },
+          {
+            value: "disable",
+            label: "禁用",
+            permission: "course:update",
+            url: "/api/course/course/state",
+          },
+        ],
+      },
+    };
   },
-  methods: {
-
-  }
-}
+  methods: {},
+};
 </script>

+ 281 - 209
exam-06173-vue/src/views/admin/exam/exam/form.vue

@@ -1,36 +1,33 @@
 <template>
   <div>
-
     <el-steps :active="step" simple>
       <el-step title="创建/选择试卷" icon="el-icon-s-claim" />
       <el-step title="考试配置" icon="el-icon-s-ticket" />
       <el-step title="发布考试" icon="el-icon-s-promotion" />
     </el-steps>
 
-    <div v-if="step===1">
-
+    <div v-if="step === 1">
       <h3>选择/创建试卷</h3>
       <el-card>
-
         <el-radio v-model="checkType" :label="1">选择已有试卷创建考试</el-radio>
 
-        <div v-if="checkType===1">
+        <div v-if="checkType === 1">
           <tmpl-list :select-mode="true" @select="handleBack" />
         </div>
-
       </el-card>
 
       <el-card style="margin-top: 20px">
         <el-radio v-model="checkType" :label="2">新建试卷并创建考试</el-radio>
-        <div v-if="checkType===2" style="padding-top: 20px">
+        <div v-if="checkType === 2" style="padding-top: 20px">
           <tmpl-pre-create @confirm="handleCreate" />
         </div>
       </el-card>
-
     </div>
 
-    <div v-if="step===2" style="margin-left: -20px; margin-right: -20px; padding-top: 20px">
-
+    <div
+      v-if="step === 2"
+      style="margin-left: -20px; margin-right: -20px; padding-top: 20px"
+    >
       <join1-form
         :title="createForm.title"
         :cat-id="createForm.catId"
@@ -38,20 +35,21 @@
         :time-type="createForm.timeType"
         @back="handleBack"
       />
-
     </div>
 
-    <div v-if="step===3">
-
-      <el-form ref="postForm" :model="postForm" :rules="rules" label-position="left" label-width="100px">
-
+    <div v-if="step === 3">
+      <el-form
+        ref="postForm"
+        :model="postForm"
+        :rules="rules"
+        label-position="left"
+        label-width="100px"
+      >
         <el-row :gutter="20">
           <el-col :span="18">
-
             <h3>考试设置</h3>
 
             <el-card>
-
               <el-row :gutter="50">
                 <el-col :span="12">
                   <el-form-item label="考试名称" prop="title">
@@ -60,13 +58,19 @@
                 </el-col>
                 <el-col :span="12">
                   <el-form-item label="考试类型" prop="examType">
-                    <dic-list-select v-model="postForm.examType" dic-code="exam_type" />
+                    <dic-list-select
+                      v-model="postForm.examType"
+                      dic-code="exam_type"
+                    />
                   </el-form-item>
                 </el-col>
 
                 <el-col :span="12">
                   <el-form-item label="及格分" prop="qualifyScore">
-                    <el-input-number v-model="postForm.qualifyScore" :max="tmplData.totalScore" />
+                    <el-input-number
+                      v-model="postForm.qualifyScore"
+                      :max="tmplData.totalScore"
+                    />
                   </el-form-item>
                 </el-col>
 
@@ -94,7 +98,9 @@
                 <el-col :span="12">
                   <el-form-item label="最低交卷时长">
                     <el-input-number v-model="postForm.handMin" />
-                    <tool-tip msg="最低答题分钟数,未达到不允许交卷,0为不开启" />
+                    <tool-tip
+                      msg="最低答题分钟数,未达到不允许交卷,0为不开启"
+                    />
                   </el-form-item>
                 </el-col>
 
@@ -111,9 +117,7 @@
                 </el-col>
 
                 <el-col v-if="postForm.timeLimit" :span="12">
-
                   <el-form-item label="考试时间设置">
-
                     <el-date-picker
                       v-model="dateValues"
                       style="margin-left: 10px"
@@ -124,14 +128,48 @@
                       start-placeholder="开始时间"
                       end-placeholder="结束时间"
                     />
-
                   </el-form-item>
                 </el-col>
 
                 <el-col v-if="postForm.timeLimit" :span="12">
                   <el-form-item label="允许迟到时长">
                     <el-input-number v-model="postForm.lateMax" />
-                    <tool-tip msg="允许迟到的分钟数,注意:某段时间都可以考试的长期考试请设置为0" />
+                    <tool-tip
+                      msg="允许迟到的分钟数,注意:某段时间都可以考试的长期考试请设置为0"
+                    />
+                  </el-form-item>
+                </el-col>
+
+                <el-col :span="24">
+                  <el-form-item label="是否关联培训">
+                    <div class="flex">
+                      <el-select
+                        style="width: 75%"
+                        v-model="postForm.courseId"
+                        placeholder="可选择或输入关键字搜索培训课程"
+                        filterable
+                        clearable
+                        @change="getTreeEnableArray"
+                      >
+                        <el-option
+                          v-for="item in courseArray"
+                          :key="item.id"
+                          :label="item.title"
+                          :value="item.id"
+                        >
+                          <span style="float: left">{{ item.title }}</span>
+                          <span
+                            style="
+                              float: right;
+                              color: #8492a6;
+                              font-size: 13px;
+                            "
+                            >{{ item.catId_dictText }}</span
+                          >
+                        </el-option>
+                      </el-select>
+                      <tool-tip msg="关联培训后,未完成学习将无法开始考试" />
+                    </div>
                   </el-form-item>
                 </el-col>
 
@@ -156,7 +194,7 @@
                   </el-form-item>
                 </el-col>
 
-                <el-col v-if="postForm.resultType!==3" :span="24">
+                <el-col v-if="postForm.resultType !== 3" :span="24">
                   <el-form-item label="考后感谢文字">
                     <el-input
                       v-model="postForm.thanks"
@@ -168,56 +206,57 @@
                 </el-col>
 
                 <el-col :span="24">
-
-                  <div style="background: #fbfbfb; padding: 10px; line-height: 36px; font-size: 14px">
-
-                    <div style="font-weight: 700">
-                      考试权限
-                    </div>
+                  <div
+                    style="
+                      background: #fbfbfb;
+                      padding: 10px;
+                      line-height: 36px;
+                      font-size: 14px;
+                    "
+                  >
+                    <div style="font-weight: 700">考试权限</div>
 
                     <el-radio-group v-model="postForm.openType" size="small">
-                      <el-radio :label="1">完全公开</el-radio>
+                      <!-- <el-radio :label="1">完全公开</el-radio> -->
                       <el-radio :label="2">部门开放</el-radio>
                       <el-radio :label="3">指定人员</el-radio>
                       <el-radio :label="9">口令密码</el-radio>
                     </el-radio-group>
 
                     <div style="padding-top: 20px">
-
-                      <div v-if="postForm.openType==1">
+                      <div v-if="postForm.openType == 1">
                         <el-alert
                           title="开放的,任何人都可以进行考试!"
                           type="warning"
                         />
                       </div>
 
-                      <div v-if="postForm.openType==2">
-                        <depart-refs v-model="postForm.deptCodes" />
+                      <div v-if="postForm.openType == 2">
+                        <depart-refs
+                          v-model="postForm.deptCodes"
+                          :enableArray="treeEnableArray"
+                        />
                       </div>
 
-                      <div v-if="postForm.openType==3">
+                      <div v-if="postForm.openType == 3">
                         <user-refs v-model="postForm.personList" />
                       </div>
 
-                      <div v-if="postForm.openType==9">
-                        <el-input v-model="postForm.password" placeholder="输入考试密码" />
+                      <div v-if="postForm.openType == 9">
+                        <el-input
+                          v-model="postForm.password"
+                          placeholder="输入考试密码"
+                        />
                       </div>
-
                     </div>
-
                   </div>
-
                 </el-col>
-
               </el-row>
-
             </el-card>
 
             <h3>防作弊选项</h3>
             <el-card>
-
               <el-row>
-
                 <el-col>
                   <el-form-item label="学员答题设备设置" label-width="200px">
                     <el-radio-group v-model="postForm.answerDevice">
@@ -229,7 +268,6 @@
                 </el-col>
 
                 <el-col>
-
                   <el-form-item label="人脸认证进入考试" label-width="200px">
                     <el-switch
                       v-model="postForm.faceOn"
@@ -239,42 +277,57 @@
 
                     &nbsp;开启后,需要人脸认证通过才可以进入考试
                   </el-form-item>
-
                 </el-col>
 
                 <el-col>
-
-                  <el-form-item label="答题时摄像头拍照监考" label-width="200px">
+                  <el-form-item
+                    label="答题时摄像头拍照监考"
+                    label-width="200px"
+                  >
                     <el-switch
                       v-model="postForm.camOn"
                       active-color="#13ce66"
                       inactive-color="#ff4949"
                     />
 
-                    <span v-if="postForm.camOn">&nbsp;学员开始答题后,每&nbsp;
-                      <el-input-number v-model="postForm.camInterval" :min="1" :max="30" label="秒数" size="mini" />
+                    <span v-if="postForm.camOn"
+                      >&nbsp;学员开始答题后,每&nbsp;
+                      <el-input-number
+                        v-model="postForm.camInterval"
+                        :min="1"
+                        :max="30"
+                        label="秒数"
+                        size="mini"
+                      />
                       &nbsp;分钟抓拍一张学员答题照片&nbsp;
                     </span>
                   </el-form-item>
-
                 </el-col>
 
                 <el-col v-if="postForm.camOn">
-
-                  <el-form-item label="考试过程定时人脸识别" label-width="200px">
+                  <el-form-item
+                    label="考试过程定时人脸识别"
+                    label-width="200px"
+                  >
                     <el-switch
                       v-model="postForm.faceCheck"
                       active-color="#13ce66"
                       inactive-color="#ff4949"
                     />
-                    对抓拍图片进行人脸识别,如果连续识别失败超过 <el-input-number v-model="postForm.faceChance" :min="0" :max="postForm.totalTime/postForm.camInterval" label="次数" size="mini" /> 次就强制交卷
+                    对抓拍图片进行人脸识别,如果连续识别失败超过
+                    <el-input-number
+                      v-model="postForm.faceChance"
+                      :min="0"
+                      :max="postForm.totalTime / postForm.camInterval"
+                      label="次数"
+                      size="mini"
+                    />
+                    次就强制交卷
                   </el-form-item>
-
                 </el-col>
 
                 <el-col>
                   <el-form-item label="切屏后强制交卷" label-width="200px">
-
                     <el-switch
                       v-model="postForm.leaveOn"
                       active-color="#13ce66"
@@ -282,14 +335,25 @@
                     />
                     <span v-if="postForm.leaveOn">
                       &nbsp;学员切换页面超过&nbsp;
-                      <el-input-number v-model="postForm.leaveCount" :min="1" :max="999" label="秒数" size="mini" />&nbsp;次后将被强制交卷,切换到其他页面&nbsp;
-                      <el-input-number v-model="postForm.leaveCheck" :min="5" :max="999" label="秒数" size="mini" />&nbsp;秒后即判定为切屏
+                      <el-input-number
+                        v-model="postForm.leaveCount"
+                        :min="1"
+                        :max="999"
+                        label="秒数"
+                        size="mini"
+                      />&nbsp;次后将被强制交卷,切换到其他页面&nbsp;
+                      <el-input-number
+                        v-model="postForm.leaveCheck"
+                        :min="5"
+                        :max="999"
+                        label="秒数"
+                        size="mini"
+                      />&nbsp;秒后即判定为切屏
                     </span>
                   </el-form-item>
                 </el-col>
 
                 <el-col>
-
                   <el-form-item label="无操作强制交卷" label-width="200px">
                     <el-switch
                       v-model="postForm.actionOn"
@@ -299,90 +363,92 @@
 
                     <span v-if="postForm.actionOn">
                       &nbsp;答题时超过&nbsp;
-                      <el-input-number v-model="postForm.actionInterval" :min="1" :max="999" label="秒数" size="mini" />
+                      <el-input-number
+                        v-model="postForm.actionInterval"
+                        :min="1"
+                        :max="999"
+                        label="秒数"
+                        size="mini"
+                      />
                       &nbsp;秒没有新操作会强制交卷&nbsp;
                     </span>
-
                   </el-form-item>
                 </el-col>
-
               </el-row>
-
             </el-card>
-
           </el-col>
 
           <el-col :span="6">
             <h3>试卷信息</h3>
             <el-card style="font-size: 14px; line-height: 28px">
-
-              <div>
-                试卷标题:<el-link :href="`/#/admin/tmpl/preview/${tmplData.id}`" type="primary" target="_blank">{{ tmplData.title }}</el-link>
-              </div>
-              <div>
-                试卷总分:{{ tmplData.totalScore }}
-              </div>
-              <div>
-                试题数量: {{ tmplData.quCount }}
-              </div>
               <div>
-                出卷人员:{{ tmplData.createBy_dictText }}
+                试卷标题:<el-link
+                  :href="`/#/admin/tmpl/preview/${tmplData.id}`"
+                  type="primary"
+                  target="_blank"
+                  >{{ tmplData.title }}</el-link
+                >
               </div>
-              <div>
-                创建时间:{{ tmplData.createTime }}
-              </div>
-
+              <div>试卷总分:{{ tmplData.totalScore }}</div>
+              <div>试题数量: {{ tmplData.quCount }}</div>
+              <div>出卷人员:{{ tmplData.createBy_dictText }}</div>
+              <div>创建时间:{{ tmplData.createTime }}</div>
             </el-card>
           </el-col>
         </el-row>
 
-        <el-button type="primary" style="margin-top: 20px" @click="nextStep">保存</el-button>
-
+        <el-button type="primary" style="margin-top: 20px" @click="nextStep"
+          >保存</el-button
+        >
       </el-form>
-
     </div>
-
   </div>
 </template>
 
 <script>
-import { fetchDetail, saveData } from '@/api/exam/exam'
-import DicListSelect from '@/components/DicListSelect'
-import ToolTip from '@/components/ToolTip'
-import TmplPreCreate from '@/views/admin/tmpl/components/components/TmplPreCreate'
-import Join1Form from '@/views/admin/tmpl/components/Join1Form'
-import TmplList from '@/views/admin/tmpl'
-import { apiFindSimple } from '@/api/tmpl/tmpl'
-import UserRefs from '@/components/UserRefs'
-import DepartRefs from '@/components/DepartRefs'
+import { fetchDetail, saveData } from "@/api/exam/exam";
+import { getTableData, getTreeEnable } from "@/api/exam/form";
+import DicListSelect from "@/components/DicListSelect";
+import ToolTip from "@/components/ToolTip";
+import TmplPreCreate from "@/views/admin/tmpl/components/components/TmplPreCreate";
+import Join1Form from "@/views/admin/tmpl/components/Join1Form";
+import TmplList from "@/views/admin/tmpl";
+import { apiFindSimple } from "@/api/tmpl/tmpl";
+import UserRefs from "@/components/UserRefs";
+import DepartRefs from "@/components/DepartRefs";
 
 export default {
-  name: 'ExamDetail',
-  components: { DepartRefs, UserRefs, TmplList, Join1Form, TmplPreCreate, ToolTip, DicListSelect },
+  name: "ExamDetail",
+  components: {
+    DepartRefs,
+    UserRefs,
+    TmplList,
+    Join1Form,
+    TmplPreCreate,
+    ToolTip,
+    DicListSelect,
+  },
   data() {
     return {
-
       step: 1,
       checkType: 1,
 
-      tmplData: {
-
-      },
+      tmplData: {},
 
       createForm: {
         data: {
-          totalScore: 0
+          totalScore: 0,
         },
         title: null,
         joinType: 0,
         catId: null,
-        timeType: 0
+        timeType: 0,
       },
 
       dateValues: [],
 
       postForm: {
-        title: '',
+        title: "",
         // 试卷ID
         tmplId: null,
         // 考试部门列表
@@ -405,161 +471,167 @@ export default {
         // 是否截屏
         isCapture: true,
         resultType: 2,
-        thanks: '感谢您的作答!',
+        thanks: "感谢您的作答!",
         personList: [],
         faceOn: false,
         faceCheck: false,
         faceInterval: 0,
-        faceChance: 0
-
+        faceChance: 0,
+        courseId: "",
       },
       rules: {
-        title: [
-          { required: true, message: '考试名称不能为空!' }
-        ],
-
-        open: [
-          { required: true, message: '考试权限不能为空!' }
-        ],
-
-        qualifyScore: [
-          { required: true, message: '及格分不能为空!' }
-        ],
-
-        totalTime: [
-          { required: true, message: '考试时间不能为空!' }
-        ],
-
-        password: [
-          { required: true, message: '考试口令不能为空!' }
-        ],
-        chance: [
-          { required: true, message: '考试次数不能为空,0为不限制!' }
-        ],
-        examType: [
-          { required: true, message: '考试类型必须选择' }
-        ]
-      }
-    }
+        title: [{ required: true, message: "考试名称不能为空!" }],
+
+        open: [{ required: true, message: "考试权限不能为空!" }],
+
+        qualifyScore: [{ required: true, message: "及格分不能为空!" }],
+
+        totalTime: [{ required: true, message: "考试时间不能为空!" }],
+
+        password: [{ required: true, message: "考试口令不能为空!" }],
+        chance: [{ required: true, message: "考试次数不能为空,0为不限制!" }],
+        examType: [{ required: true, message: "考试类型必须选择" }],
+      },
+      courseArray: [],
+      treeEnableArray: [],
+    };
   },
 
   watch: {
     dateValues: {
       handler() {
-        this.postForm.startTime = this.dateValues[0]
-        this.postForm.endTime = this.dateValues[1]
-      }
-    }
+        this.postForm.startTime = this.dateValues[0];
+        this.postForm.endTime = this.dateValues[1];
+      },
+    },
   },
   created() {
     // 选定创建试卷
-    const tmplId = this.$route.params.tmplId
-    if (tmplId !== undefined && tmplId !== '0') {
-      this.handleBack(tmplId)
-      return
+    const tmplId = this.$route.params.tmplId;
+    if (tmplId !== undefined && tmplId !== "0") {
+      this.handleBack(tmplId);
+      return;
     }
 
-    const id = this.$route.params.id
+    const id = this.$route.params.id;
     if (id !== undefined) {
-      this.fetchData(id)
+      this.fetchData(id);
     } else {
-      this.postForm.openType = 1
-      this.postForm.joinType = 1
+      this.postForm.openType = 1;
+      this.postForm.joinType = 1;
     }
+
+    getTableData().then(({ data }) => {
+      this.courseArray = data.records;
+    });
   },
   methods: {
-
+    getTreeEnableArray(courseId) {
+      getTreeEnable({ courseId }).then((res) => {
+        let treeEnableArray = [];
+        res.data.forEach((ele) => {
+          treeEnableArray.push(ele.deptCode);
+        });
+        this.treeEnableArray = treeEnableArray;
+      });
+    },
     nextStep() {
       this.$refs.postForm.validate((valid) => {
         if (!valid) {
-          return
+          return;
         }
-        this.$confirm('确实要提交保存吗?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning'
+        this.$confirm("确实要提交保存吗?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
         }).then(() => {
-          this.submitForm()
-        })
-      })
+          this.submitForm();
+        });
+      });
     },
 
     fetchData(id) {
-      fetchDetail(id).then(response => {
-        this.postForm = response.data
-        this.dateValues[0] = this.postForm.startTime
-        this.dateValues[1] = this.postForm.endTime
-        this.handleBack(this.postForm.tmplId)
-      })
+      fetchDetail(id).then((response) => {
+        this.postForm = response.data;
+        this.dateValues[0] = this.postForm.startTime;
+        this.dateValues[1] = this.postForm.endTime;
+        this.handleBack(this.postForm.tmplId);
+        if(response.data.courseId){
+          this.getTreeEnableArray(response.data.courseId);
+        }
+      });
     },
 
     submitForm() {
       saveData(this.postForm).then(() => {
         this.$notify({
-          title: '成功',
-          message: '考试保存成功!',
-          type: 'success',
-          duration: 2000
-        })
-
-        this.$router.push({ name: 'ListExam' })
-      })
+          title: "成功",
+          message: "考试保存成功!",
+          type: "success",
+          duration: 2000,
+        });
+
+        this.$router.push({ name: "ListExam" });
+      });
     },
 
     handleCreate(data) {
-      this.createForm = data
-      this.step = 2
+      this.createForm = data;
+      this.step = 2;
     },
 
     handleBack(id) {
-      this.step = 3
-      this.postForm.tmplId = id
-      apiFindSimple(id).then(res => {
-        this.tmplData = res.data
+      this.step = 3;
+      this.postForm.tmplId = id;
+      apiFindSimple(id).then((res) => {
+        this.tmplData = res.data;
         if (!this.postForm.title) {
-          this.postForm.title = res.data.title + ' - 考试'
+          this.postForm.title = res.data.title + " - 考试";
         }
 
         if (!this.postForm.qualifyScore) {
-          this.postForm.qualifyScore = res.data.totalScore * 0.6
+          this.postForm.qualifyScore = res.data.totalScore * 0.6;
         }
-      })
-    }
-
-  }
-}
+      });
+    },
+  },
+};
 </script>
 
 <style scoped>
+.score {
+  float: right;
+  font-weight: bold;
+  color: #ff0000;
+}
 
-  .score{
-    float: right; font-weight: bold; color: #ff0000
-  }
-
-  small{
-    color: #666;
-    line-height: 30px;
-    margin-left: 10px;
-  }
-
-  ::v-deep
-  .left-box{
-    height: calc(100vh - 170px);
-    position: fixed;
-    overflow: auto;
-    clear: both;
-  }
-
-  ::v-deep
-  .left-box-normal{
-    top: 170px;
-  }
-
-  ::v-deep
-  .left-box-scrolled{
-    top: 20px;
-    height: calc(100vh - 20px);
-  }
+small {
+  color: #666;
+  line-height: 30px;
+  margin-left: 10px;
+}
+
+.flex {
+  width: 100%;
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+}
 
+::v-deep .left-box {
+  height: calc(100vh - 170px);
+  position: fixed;
+  overflow: auto;
+  clear: both;
+}
+
+::v-deep .left-box-normal {
+  top: 170px;
+}
+
+::v-deep .left-box-scrolled {
+  top: 20px;
+  height: calc(100vh - 20px);
+}
 </style>
 

+ 72 - 94
exam-06173-vue/src/views/admin/exam/exam/index.vue

@@ -1,15 +1,19 @@
 <template>
-
-  <data-table
-    ref="pagingTable"
-    :options="options"
-    :list-query="listQuery"
-  >
+  <data-table ref="pagingTable" :options="options" :list-query="listQuery">
     <template slot="filter-content">
+      <dic-list-select
+        v-model="listQuery.params.examType"
+        dic-code="exam_type"
+        title="选择考试类型"
+        class="filter-item"
+      />
 
-      <dic-list-select v-model="listQuery.params.examType" dic-code="exam_type" title="选择考试类型" />
-
-      <el-select v-model="listQuery.params.openType" class="filter-item" placeholder="开放类型" clearable>
+      <el-select
+        v-model="listQuery.params.openType"
+        class="filter-item"
+        placeholder="开放类型"
+        clearable
+      >
         <el-option
           v-for="item in openTypes"
           :key="item.value"
@@ -34,58 +38,45 @@
         placeholder="考试结束时间"
       />
 
-      <el-input v-model="listQuery.params.title" placeholder="搜索考试名称" style="width: 200px;" class="filter-item" />
-
+      <el-input
+        v-model="listQuery.params.title"
+        placeholder="搜索考试名称"
+        style="width: 200px"
+        class="filter-item"
+      />
     </template>
 
     <template slot="data-columns">
-
-      <el-table-column
-        label="考试名称"
-        prop="title"
-        show-overflow-tooltip
-      >
-
+      <el-table-column label="考试名称" prop="title" show-overflow-tooltip>
         <template slot-scope="scope">
-          <detail-link :id="scope.row.id" :title="scope.row.title" to="UpdateExam" permission="exam:update" />
+          <detail-link
+            :id="scope.row.id"
+            :title="scope.row.title"
+            to="UpdateExam"
+            permission="exam:update"
+          />
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="考试类型"
-        align="center"
-        width="120px"
-      >
+      <el-table-column label="考试类型" align="center" width="120px">
         <template slot-scope="scope">
           {{ scope.row.examType_dictText }}
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="开放权限"
-        align="center"
-        width="120px"
-      >
+      <el-table-column label="开放权限" align="center" width="120px">
         <template slot-scope="scope">
           {{ scope.row.openType | examOpenType }}
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="考试时间"
-        align="center"
-      >
-
+      <el-table-column label="考试时间" align="center">
         <template slot-scope="scope">
           <span v-if="scope.row.timeLimit">
             {{ scope.row.startTime }} ~ {{ scope.row.endTime }}
           </span>
           <span v-else>不限时</span>
         </template>
-
       </el-table-column>
 
       <el-table-column
@@ -102,121 +93,108 @@
         width="100px"
       />
 
-
-
-      <el-table-column
-        label="状态"
-        align="center"
-        width="120px"
-      >
-
+      <el-table-column label="状态" align="center" width="120px">
         <template slot-scope="scope">
           {{ scope.row.state | stateFilter }}
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="操作"
-        align="center"
-        width="200px"
-      >
+      <el-table-column label="操作" align="center" width="200px">
         <template slot-scope="scope">
-          <router-link :to="{ name: 'ExamStat', params: { examId: scope.row.id }}" style="margin-left: 10px">
+          <router-link
+            :to="{ name: 'ExamStat', params: { examId: scope.row.id } }"
+            style="margin-left: 10px"
+          >
             <el-link type="primary" icon="el-icon-pie-chart">统计分析</el-link>
           </router-link>
 
-          <router-link :to="{ name: 'ExamWatch', params: { examId: scope.row.id }}" style="margin-left: 10px">
+          <router-link
+            :to="{ name: 'ExamWatch', params: { examId: scope.row.id } }"
+            style="margin-left: 10px"
+          >
             <el-link type="primary" icon="el-icon-view">监考</el-link>
           </router-link>
-
         </template>
       </el-table-column>
-
     </template>
-
   </data-table>
-
 </template>
 
 <script>
-import DataTable from '@/components/DataTable'
-import DicListSelect from '@/components/DicListSelect'
-import permission from '@/directive/permission/index.js'
-import DetailLink from '@/components/DetailLink' // 权限判断指令
+import DataTable from "@/components/DataTable";
+import DicListSelect from "@/components/DicListSelect";
+import permission from "@/directive/permission/index.js";
+import DetailLink from "@/components/DetailLink"; // 权限判断指令
 
 export default {
-  name: 'ExamList',
+  name: "ExamList",
   components: { DetailLink, DicListSelect, DataTable },
   directives: { permission },
   data() {
     return {
-
       openTypes: [
-        {
-          value: 1,
-          label: '完全开放'
-        },
+        // {
+        //   value: 1,
+        //   label: "完全开放",
+        // },
         {
           value: 2,
-          label: '指定部门'
+          label: "指定部门",
         },
         {
           value: 3,
-          label: '指定人员'
+          label: "指定人员",
         },
         {
           value: 9,
-          label: '口令密码'
-        }
+          label: "口令密码",
+        },
       ],
 
       listQuery: {
         current: 1,
         size: 10,
         params: {
-          title: ''
-        }
+          title: "",
+        },
       },
 
       options: {
         add: {
           enable: true,
-          permission: 'exam:add',
-          router: { name: 'AddExam', params: { tmplId: '0' }}
+          permission: "exam:add",
+          router: { name: "AddExam", params: { tmplId: "0" } },
         },
         edit: {
           enable: true,
-          permission: 'exam:update',
-          router: { name: 'UpdateExam', params: { id: '$id' }}
+          permission: "exam:update",
+          router: { name: "UpdateExam", params: { id: "$id" } },
         },
         delete: {
           enable: true,
-          permission: 'exam:delete',
-          url: '/api/exam/exam/delete'
+          permission: "exam:delete",
+          url: "/api/exam/exam/delete",
         },
         // 可批量操作
         multi: true,
         // 批量操作列表
         multiActions: [
           {
-            value: 'enable',
-            label: '启用',
-            url: '/api/exam/exam/state'
+            value: "enable",
+            label: "启用",
+            url: "/api/exam/exam/state",
           },
           {
-            value: 'disable',
-            label: '禁用',
-            url: '/api/exam/exam/state'
-          }
+            value: "disable",
+            label: "禁用",
+            url: "/api/exam/exam/state",
+          },
         ],
         // 列表请求URL
-        listUrl: '/api/exam/exam/paging'
-      }
-    }
+        listUrl: "/api/exam/exam/paging",
+      },
+    };
   },
-  methods: {
-
-  }
-}
+  methods: {},
+};
 </script>

+ 40 - 65
exam-06173-vue/src/views/admin/exam/review/index.vue

@@ -1,119 +1,94 @@
 <template>
-
-  <data-table
-    ref="pagingTable"
-    :options="options"
-    :list-query="listQuery"
-  >
+  <data-table ref="pagingTable" :options="options" :list-query="listQuery">
     <template slot="filter-content">
+      <dic-list-select
+        v-model="listQuery.params.examType"
+        dic-code="exam_type"
+        title="选择考试类型"
+        class="filter-item"
+      />
 
-      <dic-list-select v-model="listQuery.params.examType" dic-code="exam_type" title="选择考试类型" />
-
-      <el-input v-model="listQuery.params.title" placeholder="搜索考试名称" style="width: 200px;" class="filter-item" />
-
+      <el-input
+        v-model="listQuery.params.title"
+        placeholder="搜索考试名称"
+        style="width: 200px"
+        class="filter-item"
+      />
     </template>
 
     <template slot="data-columns">
-
-      <el-table-column
-        label="考试名称"
-        show-overflow-tooltip
-      >
-
+      <el-table-column label="考试名称" show-overflow-tooltip>
         <template slot-scope="scope">
-          <detail-link :id="scope.row.id" :title="scope.row.title" @click="toReview" />
+          <detail-link
+            :id="scope.row.id"
+            :title="scope.row.title"
+            @click="toReview"
+          />
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="考试类型"
-        align="center"
-      >
+      <el-table-column label="考试类型" align="center">
         <template slot-scope="scope">
           {{ scope.row.openType | examOpenType }}
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="考试时间"
-        width="220px"
-        align="center"
-      >
-
+      <el-table-column label="考试时间" width="220px" align="center">
         <template slot-scope="scope">
           <span v-if="scope.row.timeLimit">
             {{ scope.row.startTime }} ~ {{ scope.row.endTime }}
           </span>
           <span v-else>不限时</span>
         </template>
-
       </el-table-column>
 
-      <el-table-column
-        label="考试人数"
-        prop="examUser"
-        align="center"
-      />
+      <el-table-column label="考试人数" prop="examUser" align="center" />
 
-      <el-table-column
-        label="待阅试卷"
-        prop="unreadPaper"
-        align="center"
-      />
+      <el-table-column label="待阅试卷" prop="unreadPaper" align="center" />
 
-      <el-table-column
-        label="操作"
-        align="center"
-      >
+      <el-table-column label="操作" align="center">
         <template slot-scope="scope">
-
-          <router-link :to="{ name: 'ReviewPaper', params: { examId: scope.row.id }}">
+          <router-link
+            :to="{ name: 'ReviewPaper', params: { examId: scope.row.id } }"
+          >
             <el-link type="primary" icon="el-icon-search">批阅试卷</el-link>
           </router-link>
-
         </template>
-
       </el-table-column>
-
     </template>
-
   </data-table>
-
 </template>
 
 <script>
-import DataTable from '@/components/DataTable'
-import DicListSelect from '@/components/DicListSelect'
-import DetailLink from '@/components/DetailLink'
+import DataTable from "@/components/DataTable";
+import DicListSelect from "@/components/DicListSelect";
+import DetailLink from "@/components/DetailLink";
 
 export default {
-  name: 'ReviewList',
+  name: "ReviewList",
   components: { DetailLink, DicListSelect, DataTable },
   data() {
     return {
-
       listQuery: {
         current: 1,
         size: 10,
         params: {
-          name: ''
-        }
+          name: "",
+        },
       },
 
       options: {
         // 可批量操作
         multi: false,
         // 列表请求URL
-        listUrl: '/api/exam/exam/review-paging'
-      }
-    }
+        listUrl: "/api/exam/exam/review-paging",
+      },
+    };
   },
   methods: {
     toReview(id) {
-      this.$router.push({ name: 'ReviewPaper', params: { examId: id }})
-    }
-  }
-}
+      this.$router.push({ name: "ReviewPaper", params: { examId: id } });
+    },
+  },
+};
 </script>

+ 323 - 226
exam-06173-vue/src/views/admin/paper/paper/detail.vue

@@ -1,108 +1,202 @@
 <template>
   <div v-loading="loading">
-
     <div class="all-box">
-
       <div class="all-box-left">
-
-        <el-card v-if="!loading" :class="{'left-box':true, 'left-box-normal': !scrolled, 'left-box-scrolled': scrolled}" style="width: 250px">
-
-          <div v-for="item in postForm.groupList" :key="item.id" class="group-card">
-            <div v-if="(onlySubj && (item.quType==='4' || item.quType==='99')) || !onlySubj">
+        <el-card
+          v-if="!loading"
+          :class="{
+            'left-box': true,
+            'left-box-normal': !scrolled,
+            'left-box-scrolled': scrolled,
+          }"
+          style="width: 250px"
+        >
+          <div
+            v-for="item in postForm.groupList"
+            :key="item.id"
+            class="group-card"
+          >
+            <div
+              v-if="
+                (onlySubj && (item.quType === '4' || item.quType === '99')) ||
+                !onlySubj
+              "
+            >
               <div style="line-height: 40px">
-                <el-button style="font-size: 14px; font-weight: 700" type="text" @click="goAnchor(item.id)">{{ item.title }}</el-button>
+                <el-button
+                  style="font-size: 14px; font-weight: 700"
+                  type="text"
+                  @click="goAnchor(item.id)"
+                  >{{ item.title }}</el-button
+                >
                 <el-divider />
               </div>
               <div class="group-card-body">
                 <div>
-                  共 <i class="num">{{ item.quCount }}</i> 题,共 <i class="num">{{ item.totalScore }}</i> 分
+                  共 <i class="num">{{ item.quCount }}</i> 题,共
+                  <i class="num">{{ item.totalScore }}</i> 分
                 </div>
               </div>
 
               <div class="card-box">
-                <div v-for="card in item.quList" :key="card.id" class="item" @click="goAnchor(card.id)">
-                  <div :class="{num:true, right: card.isRight, error: !card.isRight, 'current': postForm.state==1 && card.quType=='4'}">{{ card.sort }}</div>
+                <div
+                  v-for="card in item.quList"
+                  :key="card.id"
+                  class="item"
+                  @click="goAnchor(card.id)"
+                >
+                  <div
+                    :class="{
+                      num: true,
+                      right: card.isRight,
+                      error: !card.isRight,
+                      current: postForm.state == 1 && card.quType == '4',
+                    }"
+                  >
+                    {{ card.sort }}
+                  </div>
                 </div>
               </div>
-
             </div>
-
           </div>
-
         </el-card>
-
       </div>
 
       <div class="all-box-center">
-
         <template v-for="item in postForm.groupList">
-
-          <el-card v-if="(onlySubj && (item.quType==='4' || item.quType==='99')) || !onlySubj" :id="item.id" :key="item.id" class="content-card">
-
-            <template v-for="(qu,index) in item.quList">
+          <el-card
+            v-if="
+              (onlySubj && (item.quType === '4' || item.quType === '99')) ||
+              !onlySubj
+            "
+            :id="item.id"
+            :key="item.id"
+            class="content-card"
+          >
+            <template v-for="(qu, index) in item.quList">
               <div v-if="!onlyError || (onlyError && !qu.isRight)" :key="qu.id">
                 <div :id="qu.id" class="list-box">
                   <div class="list-qu">
-                    <qu-item-show :value="qu" :index="index" :state="postForm.state" />
+                    <qu-item-show
+                      :value="qu"
+                      :index="index"
+                      :state="postForm.state"
+                    />
 
                     <div v-if="qu.quType === '99'">
-
                       <!-- 子题目的判卷 -->
-                      <div v-for="(sub,jj) in qu.subList" :key="sub.id" class="list-sub-box">
+                      <div
+                        v-for="(sub, jj) in qu.subList"
+                        :key="sub.id"
+                        class="list-sub-box"
+                      >
                         <div class="list-sub-qu">
-                          <qu-item-show :value="sub" :index="jj" :state="postForm.state" :show-type="true" :sub="true" />
+                          <qu-item-show
+                            :value="sub"
+                            :index="jj"
+                            :state="postForm.state"
+                            :show-type="true"
+                            :sub="true"
+                          />
                         </div>
 
                         <div class="list-sub-opt">
                           <div class="check-box">
-                            <div class="item">判题:
-                              <i :class="{'el-icon-success check-icon': true, 'check-icon-right':sub.isRight}" @click="checkAnswer(sub, true)" />
-                              <i :class="{'el-icon-error check-icon': true, 'check-icon-error':!sub.isRight}" @click="checkAnswer(sub, false)" />
+                            <div class="item">
+                              判题:
+                              <i
+                                :class="{
+                                  'el-icon-success check-icon': true,
+                                  'check-icon-right': sub.isRight,
+                                }"
+                                @click="checkAnswer(sub, true)"
+                              />
+                              <i
+                                :class="{
+                                  'el-icon-error check-icon': true,
+                                  'check-icon-error': !sub.isRight,
+                                }"
+                                @click="checkAnswer(sub, false)"
+                              />
+                            </div>
+                            <div class="item">
+                              得分:<el-input-number
+                                v-model="sub.actualScore"
+                                :precision="1"
+                                :max="sub.score"
+                                size="mini"
+                                style="width: 100px"
+                              />
                             </div>
-                            <div class="item">得分:<el-input-number v-model="sub.actualScore" :precision="1" :max="sub.score" size="mini" style="width: 100px" /></div>
                             <div class="item">总分:{{ sub.score }}</div>
                           </div>
                         </div>
-
                       </div>
-
                     </div>
                   </div>
 
                   <!-- 组合题不需要大题判卷 -->
-                  <div v-if="qu.quType!=='99'" class="list-opt">
-
+                  <div v-if="qu.quType !== '99'" class="list-opt">
                     <div class="check-box">
-                      <div class="item">判题:
-                        <i :class="{'el-icon-success check-icon': true, 'check-icon-right':qu.isRight}" @click="checkAnswer(qu, true)" />
-                        <i :class="{'el-icon-error check-icon': true, 'check-icon-error':!qu.isRight}" @click="checkAnswer(qu, false)" />
+                      <div class="item">
+                        判题:
+                        <i
+                          :class="{
+                            'el-icon-success check-icon': true,
+                            'check-icon-right': qu.isRight,
+                          }"
+                          @click="checkAnswer(qu, true)"
+                        />
+                        <i
+                          :class="{
+                            'el-icon-error check-icon': true,
+                            'check-icon-error': !qu.isRight,
+                          }"
+                          @click="checkAnswer(qu, false)"
+                        />
+                      </div>
+                      <div class="item">
+                        得分:<el-input-number
+                          v-model="qu.actualScore"
+                          :precision="1"
+                          :max="qu.score"
+                          size="mini"
+                          style="width: 100px"
+                        />
                       </div>
-                      <div class="item">得分:<el-input-number v-model="qu.actualScore" :precision="1" :max="qu.score" size="mini" style="width: 100px" /></div>
                       <div class="item">总分:{{ qu.score }}</div>
                     </div>
                   </div>
                 </div>
               </div>
             </template>
-
           </el-card>
-
         </template>
 
         <el-card>
-          <el-input v-model="postForm.remark" type="textarea" placeholder="请输入您的评语!" />
+          <el-input
+            v-model="postForm.remark"
+            type="textarea"
+            placeholder="请输入您的评语!"
+          />
         </el-card>
-
       </div>
 
       <div class="all-box-right">
-
-        <el-card v-if="!loading" :class="{'left-box':true, 'left-box-normal': !scrolled, 'left-box-scrolled': scrolled}" style="width: 200px">
+        <el-card
+          v-if="!loading"
+          :class="{
+            'left-box': true,
+            'left-box-normal': !scrolled,
+            'left-box-scrolled': scrolled,
+          }"
+          style="width: 200px"
+        >
           <div>人员姓名</div>
           <div style="color: #0a84ff">{{ postForm.userId_dictText }}</div>
           <el-divider />
           <div>考试成绩</div>
-          <div style="color: #FF4B50">{{ postForm.userScore }}</div>
+          <div style="color: #ff4b50">{{ postForm.userScore }}</div>
           <el-divider />
           <div>仅看错题</div>
           <div>
@@ -124,25 +218,28 @@
           <el-divider />
           <div>考试状态</div>
           <div>
-            <span v-if="postForm.userScore >= postForm.qualifyScore" style="color: #03DD6D">通过</span>
-            <span v-else style="color: #FF4B50">未通过</span>
+            <span
+              v-if="postForm.userScore >= postForm.qualifyScore"
+              style="color: #03dd6d"
+              >通过</span
+            >
+            <span v-else style="color: #ff4b50">未通过</span>
           </div>
           <el-divider />
-          <el-button type="primary" style="margin-top: 20px" @click="submitForm">提交阅卷</el-button>
+          <el-button type="primary" style="margin-top: 20px" @click="submitForm"
+            >提交阅卷</el-button
+          >
         </el-card>
       </div>
-
     </div>
-
   </div>
 </template>
 
 <script>
-
-import { paperResult } from '@/api/paper/exam'
-import { reviewPaper } from '@/api/paper/paper'
-import { scrollTo } from '@/utils/scroll-to'
-import QuItemShow from '@/views/admin/repo/qu/components/QuItemShow'
+import { paperResult } from "@/api/paper/exam";
+import { reviewPaper } from "@/api/paper/paper";
+import { scrollTo } from "@/utils/scroll-to";
+import QuItemShow from "@/views/admin/repo/qu/components/QuItemShow";
 
 export default {
   components: { QuItemShow },
@@ -153,253 +250,253 @@ export default {
       onlySubj: false,
       scrolled: false,
       // 试卷ID
-      paperId: '',
-      postForm: {
-      },
+      paperId: "",
+      postForm: {},
       hiddenQu: true,
-      loading: false
-    }
+      loading: false,
+    };
   },
 
   watch: {
-
     // 重新计算分数
-    'postForm.groupList': {
+    "postForm.groupList": {
       handler(val) {
-        let userScore = 0
+        let userScore = 0;
         for (let i = 0; i < val.length; i++) {
-          const item = val[i]
+          const item = val[i];
 
-          let groupScore = 0
-          const quList = item.quList
+          let groupScore = 0;
+          const quList = item.quList;
           for (let j = 0; j < quList.length; j++) {
-            const qu = quList[j]
+            const qu = quList[j];
             // 组合题增加组合分数
-            if (qu.quType === '99') {
-              const subList = qu.subList
+            if (qu.quType === "99") {
+              const subList = qu.subList;
               for (let k = 0; k < subList.length; k++) {
-                groupScore = this.$calc.add(subList[k].actualScore, groupScore)
+                groupScore = this.$calc.add(subList[k].actualScore, groupScore);
               }
             } else {
-              groupScore = this.$calc.add(qu.actualScore, groupScore)
+              groupScore = this.$calc.add(qu.actualScore, groupScore);
             }
           }
 
-          item.userScore = this.$calc.add(item.userScore, groupScore)
-          userScore = this.$calc.add(groupScore, userScore)
+          item.userScore = this.$calc.add(item.userScore, groupScore);
+          userScore = this.$calc.add(groupScore, userScore);
         }
 
-        this.postForm.userScore = userScore
+        this.postForm.userScore = userScore;
       },
-      deep: true
-    }
-
+      deep: true,
+    },
   },
 
   mounted() {
     // 监听滚动条
-    window.addEventListener('scroll', () => {
-      let scrollTop = 0
+    window.addEventListener("scroll", () => {
+      let scrollTop = 0;
       if (document.documentElement && document.documentElement.scrollTop) {
-        scrollTop = document.documentElement.scrollTop
+        scrollTop = document.documentElement.scrollTop;
       } else if (document.body) {
-        scrollTop = document.body.scrollTop
+        scrollTop = document.body.scrollTop;
       }
 
       if (scrollTop > 85) {
-        this.scrolled = true
+        this.scrolled = true;
       } else {
-        this.scrolled = false
+        this.scrolled = false;
       }
-    })
+    });
   },
   created() {
-    const id = this.$route.params.paperId
+    const id = this.$route.params.paperId;
     if (id !== undefined) {
-      this.paperId = id
-      this.fetchData(id)
+      this.paperId = id;
+      this.fetchData(id);
     }
   },
 
   methods: {
-
     // 手动判断正确错误
     checkAnswer(qu, isRight) {
       if (isRight) {
-        qu.actualScore = qu.score
+        qu.actualScore = qu.score;
       } else {
-        qu.actualScore = 0
+        qu.actualScore = 0;
       }
 
-      qu.isRight = isRight
+      qu.isRight = isRight;
     },
 
     // 到指定位置
     goAnchor(selector) {
-      const anchor = document.getElementById(selector)
-      const to = anchor.offsetTop + 80
-      scrollTo(to, 600, function() {
-        console.log('滑动到指定位置!')
-      })
+      const anchor = document.getElementById(selector);
+      const to = anchor.offsetTop + 80;
+      scrollTo(to, 600, () => {});
     },
 
     // 判断是否回答
     userAnswer(answerList) {
-      let answer = ''
+      let answer = "";
 
       for (let i = 0; i < answerList.length; i++) {
         if (answerList[i].checked) {
-          answer += answerList[i].abc
+          answer += answerList[i].abc;
         }
       }
 
-      if (answer == '') {
-        answer = '未作答'
+      if (answer == "") {
+        answer = "未作答";
       }
 
-      return answer
+      return answer;
     },
 
     // 获取试卷内容
     fetchData(id) {
-      const params = { id: id }
-      this.loading = true
-      paperResult(params).then(response => {
-        // 试卷内容
-        this.postForm = response.data
-        this.loading = false
-      }).catch(() => {
-        this.loading = false
-      })
+      const params = { id: id };
+      this.loading = true;
+      paperResult(params)
+        .then((response) => {
+          // 试卷内容
+          this.postForm = response.data;
+          this.loading = false;
+        })
+        .catch(() => {
+          this.loading = false;
+        });
     },
 
     handlerShow() {
-      this.hiddenQu = !this.hiddenQu
+      this.hiddenQu = !this.hiddenQu;
     },
 
     submitForm() {
-      this.loading = true
-
-      reviewPaper(this.postForm).then(() => {
-        this.$notify({
-          title: '成功',
-          message: '阅卷成功!',
-          type: 'success',
-          duration: 2000
+      this.loading = true;
+
+      reviewPaper(this.postForm)
+        .then(() => {
+          this.$notify({
+            title: "成功",
+            message: "阅卷成功!",
+            type: "success",
+            duration: 2000,
+          });
+
+          this.$router.push({
+            name: "ReviewPaper",
+            params: { examId: this.postForm.examId },
+          });
+          this.loading = false;
         })
-
-        this.$router.push({ name: 'ReviewPaper', params: { examId: this.postForm.examId }})
-        this.loading = false
-      }).catch(() => {
-        this.loading = false
-      })
-    }
-  }
-}
+        .catch(() => {
+          this.loading = false;
+        });
+    },
+  },
+};
 </script>
 
 <style scoped>
+.all-box {
+  display: flex;
+  flex-direction: row;
+}
+
+.all-box-left {
+  width: 250px;
+  flex: 0 0 250px;
+}
+
+.all-box-center {
+  flex-grow: 1;
+  padding-left: 20px;
+  padding-right: 20px;
+}
+
+.all-box-right {
+  width: 200px;
+  flex: 0 0 200px;
+  text-align: center;
+  line-height: 32px;
+  font-size: 14px;
+  font-weight: 700;
+}
 
-  .all-box{
-    display: flex;
-    flex-direction: row
-  }
-
-  .all-box-left{
-    width: 250px;
-    flex: 0 0 250px
-  }
-
-  .all-box-center{
-    flex-grow: 1;
-    padding-left: 20px;
-    padding-right: 20px
-  }
-
-  .all-box-right{
-
-    width: 200px;
-    flex: 0 0 200px;
-    text-align: center;
-    line-height: 32px;
-    font-size: 14px;
-    font-weight: 700;
-  }
-
-  .group-card{
-    margin-bottom: 15px;
-    line-height: 28px;
-    width: 100%;
-  }
-
-  .group-card-body {
-    line-height: 22px;
-    font-size: 14px;
-  }
-
-  .group-card .num{
-    color: #1890FF;
-  }
-
-  .left-box{
-    height: calc(100vh - 120px);
-    position: fixed;
-    overflow: auto;
-    clear: both;
-  }
-
-  .left-box-normal{
-    top: 103px;
-  }
-
-  .left-box-scrolled{
-    top: 20px;
-    height: calc(100vh - 40px);
-  }
-
-  .content-card{
-    margin-bottom: 20px;
-    line-height: 28px;
-  }
-
-  .check-box{
-    background: #f5f5f5;
-    padding: 20px;
-    line-height: 40px;
-    width: 200px;
-    font-size: 14px;
-    font-weight: 700;
-  }
-
-  .check-box .item{
-    display: flex;
-    flex-direction: row;
-    align-items: center
-  }
-
-  .check-icon{
-    font-size: 28px;
-    color:#ddd;
-    cursor: pointer;
-    margin-right: 10px;
-  }
-
-  .check-icon-right{
-    color: #03DD6D;
-  }
-
-  .check-icon-error{
-    color: #FF4B50;
-  }
-
-  ::v-deep
-  .el-divider--horizontal{
-    margin: 5px 0;
-  }
-
-  .link-item{
-    background: #f5f5f5; margin-bottom: 10px; padding: 10px; border-radius: 10px
-  }
+.group-card {
+  margin-bottom: 15px;
+  line-height: 28px;
+  width: 100%;
+}
+
+.group-card-body {
+  line-height: 22px;
+  font-size: 14px;
+}
+
+.group-card .num {
+  color: #1890ff;
+}
+
+.left-box {
+  height: calc(100vh - 120px);
+  position: fixed;
+  overflow: auto;
+  clear: both;
+}
+
+.left-box-normal {
+  top: 103px;
+}
 
+.left-box-scrolled {
+  top: 20px;
+  height: calc(100vh - 40px);
+}
+
+.content-card {
+  margin-bottom: 20px;
+  line-height: 28px;
+}
+
+.check-box {
+  background: #f5f5f5;
+  padding: 20px;
+  line-height: 40px;
+  width: 200px;
+  font-size: 14px;
+  font-weight: 700;
+}
+
+.check-box .item {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.check-icon {
+  font-size: 28px;
+  color: #ddd;
+  cursor: pointer;
+  margin-right: 10px;
+}
+
+.check-icon-right {
+  color: #03dd6d;
+}
+
+.check-icon-error {
+  color: #ff4b50;
+}
+
+::v-deep .el-divider--horizontal {
+  margin: 5px 0;
+}
+
+.link-item {
+  background: #f5f5f5;
+  margin-bottom: 10px;
+  padding: 10px;
+  border-radius: 10px;
+}
 </style>
 

+ 425 - 0
exam-06173-vue/src/views/admin/paper/paper/view.vue

@@ -0,0 +1,425 @@
+<template>
+  <div v-loading="loading">
+    <div class="all-box">
+      <div class="all-box-left">
+        <el-card
+          v-if="!loading"
+          :class="{
+            'left-box': true,
+            'left-box-normal': !scrolled,
+            'left-box-scrolled': scrolled,
+          }"
+          style="width: 250px"
+        >
+          <div
+            v-for="item in postForm.groupList"
+            :key="item.id"
+            class="group-card"
+          >
+            <div
+              v-if="
+                (onlySubj && (item.quType === '4' || item.quType === '99')) ||
+                !onlySubj
+              "
+            >
+              <div style="line-height: 40px">
+                <el-button
+                  style="font-size: 14px; font-weight: 700"
+                  type="text"
+                  @click="goAnchor(item.id)"
+                  >{{ item.title }}</el-button
+                >
+                <el-divider />
+              </div>
+              <div class="group-card-body">
+                <div>
+                  共 <i class="num">{{ item.quCount }}</i> 题,共
+                  <i class="num">{{ item.totalScore }}</i> 分
+                </div>
+              </div>
+
+              <div class="card-box">
+                <div
+                  v-for="card in item.quList"
+                  :key="card.id"
+                  class="item"
+                  @click="goAnchor(card.id)"
+                >
+                  <div
+                    :class="{
+                      num: true,
+                      right: card.isRight,
+                      error: !card.isRight,
+                      current: postForm.state == 1 && card.quType == '4',
+                    }"
+                  >
+                    {{ card.sort }}
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-card>
+      </div>
+
+      <div class="all-box-center">
+        <template v-for="item in postForm.groupList">
+          <el-card
+            v-if="
+              (onlySubj && (item.quType === '4' || item.quType === '99')) ||
+              !onlySubj
+            "
+            :id="item.id"
+            :key="item.id"
+            class="content-card"
+          >
+            <template v-for="(qu, index) in item.quList">
+              <div v-if="!onlyError || (onlyError && !qu.isRight)" :key="qu.id">
+                <div :id="qu.id" class="list-box">
+                  <div class="list-qu">
+                    <qu-item-show
+                      :value="qu"
+                      :index="index"
+                      :state="postForm.state"
+                    />
+
+                    <div v-if="qu.quType === '99'">
+                      <!-- 子题目的判卷 -->
+                      <div
+                        v-for="(sub, jj) in qu.subList"
+                        :key="sub.id"
+                        class="list-sub-box"
+                      >
+                        <div class="list-sub-qu">
+                          <qu-item-show
+                            :value="sub"
+                            :index="jj"
+                            :state="postForm.state"
+                            :show-type="true"
+                            :sub="true"
+                          />
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </template>
+          </el-card>
+        </template>
+      </div>
+
+      <div class="all-box-right">
+        <el-card
+          v-if="!loading"
+          :class="{
+            'left-box': true,
+            'left-box-normal': !scrolled,
+            'left-box-scrolled': scrolled,
+          }"
+          style="width: 200px"
+        >
+          <div>人员姓名</div>
+          <div style="color: #0a84ff">{{ postForm.userId_dictText }}</div>
+          <el-divider />
+          <div>考试成绩</div>
+          <div style="color: #ff4b50">{{ postForm.userScore }}</div>
+          <el-divider />
+          <div>仅看错题</div>
+          <div>
+            <el-switch
+              v-model="onlyError"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+            />
+          </div>
+          <el-divider />
+          <div>仅看主观题</div>
+          <div>
+            <el-switch
+              v-model="onlySubj"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+            />
+          </div>
+          <el-divider />
+          <div>考试状态</div>
+          <div>
+            <span
+              v-if="postForm.userScore >= postForm.qualifyScore"
+              style="color: #03dd6d"
+              >通过</span
+            >
+            <span v-else style="color: #ff4b50">未通过</span>
+          </div>
+        </el-card>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { paperResult } from "@/api/paper/exam";
+import { reviewPaper } from "@/api/paper/paper";
+import { scrollTo } from "@/utils/scroll-to";
+import QuItemShow from "@/views/admin/repo/qu/components/QuItemShow";
+
+export default {
+  components: { QuItemShow },
+  data() {
+    return {
+      onlyError: false,
+      // 只显示主观题
+      onlySubj: false,
+      scrolled: false,
+      // 试卷ID
+      paperId: "",
+      postForm: {},
+      hiddenQu: true,
+      loading: false,
+    };
+  },
+
+  watch: {
+    // 重新计算分数
+    "postForm.groupList": {
+      handler(val) {
+        let userScore = 0;
+        for (let i = 0; i < val.length; i++) {
+          const item = val[i];
+
+          let groupScore = 0;
+          const quList = item.quList;
+          for (let j = 0; j < quList.length; j++) {
+            const qu = quList[j];
+            // 组合题增加组合分数
+            if (qu.quType === "99") {
+              const subList = qu.subList;
+              for (let k = 0; k < subList.length; k++) {
+                groupScore = this.$calc.add(subList[k].actualScore, groupScore);
+              }
+            } else {
+              groupScore = this.$calc.add(qu.actualScore, groupScore);
+            }
+          }
+
+          item.userScore = this.$calc.add(item.userScore, groupScore);
+          userScore = this.$calc.add(groupScore, userScore);
+        }
+
+        this.postForm.userScore = userScore;
+      },
+      deep: true,
+    },
+  },
+
+  mounted() {
+    // 监听滚动条
+    window.addEventListener("scroll", () => {
+      let scrollTop = 0;
+      if (document.documentElement && document.documentElement.scrollTop) {
+        scrollTop = document.documentElement.scrollTop;
+      } else if (document.body) {
+        scrollTop = document.body.scrollTop;
+      }
+
+      if (scrollTop > 85) {
+        this.scrolled = true;
+      } else {
+        this.scrolled = false;
+      }
+    });
+  },
+  created() {
+    const id = this.$route.params.paperId;
+    if (id !== undefined) {
+      this.paperId = id;
+      this.fetchData(id);
+    }
+  },
+
+  methods: {
+    // 手动判断正确错误
+    checkAnswer(qu, isRight) {
+      if (isRight) {
+        qu.actualScore = qu.score;
+      } else {
+        qu.actualScore = 0;
+      }
+
+      qu.isRight = isRight;
+    },
+
+    // 到指定位置
+    goAnchor(selector) {
+      const anchor = document.getElementById(selector);
+      const to = anchor.offsetTop + 80;
+      scrollTo(to, 600, () => {});
+    },
+
+    // 判断是否回答
+    userAnswer(answerList) {
+      let answer = "";
+
+      for (let i = 0; i < answerList.length; i++) {
+        if (answerList[i].checked) {
+          answer += answerList[i].abc;
+        }
+      }
+
+      if (answer == "") {
+        answer = "未作答";
+      }
+
+      return answer;
+    },
+
+    // 获取试卷内容
+    fetchData(id) {
+      const params = { id: id };
+      this.loading = true;
+      paperResult(params)
+        .then((response) => {
+          // 试卷内容
+          this.postForm = response.data;
+          this.loading = false;
+        })
+        .catch(() => {
+          this.loading = false;
+        });
+    },
+
+    handlerShow() {
+      this.hiddenQu = !this.hiddenQu;
+    },
+
+    submitForm() {
+      this.loading = true;
+
+      reviewPaper(this.postForm)
+        .then(() => {
+          this.$notify({
+            title: "成功",
+            message: "阅卷成功!",
+            type: "success",
+            duration: 2000,
+          });
+
+          this.$router.push({
+            name: "ReviewPaper",
+            params: { examId: this.postForm.examId },
+          });
+          this.loading = false;
+        })
+        .catch(() => {
+          this.loading = false;
+        });
+    },
+  },
+};
+</script>
+
+<style scoped>
+.all-box {
+  display: flex;
+  flex-direction: row;
+}
+
+.all-box-left {
+  width: 250px;
+  flex: 0 0 250px;
+}
+
+.all-box-center {
+  flex-grow: 1;
+  padding-left: 20px;
+  padding-right: 20px;
+}
+
+.all-box-right {
+  width: 200px;
+  flex: 0 0 200px;
+  text-align: center;
+  line-height: 32px;
+  font-size: 14px;
+  font-weight: 700;
+}
+
+.group-card {
+  margin-bottom: 15px;
+  line-height: 28px;
+  width: 100%;
+}
+
+.group-card-body {
+  line-height: 22px;
+  font-size: 14px;
+}
+
+.group-card .num {
+  color: #1890ff;
+}
+
+.left-box {
+  height: calc(100vh - 120px);
+  position: fixed;
+  overflow: auto;
+  clear: both;
+}
+
+.left-box-normal {
+  top: 103px;
+}
+
+.left-box-scrolled {
+  top: 20px;
+  height: calc(100vh - 40px);
+}
+
+.content-card {
+  margin-bottom: 20px;
+  line-height: 28px;
+}
+
+.check-box {
+  background: #f5f5f5;
+  padding: 20px;
+  line-height: 40px;
+  width: 200px;
+  font-size: 14px;
+  font-weight: 700;
+}
+
+.check-box .item {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.check-icon {
+  font-size: 28px;
+  color: #ddd;
+  cursor: pointer;
+  margin-right: 10px;
+}
+
+.check-icon-right {
+  color: #03dd6d;
+}
+
+.check-icon-error {
+  color: #ff4b50;
+}
+
+::v-deep .el-divider--horizontal {
+  margin: 5px 0;
+}
+
+.link-item {
+  background: #f5f5f5;
+  margin-bottom: 10px;
+  padding: 10px;
+  border-radius: 10px;
+}
+</style>
+

+ 1 - 1
exam-06173-vue/src/views/admin/repo/form.vue

@@ -50,7 +50,7 @@
             <el-card>
 
               <el-radio-group v-model="postForm.openType" size="small">
-                <el-radio :label="1">完全公开</el-radio>
+                <!-- <el-radio :label="1">完全公开</el-radio> -->
                 <el-radio :label="2">部门开放</el-radio>
                 <el-radio :label="3">指定人员</el-radio>
               </el-radio-group>

+ 7 - 3
exam-06173-vue/src/views/admin/stat/total/detailBm.vue

@@ -28,13 +28,17 @@
             width="120"
             align="center"
           />
-          <el-table-column prop="createTime" label="开始时间" align="center" />
-          <el-table-column prop="finishTime" label="完成时间" align="center" />
+          <el-table-column prop="createTime" label="创建时间" align="center" />
           <el-table-column label="进度" width="200" align="center">
             <template slot-scope="scope">
               <el-progress
                 :percentage="
-                  parseInt((scope.row.passUser / scope.row.actualUser) * 100)
+                  parseInt((scope.row.passUser / scope.row.actualUser) * 100) >
+                  100
+                    ? 100
+                    : parseInt(
+                        (scope.row.passUser / scope.row.actualUser) * 100
+                      )
                 "
               ></el-progress>
             </template>

+ 5 - 2
exam-06173-vue/src/views/admin/stat/total/detailZg.vue

@@ -34,7 +34,10 @@
             <template slot-scope="scope">
               <el-progress
                 :percentage="
-                  parseInt((scope.row.learnMin / scope.row.needLearn) * 100)
+                  parseInt((scope.row.learnMin / scope.row.needLearn) * 100) >
+                  100
+                    ? 100
+                    : parseInt((scope.row.learnMin / scope.row.needLearn) * 100)
                 "
               ></el-progress>
             </template>
@@ -184,7 +187,7 @@ export default {
 
     // 查看试卷
     viewPaper(item) {
-      this.$router.push({ path: `/admin/tmpl/preview/${item.tmplId}` });
+      this.$router.push({ path: `/admin/exam/paperView/${item.paperId}` });
     },
 
     // 重置表格布局

+ 28 - 0
exam-06173-vue/src/views/checkPoint/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+import { Message } from "element-ui";
+export default {
+  created() {
+    const userName = this.$route.query.userName || "";
+    const mark = this.$route.query.mark || "";
+    const postForm = { userName, mark };
+
+    if (!userName || !mark) {
+      Message.error("登录失败,未知用户");
+    } else {
+      this.$store.dispatch("user/login", postForm).then((res) => {
+        // 学员端
+        if (res.roleType === 1) {
+          this.$router.push({ path: "/" });
+        } else {
+          // 其它跳到后台
+          this.$router.push({ path: "/admin/dashboard" });
+        }
+      });
+    }
+  },
+};
+</script>

+ 55 - 32
exam-06173-vue/src/views/dashboard/index.vue

@@ -8,7 +8,7 @@
         </div>
         <div class="r">
           <span class="statisticsTitle">学员总数</span>
-          <span class="statisticsNum">128</span>
+          <span class="statisticsNum">{{ usernum }}</span>
         </div>
       </el-card>
       <el-card class="statisticsItem" @click.native="jump('ExamTmpl')">
@@ -17,7 +17,7 @@
         </div>
         <div class="r">
           <span class="statisticsTitle">试卷总数</span>
-          <span class="statisticsNum">327</span>
+          <span class="statisticsNum">{{ tmplnum }}</span>
         </div>
       </el-card>
       <el-card class="statisticsItem" @click.native="jump('QuList')">
@@ -26,7 +26,7 @@
         </div>
         <div class="r">
           <span class="statisticsTitle">试题总数</span>
-          <span class="statisticsNum">1653</span>
+          <span class="statisticsNum">{{ titlenum }}</span>
         </div>
       </el-card>
       <el-card class="statisticsItem" @click.native="jump('ListCourse')">
@@ -35,7 +35,7 @@
         </div>
         <div class="r">
           <span class="statisticsTitle">课程总数</span>
-          <span class="statisticsNum">695</span>
+          <span class="statisticsNum">{{ coursenum }}</span>
         </div>
       </el-card>
     </div>
@@ -107,7 +107,16 @@
               </div>
               <el-form label-width="90px">
                 <el-form-item label="考试时间:">
-                  <span>{{ item.startTime }}</span>
+                  <el-tooltip
+                    class="item"
+                    effect="dark"
+                    :content="`${item.startTime} - ${item.endTime}`"
+                    placement="top"
+                  >
+                    <span class="ovf"
+                      >{{ item.startTime }} - {{ item.endTime }}</span
+                    >
+                  </el-tooltip>
                 </el-form-item>
                 <el-form-item label="考试时长:">
                   <span>{{ item.totalTime }}</span>
@@ -132,24 +141,12 @@
 
       <el-tabs v-model="tabActive" @tab-click="reLayoutTable">
         <el-tab-pane class="chartItem" label="部门" name="bm">
-          <div
-            id="bmPxChartDom"
-            style="width: 48%; height: 280px; margin-top: 20px"
-          ></div>
-          <div
-            id="bmKsChartDom"
-            style="width: 48%; height: 280px; margin-top: 20px"
-          ></div>
+          <div id="bmPxChartDom" style="width: 48%; height: 280px"></div>
+          <div id="bmKsChartDom" style="width: 48%; height: 280px"></div>
         </el-tab-pane>
         <el-tab-pane class="chartItem" label="职工" name="zg">
-          <div
-            id="zgPxChartDom"
-            style="width: 48%; height: 280px; margin-top: 20px"
-          ></div>
-          <div
-            id="zgKsChartDom"
-            style="width: 48%; height: 280px; margin-top: 20px"
-          ></div>
+          <div id="zgPxChartDom" style="width: 48%; height: 280px"></div>
+          <div id="zgKsChartDom" style="width: 48%; height: 280px"></div>
         </el-tab-pane>
       </el-tabs>
     </div>
@@ -159,6 +156,7 @@
 <script>
 import * as echarts from "echarts";
 import {
+  getCttTotal,
   getCurrentExam,
   getDepartRank,
   getPassedRate,
@@ -169,6 +167,10 @@ export default {
   components: {},
   data() {
     return {
+      usernum: 0,
+      tmplnum: 0,
+      titlenum: 0,
+      coursenum: 0,
       tabActive: "bm",
       radioValue: "2",
       ksList: [],
@@ -180,9 +182,19 @@ export default {
   },
   mounted() {
     this.initChart();
+    this.getCttTotal();
     this.getCurrentExam();
   },
   methods: {
+    // 获取统计数值
+    getCttTotal() {
+      getCttTotal().then(({ data }) => {
+        this.usernum = data.usernum;
+        this.tmplnum = data.tmplnum;
+        this.titlenum = data.titlenum;
+        this.coursenum = data.coursenum;
+      });
+    },
     // 页面跳转
     jump(name, params = {}) {
       this.$router.push({ name, params });
@@ -247,6 +259,9 @@ export default {
           xAxis: {
             type: "category",
             data: xAxisData,
+            axisLabel: {
+              rotate: -40,
+            },
           },
           yAxis: {
             type: "value",
@@ -258,9 +273,7 @@ export default {
               let str = "";
               data.forEach((ele) => {
                 let circle = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;left:5px;background-color:${ele.color}"></span>`;
-                str +=
-                  circle +
-                  `${ele.name}: ${ele.value}分钟 <br />`;
+                str += circle + `${ele.name}: ${ele.value}分钟 <br />`;
               });
               return str;
             },
@@ -334,9 +347,7 @@ export default {
               let str = "";
               data.forEach((ele) => {
                 let circle = `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;left:5px;background-color:${ele.color}"></span>`;
-                str +=
-                  circle +
-                  `${ele.name}: ${ele.value}% <br />`;
+                str += circle + `${ele.name}: ${ele.value}% <br />`;
               });
               return str;
             },
@@ -394,7 +405,7 @@ export default {
   .statisticsItem {
     margin-left: 20px;
     background: #1890ff;
-    padding: 10px;
+    padding: 5px;
     cursor: pointer;
 
     .el-card__body {
@@ -429,7 +440,7 @@ export default {
         }
 
         .statisticsNum {
-          font-size: 14px;
+          font-size: 18px;
         }
       }
     }
@@ -448,7 +459,7 @@ export default {
 
   .contentItem {
     width: 48%;
-    height: 212px;
+    height: 225px;
 
     .quickBox {
       width: 100%;
@@ -547,8 +558,21 @@ export default {
         }
       }
 
+      .el-card__body {
+        padding: 20px;
+      }
+
       .el-form-item {
-        margin-bottom: 2px;
+        margin-bottom: 0;
+        height: 36px;
+
+        .ovf {
+          display: inline-block;
+          width: 100%;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
       }
     }
   }
@@ -556,7 +580,6 @@ export default {
 
 .chartBox {
   width: 100%;
-  height: 300px;
   margin-top: 50px;
   position: relative;
 

+ 27 - 29
exam-06173-vue/src/views/login/components/third-login.vue

@@ -1,8 +1,10 @@
 <template>
-
-  <div v-if="siteData.wechatLogin || siteData.faceLogin" style="line-height: 35px; margin-top: 10px">
+  <div
+    v-if="false && (siteData.wechatLogin || siteData.faceLogin)"
+    style="line-height: 35px; margin-top: 10px"
+  >
     <div class="title-line">其它方式登录</div>
-    <div style="display: flex;">
+    <div style="display: flex">
       <a v-if="siteData.wechatLogin" class="third-item wechat-icon">
         <svg-icon icon-class="wechat" @click="wechatLogin" />
         <div>微信登录</div>
@@ -14,47 +16,42 @@
       </a>
     </div>
   </div>
-
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import { apiGetWechatUrl } from '@/api/ability/login'
+import { mapGetters } from "vuex";
+import { apiGetWechatUrl } from "@/api/ability/login";
 
 export default {
-  name: 'ThirdLogin',
+  name: "ThirdLogin",
 
   data() {
     return {
       loading1: false,
       loading2: false,
-      loginForm: {}
-    }
+      loginForm: {},
+    };
   },
   computed: {
-    ...mapGetters([
-      'siteData'
-    ])
+    ...mapGetters(["siteData"]),
   },
   methods: {
-
     wechatLogin() {
       // 获得跳转地址并跳转
-      apiGetWechatUrl({ state: 'pc' }).then(res => {
-        window.location = res.data.url
-      })
+      apiGetWechatUrl({ state: "pc" }).then((res) => {
+        window.location = res.data.url;
+      });
     },
 
     faceLogin() {
-      this.$router.push({ name: 'FaceLogin' })
-    }
-  }
-}
+      this.$router.push({ name: "FaceLogin" });
+    },
+  },
+};
 </script>
 
 <style scoped>
-
-.third-item{
+.third-item {
   margin-right: 10px;
   color: #888;
   display: flex;
@@ -63,19 +60,20 @@ export default {
   align-items: center;
 }
 
-.third-item .svg-icon{
-  width: 22px; height: 22px;
+.third-item .svg-icon {
+  width: 22px;
+  height: 22px;
   cursor: pointer;
 }
-.third-item div{
- font-size: 12px;
+.third-item div {
+  font-size: 12px;
 }
 
-.wechat-icon :hover{
-  color: #1AAC1A;
+.wechat-icon :hover {
+  color: #1aac1a;
 }
 
-.face-icon :hover{
+.face-icon :hover {
   color: #ff8000;
 }
 </style>

+ 124 - 78
exam-06173-vue/src/views/login/index.vue

@@ -1,20 +1,19 @@
 <template>
-
   <div>
     <el-row>
-
       <el-col :lg="14" :md="10" class="left hidden-sm-and-down">
-        <img src="@/assets/web/images/login2.png" style="height: 45vh">
+        <img src="@/assets/web/images/login2.png" style="height: 45vh" />
       </el-col>
-
       <el-col :lg="10" :md="14" class="right">
-
         <div class="box">
-
           <el-tabs v-model="activeName">
             <el-tab-pane label="账号登录" name="account">
-
-              <el-form v-if="activeName === 'account'" ref="postForm" :model="postForm" :rules="loginRules">
+              <el-form
+                v-if="activeName === 'account'"
+                ref="postForm"
+                :model="postForm"
+                :rules="loginRules"
+              >
                 <el-form-item prop="username">
                   <el-input
                     v-model="postForm.username"
@@ -39,15 +38,23 @@
                 </el-form-item>
 
                 <el-form-item>
-                  <el-button :loading="loading" type="primary" style="width: 100%" @click.native.prevent="accountLogin">登录</el-button>
+                  <el-button
+                    :loading="loading"
+                    type="primary"
+                    style="width: 100%"
+                    @click.native.prevent="accountLogin"
+                    >登录</el-button
+                  >
                 </el-form-item>
-
               </el-form>
-
             </el-tab-pane>
-            <el-tab-pane label="手机登录" name="mobile">
-
-              <el-form v-if="activeName === 'mobile'" ref="postForm" :model="postForm" :rules="loginRules">
+            <el-tab-pane label="手机登录" name="mobile" v-if="false">
+              <el-form
+                v-if="activeName === 'mobile'"
+                ref="postForm"
+                :model="postForm"
+                :rules="loginRules"
+              >
                 <el-form-item prop="mobile">
                   <el-input
                     v-model="postForm.mobile"
@@ -62,33 +69,52 @@
                 </el-form-item>
 
                 <el-form-item>
-                  <el-button :loading="loading" type="primary" style="width: 100%" @click.native.prevent="mobileLogin">登录</el-button>
+                  <el-button
+                    :loading="loading"
+                    type="primary"
+                    style="width: 100%"
+                    @click.native.prevent="mobileLogin"
+                    >登录</el-button
+                  >
                 </el-form-item>
-
               </el-form>
             </el-tab-pane>
-
           </el-tabs>
 
           <div style="text-align: right; line-height: 10px">
             <el-link href="/#/register">立即注册</el-link>
-            <el-link href="/#/forgot" style="margin-left: 10px">忘记密码?</el-link>
+            <el-link href="/#/forgot" style="margin-left: 10px"
+              >忘记密码?</el-link
+            >
           </div>
 
           <login-demo v-if="isDemo" />
 
-          <div v-if="siteData.h5Code || siteData.mpCode" style="line-height: 35px; margin-top: 10px">
+          <div
+            v-if="siteData.h5Code || siteData.mpCode"
+            style="line-height: 35px; margin-top: 10px"
+          >
             <div class="title-line">手机端</div>
-            <el-button v-if="siteData.h5Code" size="mini" round @click="showH5Code">H5学员端</el-button>
-            <el-button v-if="siteData.mpCode" size="mini" type="success" round @click="showMpCode">小程序学员端</el-button>
+            <el-button
+              v-if="siteData.h5Code"
+              size="mini"
+              round
+              @click="showH5Code"
+              >H5学员端</el-button
+            >
+            <el-button
+              v-if="siteData.mpCode"
+              size="mini"
+              type="success"
+              round
+              @click="showMpCode"
+              >小程序学员端</el-button
+            >
           </div>
 
           <third-login />
-
         </div>
-
       </el-col>
-
     </el-row>
 
     <el-dialog
@@ -97,7 +123,10 @@
       style="text-align: center"
     >
       <div class="code-tips">扫码进入H5学员端</div>
-      <img :src="siteData.h5Code" style="width: 300px; border: #ddd 1px solid">
+      <img
+        :src="siteData.h5Code"
+        style="width: 300px; border: #ddd 1px solid"
+      />
     </el-dialog>
 
     <el-dialog
@@ -106,122 +135,139 @@
       style="text-align: center"
     >
       <div class="code-tips">扫码进入小程序学员端</div>
-      <img :src="siteData.mpCode" style="width: 300px; border: #ddd 1px solid">
+      <img
+        :src="siteData.mpCode"
+        style="width: 300px; border: #ddd 1px solid"
+      />
     </el-dialog>
-
   </div>
-
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import LoginDemo from './components/demo'
-import YfCaptcha from '@/components/Captcha'
-import SmsInput from '@/components/SmsInput'
-import ThirdLogin from '@/views/login/components/third-login'
+import { mapGetters } from "vuex";
+import LoginDemo from "./components/demo";
+import YfCaptcha from "@/components/Captcha";
+import SmsInput from "@/components/SmsInput";
+import ThirdLogin from "@/views/login/components/third-login";
 
 export default {
   components: { ThirdLogin, SmsInput, YfCaptcha, LoginDemo },
   data() {
     return {
       isDemo: this.$demo,
-      activeName: 'account',
+      activeName: "account",
       loading: false,
       h5Visible: false,
       mpVisible: false,
       wxVisible: false,
       postForm: {
-        smsCode: '',
-        captchaKey: '',
-        captchaValue: ''
+        smsCode: "",
+        captchaKey: "",
+        captchaValue: "",
       },
       loginRules: {
-        username: [{ required: true, trigger: 'blur', message: '用户名不能为空' }],
-        password: [{ required: true, trigger: 'blur', message: '密码不能为空' }],
-        captchaValue: [{ required: true, trigger: 'blur', message: '验证码不能为空' }],
-        smsCode: [{ required: true, trigger: 'blur', message: '短信验证码不能为空' }],
-        mobile: [{ required: true, trigger: 'blur', message: '手机号不能为空' }]
-      }
-    }
+        username: [
+          { required: true, trigger: "blur", message: "用户名不能为空" },
+        ],
+        password: [
+          { required: true, trigger: "blur", message: "密码不能为空" },
+        ],
+        captchaValue: [
+          { required: true, trigger: "blur", message: "验证码不能为空" },
+        ],
+        smsCode: [
+          { required: true, trigger: "blur", message: "短信验证码不能为空" },
+        ],
+        mobile: [
+          { required: true, trigger: "blur", message: "手机号不能为空" },
+        ],
+      },
+    };
   },
 
   computed: {
-    ...mapGetters([
-      'siteData'
-    ])
+    ...mapGetters(["siteData"]),
   },
 
   mounted() {
-    window.addEventListener('keydown', this.keyDown)
+    window.addEventListener("keydown", this.keyDown);
   },
   destroyed() {
-    window.removeEventListener('keydown', this.keyDown, false)
+    window.removeEventListener("keydown", this.keyDown, false);
   },
 
   methods: {
-
     showH5Code() {
-      this.h5Visible = true
+      this.h5Visible = true;
     },
     showMpCode() {
-      this.mpVisible = true
+      this.mpVisible = true;
     },
 
     loginBack(res) {
       // 学员端
       if (res.roleType === 1) {
-        this.$router.push({ path: '/' })
+        this.$router.push({ path: "/" });
       } else {
         // 其它跳到后台
-        this.$router.push({ path: '/admin/dashboard' })
+        this.$router.push({ path: "/admin/dashboard" });
       }
     },
 
     async mobileLogin() {
-      this.$refs.postForm.validate(valid => {
+      this.$refs.postForm.validate((valid) => {
         if (valid) {
-          this.loading = true
-          this.$store.dispatch('user/mobileLogin', this.postForm)
-            .then(res => {
-              this.loginBack(res)
+          this.loading = true;
+          this.$store
+            .dispatch("user/mobileLogin", this.postForm)
+            .then((res) => {
+              this.loginBack(res);
             })
             .catch(() => {
-              this.loading = false
-            })
+              this.loading = false;
+            });
         }
-      })
+      });
     },
 
     accountLogin() {
-      this.$refs.postForm.validate(valid => {
+      this.$refs.postForm.validate((valid) => {
         if (valid) {
-          this.loading = true
-          this.$store.dispatch('user/login', this.postForm)
-            .then(res => {
-              this.loginBack(res)
+          this.loading = true;
+          this.$store
+            .dispatch("user/login", this.postForm)
+            .then((res) => {
+              this.loginBack(res);
             })
             .catch(() => {
-              this.loading = false
-            })
+              this.loading = false;
+            });
         }
-      })
+      });
     },
 
     studentRegister() {
-      this.$router.push({ name: 'Register' })
+      this.$router.push({ name: "Register" });
     },
 
     keyDown(e) {
       if (e.keyCode == 13) {
-        if (this.activeName === 'account') {
-          this.accountLogin()
+        if (this.activeName === "account") {
+          this.accountLogin();
         }
 
-        if (this.activeName === 'mobile') {
-          this.mobileLogin()
+        if (this.activeName === "mobile") {
+          this.mobileLogin();
         }
       }
-    }
-  }
-}
+    },
+  },
+};
 </script>
+
+<style lang="scss" scoped>
+.box {
+  position: relative;
+  z-index: 50;
+}
+</style>

+ 75 - 50
exam-06173-vue/src/views/login/layout/LoginLayout.vue

@@ -1,87 +1,93 @@
 <template>
-
   <div class="login-container">
-
     <el-row :gutter="20" class="top">
-      <el-col :span="6" :offset="3">
+      <el-col :span="6" :offset="1">
         <div style="display: flex; flex-direction: row; align-items: center">
           <a href="/">
             <div v-if="siteData.frontLogo">
-              <img :src="siteData.frontLogo">
+              <img :src="siteData.frontLogo" />
             </div>
-            <div v-else style="font-weight: 700; font-size: 26px; color: #eee; flex-grow: 1; text-align: left; padding-left: 10px">
+            <div
+              v-else
+              style="
+                font-weight: 700;
+                font-size: 26px;
+                color: #eee;
+                flex-grow: 1;
+                text-align: left;
+                padding-left: 10px;
+              "
+            >
               {{ siteData.siteName }}
             </div>
           </a>
         </div>
-
       </el-col>
 
       <el-col :span="12" style="line-height: 60px; text-align: right" />
-
     </el-row>
 
     <el-row :gutter="20" class="content">
       <el-col :span="18" :offset="3">
         <app-main />
       </el-col>
-
+      <!-- <div class="loginImgBox">
+        <img src="@/assets/web/images/login3.png" />
+      </div> -->
     </el-row>
 
     <el-row :gutter="20" class="footer">
-      <el-col :span="18" :offset="3">
+      <!-- <el-col :span="18" :offset="3">
         <div v-html="siteData.copyRight" />
-      </el-col>
+      </el-col> -->
+      <div class="flex">
+        <div v-html="siteData.copyRight" />
+      </div>
     </el-row>
-
   </div>
-
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import AppMain from '@/layout/components/AppMain'
+import { mapGetters } from "vuex";
+import AppMain from "@/layout/components/AppMain";
 
 export default {
-  name: 'LoginLayout',
+  name: "LoginLayout",
   components: { AppMain },
 
   data() {
     return {
-      activeIndex: ''
-    }
+      activeIndex: "",
+    };
   },
   computed: {
-    ...mapGetters([
-      'siteData'
-    ])
+    ...mapGetters(["siteData"]),
   },
   methods: {
     isActive(url) {
       if (this.activeIndex === url) {
-        return 'nav active'
+        return "nav active";
       }
-      return 'nav'
-    }
-  }
-}
+      return "nav";
+    },
+  },
+};
 </script>
 
 <style scoped>
 @import "~@/styles/login.css";
 
- ::v-deep
- .app-main {
-   min-height: 84vh;
-   width: 100%;
- }
+::v-deep .app-main {
+  min-height: 84vh;
+  width: 100%;
+}
 
-.header-bg{
+.header-bg {
   height: 60px;
   background: #4377fb;
 }
 
-.right-user{
+.right-user {
   display: flex;
   justify-content: flex-end;
   flex-direction: row;
@@ -89,25 +95,26 @@ export default {
   align-items: center;
 }
 
-.right-user :nth-child(1), .right-user :nth-child(2){
+.right-user :nth-child(1),
+.right-user :nth-child(2) {
   margin-right: 10px;
 }
 
-.right-user a{
+.right-user a {
   color: #efefef;
   font-size: 14px;
   font-weight: 500;
 }
 
-.right-user a:first-child{
+.right-user a:first-child {
   margin-right: 10px;
 }
 
-.right-user a:hover{
+.right-user a:hover {
   color: #ffd550;
 }
 
-.nav{
+.nav {
   color: #333;
   border: none;
   background: transparent;
@@ -118,43 +125,61 @@ export default {
   background: #fff;
 }
 
-.active{
+.active {
   color: #000055;
-  background: #FFD550;
+  background: #ffd550;
 }
 
 .nav:hover {
   color: #000055;
-  background: #FFD550;
+  background: #ffd550;
 }
 
-.col-logo{
-
+.col-logo {
   display: flex;
   align-items: center;
   justify-content: flex-start;
   height: 60px;
 }
 
-/deep/
-.top-avatar{
+/deep/ .top-avatar {
   text-align: right;
   display: flex;
   align-items: center;
   margin-right: 5px !important;
-
 }
 
-/deep/
-.top-avatar div{
+/deep/ .top-avatar div {
   display: flex;
   align-items: center;
   margin-right: -10px !important;
 }
 
-/deep/
-.top-avatar img{
-  width: 30px; height: 30px; border-radius: 15px;
+/deep/ .top-avatar img {
+  width: 30px;
+  height: 30px;
+  border-radius: 15px;
+}
+
+.footer .flex {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
 }
+</style>
+
 
+<style lang="scss" scoped>
+.loginImgBox {
+  position: absolute;
+  height: 100%;
+  width: 100%;
+
+  img {
+    width: 100%;
+    height: 100%;
+    background-size: 100% 100%;
+  }
+}
 </style>

+ 86 - 68
exam-06173-vue/src/views/web/course/list.vue

@@ -1,7 +1,5 @@
 <template>
-
   <div>
-
     <el-tabs v-model="listQuery.params.learnState" @tab-click="tabClick">
       <el-tab-pane name="0" label="全部课程" />
       <el-tab-pane name="1" label="未学习" />
@@ -9,16 +7,23 @@
       <el-tab-pane name="3" label="已学完" />
     </el-tabs>
 
-    <web-table
-      ref="pagingTable"
-      :options="options"
-      :list-query="listQuery"
-    >
-
+    <web-table ref="pagingTable" :options="options" :list-query="listQuery">
       <template slot="filter-content">
-
-        <dic-catalog-tree v-model="listQuery.params.catId" dic-code="course_catalog" placeholder="课程分类" width="200" class="filter-item" style="margin-top: 3px" />
-        <dic-list-select v-model="listQuery.params.isMust" dic-code="course_need" title="学习类型" />
+        <dic-catalog-tree
+          v-model="listQuery.params.catId"
+          dic-code="course_catalog"
+          placeholder="课程分类"
+          width="200"
+          class="filter-item"
+          style="margin-top: 3px"
+        />
+        <dic-list-select
+          v-model="listQuery.params.isMust"
+          dic-code="course_need"
+          title="学习类型"
+          class="filter-item"
+          style="margin-top: 3px"
+        />
 
         <el-input
           v-model="listQuery.params.title"
@@ -27,35 +32,46 @@
           prefix-icon="el-icon-search"
           style="width: 200px; margin-left: 10px"
         />
-
       </template>
 
       <template slot="data-columns">
-
         <el-table-column
           label=" "
           prop="title"
           width="150px"
           show-overflow-tooltip
         >
-
           <template slot-scope="scope">
-            <el-image v-if="scope.row.cover!=null && scope.row.cover!=''" :src="scope.row.cover" fit="fill" class="cover" />
-            <el-image v-else :src="require('@/assets/web/images/course_default.jpg')" fit="fill" class="cover" />
+            <el-image
+              v-if="scope.row.cover != null && scope.row.cover != ''"
+              :src="scope.row.cover"
+              fit="fill"
+              class="cover"
+            />
+            <el-image
+              v-else
+              :src="require('@/assets/web/images/course_default.jpg')"
+              fit="fill"
+              class="cover"
+            />
           </template>
-
         </el-table-column>
 
-        <el-table-column
-          label="课程名称"
-          prop="title"
-          show-overflow-tooltip
-        >
+        <el-table-column label="课程名称" prop="title" show-overflow-tooltip>
           <template slot-scope="scope">
-            <detail-link :id="scope.row.id" :title="scope.row.title" @click="handelView" />
-            <el-tag v-if="scope.row.liveCount > 0" style="margin-left: 5px" type="danger" size="mini">直播</el-tag>
+            <detail-link
+              :id="scope.row.id"
+              :title="scope.row.title"
+              @click="handelView"
+            />
+            <el-tag
+              v-if="scope.row.liveCount > 0"
+              style="margin-left: 5px"
+              type="danger"
+              size="mini"
+              >直播</el-tag
+            >
           </template>
-
         </el-table-column>
 
         <el-table-column
@@ -78,23 +94,24 @@
           width="180px"
         >
           <template slot-scope="scope">
-            <el-progress :text-inside="true" :stroke-width="15" :percentage="Number(scope.row.proportion*100).toFixed(0)" color="#c11920" />
+            <el-progress
+              class="progressStyle"
+              :text-inside="true"
+              :stroke-width="15"
+              :percentage="Number(scope.row.proportion * 100).toFixed(0)"
+            />
           </template>
         </el-table-column>
 
-        <el-table-column
-          label="状态"
-          align="center"
-          width="120px"
-        >
-
+        <el-table-column label="状态" align="center" width="120px">
           <template slot-scope="scope">
             <span v-if="scope.row.state == null">未学习</span>
             <span v-else-if="scope.row.state == 0">学习中</span>
-            <span v-else-if="scope.row.state == 1">已学完</span>
+            <span v-else-if="scope.row.state == 1" style="color: #1890ff"
+              >已学完</span
+            >
             <span v-else> -- </span>
           </template>
-
         </el-table-column>
 
         <el-table-column
@@ -105,76 +122,77 @@
         />
       </template>
     </web-table>
-
   </div>
-
 </template>
 
 <script>
-
-import DicListSelect from '@/components/DicListSelect'
-import DetailLink from '@/components/DetailLink'
-import WebTable from '@/components/WebTable'
-import DicCatalogTree from '../../../components/DicTreeSelect'
+import DicListSelect from "@/components/DicListSelect";
+import DetailLink from "@/components/DetailLink";
+import WebTable from "@/components/WebTable";
+import DicCatalogTree from "../../../components/DicTreeSelect";
 
 export default {
-  name: 'UserCourseList',
+  name: "UserCourseList",
   components: { DicCatalogTree, WebTable, DetailLink, DicListSelect },
   props: {
     onlyLearn: {
       type: Boolean,
-      default: false
-    }
+      default: false,
+    },
   },
   data() {
     return {
-
       listQuery: {
         current: 1,
         size: 10,
         params: {
           onlyLearn: this.onlyLearn,
-          learnState: '0'
-        }
+          learnState: "0",
+        },
       },
 
       options: {
         // 列表请求URL
-        listUrl: '/api/course/course/user-paging'
-      }
-    }
+        listUrl: "/api/course/course/user-paging",
+      },
+    };
   },
 
-  created() {
-
-  },
+  created() {},
   methods: {
-
     tabClick(tab) {
-      this.listQuery.params.learnState = tab.name
-      this.$refs.pagingTable.getList()
+      this.listQuery.params.learnState = tab.name;
+      this.$refs.pagingTable.getList();
     },
 
     // 课程详情
     handelView(courseId) {
-      this.$router.push({ name: 'CourseView', params: { courseId: courseId }})
-    }
-
-  }
-}
+      this.$router.push({ name: "CourseView", params: { courseId: courseId } });
+    },
+  },
+};
 </script>
 
 <style scoped>
-
-/deep/
-.el-card__body{
-  padding: 0px
+/deep/ .el-card__body {
+  padding: 0px;
 }
 
-.cover{
+.cover {
   height: 60px;
-  width: 120px
+  width: 120px;
 }
-
 </style>
 
+<style lang="scss">
+.progressStyle {
+  .el-progress-bar__innerText {
+    color: #fff;
+    font-size: 14px;
+  }
+  .el-progress-bar__inner {
+    background-color: unset;
+    background-image: linear-gradient(to right, #3587d8, #6855ff);
+  }
+}
+</style>

+ 81 - 70
exam-06173-vue/src/views/web/exam/list.vue

@@ -1,36 +1,30 @@
 <template>
-
   <div>
-
-    <div v-if="breakShow" style="margin-bottom: 30px; cursor: pointer" @click="continueExam">
-
+    <div
+      v-if="breakShow"
+      style="margin-bottom: 30px; cursor: pointer"
+      @click="continueExam"
+    >
       <el-alert
         :closable="false"
         title="您有正在进行的考试,离线太久考试将被作废哦,点击此处可继续考试!"
         type="error"
       />
-
     </div>
 
-    <el-tabs v-model="listQuery.params.examType" @tab-click="tabClick">
+    <!-- <el-tabs v-model="listQuery.params.examType" @tab-click="tabClick">
       <el-tab-pane name="1" label="正式考试" />
       <el-tab-pane name="2" label="模拟考试" />
-    </el-tabs>
-
-    <web-table
-      ref="pagingTable"
-      :options="options"
-      :list-query="listQuery"
-    >
+    </el-tabs> -->
 
+    <web-table ref="pagingTable" :options="options" :list-query="listQuery">
       <template slot="filter-content">
-
         <el-input
           v-model="listQuery.params.title"
           class="filter-item"
           placeholder="搜索考试名称"
           prefix-icon="el-icon-search"
-          style="width:200px"
+          style="width: 200px"
         />
 
         <el-date-picker
@@ -42,20 +36,33 @@
           start-placeholder="开始日期"
           end-placeholder="结束日期"
         />
+
+        <!-- <dic-list-select
+          class="filter-item"
+          v-model="listQuery.params.examType"
+          dic-code="exam_type"
+        /> -->
       </template>
 
       <template slot="data-columns">
-
-        <el-table-column
-          label="考试名称"
-          show-overflow-tooltip
-        >
+        <el-table-column label="考试名称" show-overflow-tooltip>
           <template slot-scope="scope">
-            <detail-link :id="scope.row.id" :title="scope.row.title" to="CheckExam" />
+            <detail-link
+              :id="scope.row.id"
+              :title="scope.row.title"
+              to="CheckExam"
+            />
           </template>
         </el-table-column>
 
         <el-table-column
+          label="类型"
+          align="center"
+          prop="examType_dictText"
+          width="120px"
+        />
+
+        <el-table-column
           label="时长"
           align="center"
           prop="totalTime"
@@ -76,101 +83,105 @@
           width="120px"
         />
 
-        <el-table-column
-          label="开放时间"
-          align="center"
-        >
-
+        <el-table-column label="开放时间" align="center">
           <template slot-scope="scope">
-            <span v-if="scope.row.timeLimit">{{ scope.row.startTime }} ~ {{ scope.row.endTime }}</span><span v-else>不限时</span>
+            <span v-if="scope.row.timeLimit"
+              >{{ scope.row.startTime }} ~ {{ scope.row.endTime }}</span
+            ><span v-else>不限时</span>
           </template>
-
         </el-table-column>
 
-
-        <el-table-column
-          label="考试状态"
-          align="center"
-          width="120px"
-        >
-
+        <el-table-column label="考试状态" align="center" width="120px">
           <template slot-scope="scope">
-
-            <el-tag v-if="scope.row.state===0" type="success">进行中</el-tag>
-            <el-tag v-if="scope.row.state===1" type="danger" disabled>已禁用</el-tag>
-            <el-tag v-if="scope.row.state===2" type="warning" disabled>未开始</el-tag>
-            <el-tag v-if="scope.row.state===3" disabled>已结束</el-tag>
-
+            <el-tag v-if="scope.row.state === 0" type="success">进行中</el-tag>
+            <el-tag v-if="scope.row.state === 1" type="danger" disabled
+              >已禁用</el-tag
+            >
+            <el-tag v-if="scope.row.state === 2" type="warning" disabled
+              >未开始</el-tag
+            >
+            <el-tag v-if="scope.row.state === 3" disabled>已结束</el-tag>
           </template>
-
         </el-table-column>
 
+        <el-table-column label="关联培训" align="center" width="120px">
+          <template slot-scope="scope">
+            <el-button
+              type="text"
+              size="small"
+              :disabled="scope.row.associate !== 1"
+              :style="`${scope.row.associate === 1 ? 'color:#1890ff;' : ''}`"
+              @click="jump(scope.row)"
+              >{{ scope.row.associate === 1 ? "是" : "否" }}</el-button
+            >
+          </template>
+        </el-table-column>
       </template>
-
     </web-table>
-
   </div>
-
 </template>
 
 <script>
-import { checkProcess } from '@/api/paper/exam'
-import WebTable from '@/components/WebTable'
-import DetailLink from '@/components/DetailLink'
+import { checkProcess } from "@/api/paper/exam";
+import WebTable from "@/components/WebTable";
+import DetailLink from "@/components/DetailLink";
+import DicListSelect from "@/components/DicListSelect";
 
 export default {
-  name: 'OnlineList',
-  components: { DetailLink, WebTable },
+  name: "OnlineList",
+  components: { DetailLink, WebTable, DicListSelect },
   data() {
     return {
-
       detailData: {},
       listQuery: {
         current: 1,
         size: 10,
         params: {
-          examType: '1'
-        }
+          examType: "",
+        },
       },
 
       options: {
         // 列表请求URL
-        listUrl: '/api/exam/exam/online-paging'
+        listUrl: "/api/exam/exam/online-paging",
       },
 
       postForm: {
-        examId: '',
-        password: ''
+        examId: "",
+        password: "",
       },
 
       breakShow: false,
-      breakId: ''
-    }
+      breakId: "",
+    };
   },
 
   created() {
-    checkProcess().then(res => {
+    checkProcess().then((res) => {
       if (res.data && res.data.id) {
-        this.breakShow = true
-        this.breakId = res.data.id
+        this.breakShow = true;
+        this.breakId = res.data.id;
       }
-    })
+    });
   },
   methods: {
-
     tabClick(tab, event) {
-      console.log(tab.name)
-      this.listQuery.params.examType = tab.name
-      this.$refs.pagingTable.getList()
+      this.listQuery.params.examType = tab.name;
+      this.$refs.pagingTable.getList();
     },
 
     /**
      * 继续考试
      */
     continueExam() {
-      this.$router.push({ name: 'StartExam', params: { id: this.breakId }})
-    }
-  }
-}
+      this.$router.push({ name: "StartExam", params: { id: this.breakId } });
+    },
+
+    // 查看课程详情
+    jump(item) {
+      this.$router.push({ path: `/web/course/view/${item.courseId}` });
+    },
+  },
+};
 </script>