[测评系统]--前端(用户答题页面)
linzhijie
2021-05-06 b406c4d779977b66b29766b531c997a9f09479f4
更新最新代码
147个文件已添加
35727 ■■■■■ 已修改文件
.browserslistrc 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.editorconfig 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.dev 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.pre 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.prod 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.test 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintignore 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintrc.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
babel.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist.zip 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 13957 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
postcss.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/dashboard.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/demography.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/examPaper.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/examPaperAnswer.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/login.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/question.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/questionAnswer.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/register.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/subject.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/user.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/401_images/401.gif 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/404_images/404.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/404_images/404_cloud.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/WechatIMG2896.jpeg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/avatar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/carousel/1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/carousel/2.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/carousel/3.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/carousel/4.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/custom-theme/fonts/element-icons.ttf 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/custom-theme/fonts/element-icons.woff 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/custom-theme/index.css 14738 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/custom-theme/indexExtend.css 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/exam-paper/show1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/exam-paper/show2.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/exam-paper/show3.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo-t.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo.jpeg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo2.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/logo2.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/BackToTop/index.vue 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/LangSelect/index.vue 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Pagination/index.vue 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PanThumb/index.vue 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SvgIcon/index.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Ueditor/index.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/index.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/404.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/bug.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/chart.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/clipboard.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/component.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/dashboard.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/documentation.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/drag.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/edit.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/education.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/email.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/exam.svg 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/example.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/excel.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/exit-fullscreen.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/eye-open.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/eye.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/form.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/fullscreen.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/gitee.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/guide.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/home.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/icon.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/international.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/language.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/link.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/list.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/lock.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/message.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/money.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/nested.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/password.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/pdf.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/people.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/peoples.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/qq.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/question.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/search.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/shopping.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/size.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/skill.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/social-github.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/star.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/tab.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/table.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/theme.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/tree-table.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/tree.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/user.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/users.svg 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/wechat.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svg/zip.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/icons/svgo.yml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lang/en.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lang/index.js 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lang/th.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lang/zh.js 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/index.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/indexold.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router.js 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/enumItem.js 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/exam.js 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/styles/index.scss 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/index.js 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.js 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/scroll-to.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/validate.js 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demography/index.vue 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demography/langOption.vue 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demography/login.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/error-page/401.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/error-page/404.vue 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/components/QuestionEdit.vue 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/components/QuestionIntelligenceEdit.vue 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/components/QuestionJobEdit.vue 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/do.vue 295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/doPuzzle.vue 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/exception.vue 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/finish.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/guide.vue 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/intelligence.vue 814 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/job.vue 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/personality.vue 561 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/paper/read.vue 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/paper/index.vue 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/question-error/index.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/record/index.vue 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.browserslistrc
New file
@@ -0,0 +1,2 @@
> 1%
last 2 versions
.editorconfig
New file
@@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
.env
New file
@@ -0,0 +1 @@
timeOut=30
.env.dev
New file
@@ -0,0 +1,4 @@
NODE_ENV = 'development'
VUE_APP_API = 'http://localhost:8001'
VUE_APP_URL = 'http://localhost:8001'
VUE_OTS_URL = 'http://localhost:8090'
.env.pre
New file
@@ -0,0 +1,3 @@
NODE_ENV = 'production'
VUE_APP_API = 'http://localhost:8001'
VUE_APP_URL = 'http://localhost'
.env.prod
New file
@@ -0,0 +1,3 @@
NODE_ENV = 'production'
VUE_APP_API = 'https://www.alvisu.com:8001'
VUE_APP_URL = 'https://www.alvisu.com:8085'
.env.test
New file
@@ -0,0 +1,3 @@
NODE_ENV = 'production'
VUE_APP_API = 'http://10.103.15.180:8001'
VUE_APP_URL = 'http://10.103.15.180'
.eslintignore
New file
@@ -0,0 +1,2 @@
/public/student/components/ueditor
/public/student/components/echarts
.eslintrc.js
New file
@@ -0,0 +1,17 @@
module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/essential',
    '@vue/standard'
  ],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  },
  parserOptions: {
    parser: 'babel-eslint'
  }
}
.gitignore
New file
@@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
babel.config.js
New file
@@ -0,0 +1,5 @@
module.exports = {
  presets: [
    '@vue/app'
  ]
}
dist.zip
Binary files differ
package-lock.json
New file
Diff too large
package.json
New file
@@ -0,0 +1,45 @@
{
  "name": "exam-student",
  "version": "2.0.1",
  "author": "alvis",
  "description": "OTS测评系统",
  "license": "MIT",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve --mode dev",
    "build": "vue-cli-service build --mode prod",
    "build:dev": "vue-cli-service build --mode dev",
    "build:test": "vue-cli-service build --mode test",
    "build:pre": "vue-cli-service build --mode pre",
    "build:prod": "vue-cli-service build --mode prod",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "axios": "^0.19.0",
    "core-js": "^2.6.9",
    "element-ui": "^2.10.1",
    "es6-promise": "^4.2.8",
    "js-cookie": "2.2.0",
    "normalize.css": "8.0.1",
    "nprogress": "0.2.0",
    "vue": "2.6.10",
    "vue-i18n": "^8.15.3",
    "vue-router": "^3.0.3",
    "vue-template-compiler": "^2.6.10",
    "vuex": "3.1.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.9.0",
    "@vue/cli-plugin-eslint": "^3.9.0",
    "@vue/cli-service": "^3.9.0",
    "@vue/eslint-config-standard": "^4.0.0",
    "babel-eslint": "^10.0.1",
    "babel-polyfill": "^6.26.0",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "5.2.2",
    "node-sass": "^4.12.0",
    "sass-loader": "^7.1.0",
    "svg-sprite-loader": "4.1.6",
    "vue-template-compiler": "^2.6.10"
  }
}
postcss.config.js
New file
@@ -0,0 +1,5 @@
module.exports = {
  plugins: {
    autoprefixer: {}
  }
}
public/favicon.ico
public/index.html
New file
@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <meta name="google" content="notranslate" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title></title>
    <style>
        html {
            height: 100%;
            width: 100%;
        }
        body {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }
    </style>
    <!--  <script src='<%= BASE_URL %>/student/components/echarts/echarts.min.js'></script>
      <script src='<%= BASE_URL %>/student/components/echarts/theme/macarons.js'></script>
      <script src='<%= BASE_URL %>/student/components/ueditor/ueditor.config.js'></script>
      <script src='<%= BASE_URL %>/student/components/ueditor/ueditor.all.min.js'></script>
      <script src='<%= BASE_URL %>/student/components/ueditor/lang/zh-cn/zh-cn.js'></script>
      <script src='<%= BASE_URL %>/student/components/ueditor/kityformula-plugin/addKityFormulaDialog.js'></script>
      <script src='<%= BASE_URL %>/student/components/ueditor/kityformula-plugin/getKfContent.js'></script>
      <script src='<%= BASE_URL %>/student/components/ueditor/kityformula-plugin/defaultFilterFix.js'></script>-->
    <script>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement('script')
        // hm.src = 'https://hm.baidu.com/hm.js?cd8218cd51f800ed2b73e5751cb3f4f9'
        var s = document.getElementsByTagName('script')[0]
        s.parentNode.insertBefore(hm, s)
      })()
    </script>
</head>
<body>
<noscript>
    <strong>We're sorry but vue-demo doesn't work properly without JavaScript enabled. Please enable it to
        continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
src/App.vue
New file
@@ -0,0 +1,19 @@
<template>
  <div id="app">
    <router-view />
    <back-to-top :visibility-height="100" :back-position="0" transition-name="fade" ref="backTop"/>
  </div>
</template>
<script>
import BackToTop from '@/components/BackToTop'
export default {
  name: 'App',
  components: {
    BackToTop
  }
}
</script>
src/api/dashboard.js
New file
@@ -0,0 +1,8 @@
import { post, postOts } from '@/utils/request'
export default {
  // index: () => post('/api/student/dashboard/index'),
  index: () => postOts('/api/exam/dashboard/index'),
  // task: () => post('/api/student/dashboard/task')
  task: () => postOts('/api/exam/dashboard/task')
}
src/api/demography.js
New file
@@ -0,0 +1,54 @@
import { getOts, postOts } from '@/utils/request'
// const otsUlr = '/remote/member'
export default {
  demography: (token, langType, query) => getOts(`/remote/member/param/` + token + `/` + langType, query),
  submitDemography: (saveParam, langType, query) => postOts(`/remote/member/save/` + saveParam + '/' + langType, query),
  queryDemographyParamInfo: (token, query) => getOts(`/remote/member/paramInfo/` + token, query),
  queryLangList: (token, reportTemplateId, query) => getOts(`/remote/member/langList/` + token + `/` + reportTemplateId, query),
  queryCreate: function (langType) {
    // 先查询人口学是否已经填完
    let _this = this
    _this.demography(_this.paraMemberToken, langType)
      .then(re => {
        if (re.code !== 1) {
          _this.$message.error(re.message + 'setMemberTocken')
          return
        }
        if (re.response == null || re.response.length === 0) {
          _this.$router.push({path: '/exam/' + _this.doUrl})
          return
        }
        for (let i = 0; i < re.response.length; i++) {
          if (re.response[i].paramValue === null) {
            re.response[i].paramValue = ''
          }
        }
        _this.demographyMenber.data = re.response
        _this.demographyApi.queryDemographyParamInfo(_this.paraMemberToken).then(
          re => {
            if (re.code !== 1) {
              _this.$router.push({path: '/demography/fill/index', query: {demographyMenber: _this.demographyMenber, isStart: false}})
            }
            if (re.response.length === _this.demographyMenber.data.length) {
              _this.$router.push({ path: '/exam/' + _this.doUrl })
              return
            }
            for (let i = 0; i < re.response.length; i++) {
              _this.parasInfoIds[re.response[i].paramId] = re.response[i]
            }
            for (let i = 0; i < _this.demographyMenber.data.length; i++) {
              if (_this.parasInfoIds[_this.demographyMenber.data[i].paramId] !== undefined) {
                _this.demographyMenber.data[i].paramValue = _this.parasInfoIds[_this.demographyMenber.data[i].paramId].paramValue
              }
            }
            _this.$router.push({ path: '/demography/fill/index', query: { demographyMenber: _this.demographyMenber, isStart: false } })
          }
        ).catch(e => {
        })
      }).catch(e => {
        _this.$message.error(_this.$t('userinfoErro'))
      })
  }
}
src/api/examPaper.js
New file
@@ -0,0 +1,10 @@
import { post, postOts } from '@/utils/request'
export default {
  // select: id => post('/api/student/exam/paper/select/' + id),
  select: (tokenId, langType) => postOts('/api/exam/paper/select/' + tokenId + '/' + langType),
  selectTokenId: tokenId => postOts('/api/exam/paper/selectTokenId/' + tokenId),
  recordLogininfor: tokenId => postOts('/api/exam/paper/recordLogininfor/' + tokenId),
  pageList: query => post('/api/student/exam/paper/pageList', query),
  updateTestMemberStatus: tokenId => postOts('/api/exam/exampaper/answer/updateTestMemberStatus/' + tokenId)
}
src/api/examPaperAnswer.js
New file
@@ -0,0 +1,10 @@
import { post, postOts } from '@/utils/request'
export default {
  // pageList: query => post('/api/student/exampaper/answer/pageList', query),
  pageList: query => postOts('/api/exam/exampaper/answer/pageList', query),
  // answerSubmit: form => post('/api/student/exampaper/answer/answerSubmit', form),
  answerSubmit: (form, tokenId) => postOts('/api/exam/exampaper/answer/answerSubmit/' + tokenId, form),
  read: id => post('/api/student/exampaper/answer/read/' + id),
  edit: form => post('/api/student/exampaper/answer/edit', form)
}
src/api/login.js
New file
@@ -0,0 +1,7 @@
import { post, postWithLoadTip, postWithLoadTipOts } from '@/utils/request'
export default {
  // login: query => postWithLoadTip(`/api/user/login`, query),
  login: query => postWithLoadTipOts(`/api/exam/examUser/login`, query),
  logout: query => post(`/api/user/logout`, query)
}
src/api/question.js
New file
@@ -0,0 +1,4 @@
import { post } from '@/utils/request'
export default {
}
src/api/questionAnswer.js
New file
@@ -0,0 +1,6 @@
import { post } from '@/utils/request'
export default {
  pageList: query => post('/api/student/question/answer/page', query),
  select: id => post('/api/student/question/answer/select/' + id)
}
src/api/register.js
New file
@@ -0,0 +1,5 @@
import { postWithLoadTip } from '@/utils/request'
export default {
  register: query => postWithLoadTip(`/api/student/user/register`, query)
}
src/api/subject.js
New file
@@ -0,0 +1,6 @@
import { post } from '@/utils/request'
export default {
  list: query => post('/api/student/education/subject/list'),
  select: id => post('/api/student/education/subject/select/' + id)
}
src/api/user.js
New file
@@ -0,0 +1,12 @@
import { post, postOts } from '@/utils/request'
export default {
  createUser: query => post('/api/student/user/edit', query),
  getCurrentUser: () => postOts('/api/exam/examUser/current'),
  getUserEvent: () => post('/api/student/user/log'),
  update: query => post('/api/student/user/update', query),
  messagePageList: query => post('/api/student/user/message/page', query),
  read: id => post('/api/student/user/message/read/' + id),
  // getMessageCount: () => post('/api/student/user/message/unreadCount')
  getMessageCount: () => postOts('/api/exam/examUser/message/unreadCount')
}
src/assets/401_images/401.gif
src/assets/404_images/404.png
src/assets/404_images/404_cloud.png
src/assets/WechatIMG2896.jpeg
src/assets/avatar.png
src/assets/carousel/1.png
src/assets/carousel/2.png
src/assets/carousel/3.png
src/assets/carousel/4.png
src/assets/custom-theme/fonts/element-icons.ttf
Binary files differ
src/assets/custom-theme/fonts/element-icons.woff
Binary files differ
src/assets/custom-theme/index.css
New file
Diff too large
src/assets/custom-theme/indexExtend.css
New file
@@ -0,0 +1,10 @@
@charset "UTF-8";
.el-loading-spinner .el-loading-text {
    color: #2b90ff;
    margin: 3px 0;
    font-size: x-large;
}
.el-radio, .el-radio--medium.is-bordered .el-radio__label {
    font-size: 31px
}
src/assets/exam-paper/show1.png
src/assets/exam-paper/show2.png
src/assets/exam-paper/show3.png
src/assets/logo-t.jpg
src/assets/logo.jpeg
src/assets/logo.png
src/assets/logo2.jpg
src/assets/logo2.png
src/components/BackToTop/index.vue
New file
@@ -0,0 +1,129 @@
<template>
  <transition :name="transitionName">
    <div v-show="visible"  class="back-to-ceiling" @click="backToTop">
      <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px">
        <path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" />
      </svg>
    </div>
  </transition>
</template>
<script>
export default {
  name: 'BackToTop',
  props: {
    visibilityHeight: {
      type: Number,
      default: 400
    },
    backPosition: {
      type: Number,
      default: 0
    },
    customStyle: {
      type: Object,
      default: function () {
        return {
          right: '20px',
          bottom: '20px',
          width: '30px',
          height: '30px',
          'border-radius': '4px',
          'line-height': '35px',
          background: '#e7eaf1'
        }
      }
    },
    transitionName: {
      type: String,
      default: 'fade'
    }
  },
  data () {
    return {
      visible: false,
      interval: null,
      isMoving: false
    }
  },
  mounted () {
    window.addEventListener('scroll', this.handleScroll)
  },
  beforeDestroy () {
    window.removeEventListener('scroll', this.handleScroll)
    if (this.interval) {
      clearInterval(this.interval)
    }
  },
  methods: {
    handleScroll () {
      this.visible = window.pageYOffset > this.visibilityHeight
    },
    backToTop () {
      if (this.isMoving) return
      const start = window.pageYOffset
      let i = 0
      this.isMoving = true
      this.interval = setInterval(() => {
        const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
        if (next <= this.backPosition) {
          window.scrollTo(0, this.backPosition)
          clearInterval(this.interval)
          this.isMoving = false
        } else {
          window.scrollTo(0, next)
        }
        i++
      }, 5)
    },
    easeInOutQuad (t, b, c, d) {
      if ((t /= d / 2) < 1) return c / 2 * t * t + b
      return -c / 2 * (--t * (t - 2) - 1) + b
    }
  }
}
</script>
<style scoped>
.back-to-ceiling {
/*  position: fixed;
  display: inline-block;
  text-align: center;
  cursor: pointer;*/
  right: 20px;
  bottom: 7%;
  line-height: 35px;
  background: #fff;
  position: fixed;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: #409eff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  box-shadow: 0 0 6px rgba(0,0,0,.12);
  cursor: pointer;
  z-index: 5;
}
.back-to-ceiling:hover {
  background: #d5dbe7;
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0
}
.back-to-ceiling .Icon {
  fill: #409eff;
  background: none;
}
</style>
src/components/LangSelect/index.vue
New file
@@ -0,0 +1,55 @@
<template>
  <el-dropdown trigger="click" class="international" @command="handleSetLanguage">
    <div style="color:white">
      <svg-icon class-name="international-icon" icon-class="language" style="color:white"/>
       LANGUAGE
    </div>
    <el-dropdown-menu slot="dropdown">
      <template v-for="item in langType">
        <el-dropdown-item :command="item.dictValue">
          {{item.dictLabel}}
        </el-dropdown-item>
      </template>
    </el-dropdown-menu>
  </el-dropdown>
</template>
<script>
import demographyApi from '@/api/demography'
import { mapState, mapMutations } from 'vuex'
export default {
  data () {
    return {
      langType: []
    }
  },
  created () {
    let _this = this
    demographyApi.queryLangList(_this.memberToken).then(function (result) {
      if (result && result.code === 1) {
        _this.langType = result.response
      }
    }
    ).catch(e => {
      _this.$message.error(_this.$t('noquestionMsg'))
    })
  },
  computed: {
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('user', { langType: state => state.langType })
  },
  methods: {
    handleSetLanguage (lang) {
      let _this = this
      _this.setLangType(lang)
      if (lang === 'Chinese') {
        _this.$i18n.locale = 'zh'
      } else {
        _this.$i18n.locale = 'en'
      }
    },
    ...mapMutations('user', ['setLangType'])
  }
}
</script>
src/components/Pagination/index.vue
New file
@@ -0,0 +1,100 @@
<template>
  <div :class="{'hidden':hidden}" class="pagination-container">
    <el-pagination
      :background="background"
      :current-page.sync="currentPage"
      :page-size.sync="pageSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :total="total"
      v-bind="$attrs"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>
<script>
import { scrollTo } from '@/utils/scroll-to'
export default {
  name: 'Pagination',
  props: {
    total: {
      required: true,
      type: Number
    },
    page: {
      type: Number,
      default: 1
    },
    limit: {
      type: Number,
      default: 10
    },
    pageSizes: {
      type: Array,
      default () {
        return [5, 10, 20, 30, 50]
      }
    },
    layout: {
      type: String,
      default: 'prev, pager, next'
    },
    background: {
      type: Boolean,
      default: true
    },
    autoScroll: {
      type: Boolean,
      default: true
    },
    hidden: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    currentPage: {
      get () {
        return this.page
      },
      set (val) {
        this.$emit('update:page', val)
      }
    },
    pageSize: {
      get () {
        return this.limit
      },
      set (val) {
        this.$emit('update:limit', val)
      }
    }
  },
  methods: {
    handleSizeChange (val) {
      this.$emit('pagination', { page: this.currentPage, limit: val })
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    },
    handleCurrentChange (val) {
      this.$emit('pagination', { page: val, limit: this.pageSize })
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    }
  }
}
</script>
<style scoped>
.pagination-container {
  background: #fff;
}
.pagination-container.hidden {
  display: none;
}
</style>
src/components/PanThumb/index.vue
New file
@@ -0,0 +1,140 @@
<template>
  <div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
    <div class="pan-info">
      <div class="pan-info-roles-container">
        <slot />
      </div>
    </div>
    <img :src="image" class="pan-thumb">
  </div>
</template>
<script>
export default {
  name: 'PanThumb',
  props: {
    image: {
      type: String,
      required: true
    },
    zIndex: {
      type: Number,
      default: 1
    },
    width: {
      type: String,
      default: '150px'
    },
    height: {
      type: String,
      default: '150px'
    }
  }
}
</script>
<style scoped>
.pan-item {
  width: 200px;
  height: 200px;
  border-radius: 50%;
  display: inline-block;
  position: relative;
  cursor: default;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.pan-info-roles-container {
  padding: 20px;
  text-align: center;
}
.pan-thumb {
  width: 100%;
  height: 100%;
  background-size: 100%;
  border-radius: 50%;
  overflow: hidden;
  position: absolute;
  transform-origin: 95% 40%;
  transition: all 0.3s ease-in-out;
}
.pan-thumb:after {
  content: '';
  width: 8px;
  height: 8px;
  position: absolute;
  border-radius: 50%;
  top: 40%;
  left: 95%;
  margin: -4px 0 0 -4px;
  background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
  box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
}
.pan-info {
  position: absolute;
  width: inherit;
  height: inherit;
  border-radius: 50%;
  overflow: hidden;
  box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
}
.pan-info h3 {
  color: #fff;
  text-transform: uppercase;
  position: relative;
  letter-spacing: 2px;
  font-size: 18px;
  margin: 0 60px;
  padding: 22px 0 0 0;
  height: 85px;
  font-family: 'Open Sans', Arial, sans-serif;
  text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
}
.pan-info p {
  color: #fff;
  padding: 10px 5px;
  font-style: italic;
  margin: 0 30px;
  font-size: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.5);
}
.pan-info p a {
  display: block;
  color: #333;
  width: 80px;
  height: 80px;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  color: #fff;
  font-style: normal;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 9px;
  letter-spacing: 1px;
  padding-top: 24px;
  margin: 7px auto 0;
  font-family: 'Open Sans', Arial, sans-serif;
  opacity: 0;
  transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
  transform: translateX(60px) rotate(90deg);
}
.pan-info p a:hover {
  background: rgba(255, 255, 255, 0.5);
}
.pan-item:hover .pan-thumb {
  transform: rotate(-110deg);
}
.pan-item:hover .pan-info p a {
  opacity: 1;
  transform: translateX(0px) rotate(0deg);
}
</style>
src/components/SvgIcon/index.vue
New file
@@ -0,0 +1,62 @@
<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  computed: {
    isExternal () {
      return isExternal(this.iconClass)
    },
    iconName () {
      return `#icon-${this.iconClass}`
    },
    svgClass () {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    },
    styleExternalIcon () {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
      }
    }
  }
}
</script>
<style scoped>
  .svg-icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
  }
  .svg-external-icon {
    background-color: currentColor;
    mask-size: cover!important;
    display: inline-block;
  }
</style>
src/components/Ueditor/index.vue
New file
@@ -0,0 +1,65 @@
<template>
  <div>
    <script :id="randomId" type="text/plain" style="height: 300px;"></script>
  </div>
</template>
<script>
export default {
  name: 'UE',
  props: {
    value: {
      default: function () {
        return ''
      }
    }
  },
  data () {
    return {
      randomId: 'editor_' + Math.random() * 100000000000000000,
      // 编辑器实例
      instance: null,
      ready: false
    }
  },
  watch: {
    value: function (val, oldVal) {
      if (val != null && this.ready) {
        // eslint-disable-next-line no-undef
        this.instance = UE.getEditor(this.randomId)
        this.instance.setContent(val)
      }
    }
  },
  mounted () {
    this.initEditor()
  },
  beforeDestroy () {
    if (this.instance !== null && this.instance.destroy) {
      this.instance.destroy()
    }
  },
  methods: {
    initEditor () {
      this.$nextTick(() => {
        // eslint-disable-next-line no-undef
        this.instance = UE.getEditor(this.randomId)
        this.instance.addListener('ready', () => {
          this.ready = true
          this.$emit('ready', this.instance)
        })
      })
    },
    getUEContent () {
      return this.instance.getContent()
    },
    setText (con) {
      // eslint-disable-next-line no-undef
      this.instance = UE.getEditor(this.randomId)
      this.instance.setContent(con)
    }
  }
}
</script>
src/icons/index.js
New file
@@ -0,0 +1,9 @@
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
src/icons/svg/404.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>
src/icons/svg/bug.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>
src/icons/svg/chart.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg>
src/icons/svg/clipboard.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg>
src/icons/svg/component.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h54.857v54.857H0V0zm0 73.143h54.857V128H0V73.143zm73.143 0H128V128H73.143V73.143zm27.428-18.286C115.72 54.857 128 42.577 128 27.43 128 12.28 115.72 0 100.571 0 85.423 0 73.143 12.28 73.143 27.429c0 15.148 12.28 27.428 27.428 27.428z"/></svg>
src/icons/svg/dashboard.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg>
src/icons/svg/documentation.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M71.984 44.815H115.9L71.984 9.642v35.173zM16.094.05h63.875l47.906 38.37v76.74c0 3.392-1.682 6.645-4.677 9.044-2.995 2.399-7.056 3.746-11.292 3.746H16.094c-4.236 0-8.297-1.347-11.292-3.746-2.995-2.399-4.677-5.652-4.677-9.044V12.84C.125 5.742 7.23.05 16.094.05zm71.86 102.32V89.58h-71.86v12.79h71.86zm23.952-25.58V64H16.094v12.79h95.812z"/></svg>
src/icons/svg/drag.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M73.137 29.08h-9.209 29.7L63.886.093 34.373 29.08h20.49v27.035H27.238v17.948h27.625v27.133h18.274V74.063h27.41V56.115h-27.41V29.08zm-9.245 98.827l27.518-26.711H36.59l27.302 26.71zM.042 64.982l27.196 27.029V38.167L.042 64.982zm100.505-26.815V92.01l27.41-27.029-27.41-26.815z"/></svg>
src/icons/svg/edit.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>
src/icons/svg/education.svg
New file
@@ -0,0 +1,5 @@
<svg width="1024" height="1024"
    xmlns="http://www.w3.org/2000/svg">
    <path d="M832 1024h-640c-70.656 0-128-57.344-128-128V128c0-70.656 57.344-128 128-128h640c70.656 0 128 57.344 128 128v768c0 70.656-57.344 128-128 128zM192 64C156.672 64 128 92.672 128 128v768c0 35.328 28.672 64 64 64h640c35.328 0 64-28.672 64-64V128c0-35.328-28.672-64-64-64h-640z" ></path>
    <path d="M256 992.256c-17.92 0-32.256-14.336-32.256-32.256v-896c0-17.92 14.336-31.744 32.256-31.744s32.256 14.336 32.256 31.744v896c-0.512 17.92-14.336 32.256-32.256 32.256zM544.256 384c-10.24 0-20.48-2.56-30.208-7.68-20.992-11.264-33.792-32.768-33.792-56.32v-256c0-17.92 14.336-31.744 32.256-31.744s32.256 14.336 32.256 31.744v256l78.336-52.224c10.752-7.168 24.576-7.168 35.328 0l78.336 52.224v-256c0-17.92 14.336-31.744 32.256-31.744s32.256 14.336 32.256 31.744v256c0 35.328-28.672 64-64 64-12.8 0-25.088-3.584-35.328-10.752L640 332.8l-60.416 40.448c-10.752 7.168-23.04 10.752-35.328 10.752zM768 832h-256c-17.92 0-32.256-14.336-32.256-32.256s14.336-32.256 32.256-32.256h256c17.92 0 32.256 14.336 32.256 32.256s-14.336 32.256-32.256 32.256z" ></path>
</svg>
src/icons/svg/email.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg>
src/icons/svg/exam.svg
New file
@@ -0,0 +1,7 @@
<svg width="1024" height="1024"
    xmlns="http://www.w3.org/2000/svg">
    <path d="M960.00781812 49.59100906H232.7603225a76.54434375 76.54434375 0 0 0-76.55607094 76.55607094v695.14976812H49.59100906v76.55607188a76.55607094 76.55607094 0 0 0 76.55607094 76.55607094h719.03835844a76.55607094 76.55607094 0 0 0 76.55607187-76.55607094V202.75006063h114.83410688V126.14708A76.55607094 76.55607094 0 0 0 960.00781812 49.59100906z m-833.81382843 886.58685563a38.27803594 38.27803594 0 0 1-38.26630782-38.27803594v-38.32494469h642.42364969v38.26630782a76.12215937 76.12215937 0 0 0 10.26142219 38.27803593zM883.45174719 126.14708v771.65893031a38.27803594 38.27803594 0 0 1-38.26630875 38.27803594h-38.27803594a38.27803594 38.27803594 0 0 1-38.28976219-38.23112625v-76.55607188H194.48228656V126.14708a38.27803594 38.27803594 0 0 1 38.27803594-38.32494469H893.72489656a76.22770594 76.22770594 0 0 0-10.26142218 38.26630875z m114.83410687 38.27803594h-76.55607187V126.14708a38.27803594 38.27803594 0 0 1 76.55607187 0z m-669.83044219 79.64036156h191.37845063a19.1390175 19.1390175 0 1 0 0-38.27803594H328.45541188a19.1390175 19.1390175 0 1 0-1e-8 38.27803594z m0 153.10041562h421.01148282a19.1390175 19.1390175 0 0 0 0-38.27803593h-421.01148282a19.1390175 19.1390175 0 1 0 0 38.27803593z m0 153.10041563h267.9345225a19.1390175 19.1390175 0 1 0 0-38.26630875H328.45541188a19.1390175 19.1390175 0 1 0-1e-8 38.26630875z m0 153.11214187h421.01148282a19.1390175 19.1390175 0 0 0 0-38.278035h-421.01148282a19.1390175 19.1390175 0 1 0 0 38.278035z m0 0" p-id="748"></path>
    <path d="M845.18543844 977.34082625H126.14708a79.58172469 79.58172469 0 0 1-79.48790625-79.48790625v-79.48790625h106.62496969V126.14708A79.5699975 79.5699975 0 0 1 232.77204969 46.65917375h727.23576843a79.58172469 79.58172469 0 0 1 79.51136063 79.48790625v79.48790625h-114.85756125v692.21793375a79.58172469 79.58172469 0 0 1-79.47617906 79.48790625zM52.52284344 824.22868344v73.62423656a73.70632781 73.70632781 0 0 0 73.62423656 73.62423656h719.03835844a73.70632781 73.70632781 0 0 0 73.62423656-73.62423656V199.75958937h114.84583406V126.14708a73.70632781 73.70632781 0 0 0-73.64769094-73.62423656H232.7603225a73.70632781 73.70632781 0 0 0-73.62423656 73.62423656v698.08160344z m792.662595 114.83410687h-38.27803594a41.25678 41.25678 0 0 1-41.20987031-41.20987031v-73.62423656H191.55045219V126.14708a41.25678 41.25678 0 0 1 41.20987031-41.19814313h666.03078469l-2.54483344 4.3977525a73.33105312 73.33105312 0 0 0-9.87441938 36.80039063v771.65893031a41.25678 41.25678 0 0 1-41.18641593 41.25678zM197.41412187 818.36501375h574.14708v79.48790625a35.39311031 35.39311031 0 0 0 35.34620063 35.34620062h38.27803594a35.38138312 35.38138312 0 0 0 35.33447343-35.34620062v-771.65893031a79.21817719 79.21817719 0 0 1 8.29122938-35.33447344H232.7603225a35.38138312 35.38138312 0 0 0-35.34620062 35.33447344z m548.30002407 120.69777656H126.14708a41.25678 41.25678 0 0 1-41.23332469-41.20987031v-41.20987125h648.36941063v41.19814312a73.27241625 73.27241625 0 0 0 9.87442031 36.81211875zM90.777425 862.50671844v35.33447344a35.38138312 35.38138312 0 0 0 35.33447344 35.34620156h609.59882719a79.13608594 79.13608594 0 0 1-8.29122938-35.34620156v-35.33447344z m658.71292406-156.1964325h-421.01148281a22.07085281 22.07085281 0 1 1 0-44.14170563h421.01148281a22.07085281 22.07085281 0 0 1 0 44.14170563z m-421.01148281-38.27803594a16.20718313 16.20718313 0 1 0 0 32.41436625h421.01148281a16.20718313 16.20718313 0 0 0 0-32.41436625z m267.9345225-114.83410688H328.45541188a22.07085281 22.07085281 0 1 1-1e-8-44.1299775h267.92279438a22.07085281 22.07085281 0 1 1 0 44.1299775zM328.45541188 514.93183437a16.20718313 16.20718313 0 1 0-1e-8 32.40263907h267.92279438a16.20718313 16.20718313 0 1 0 0-32.40263907z m421.01148281-114.83410687h-421.01148282a22.07085281 22.07085281 0 1 1 1e-8-44.14170469h421.01148281a22.07085281 22.07085281 0 0 1 0 44.14170469z m-421.01148282-38.278035a16.20718313 16.20718313 0 1 0 1e-8 32.41436531h421.01148281a16.20718313 16.20718313 0 0 0 0-32.41436531z m191.37845063-114.82237969H328.45541188a22.07085281 22.07085281 0 1 1-1e-8-44.14170562h191.37845063a22.07085281 22.07085281 0 0 1 0 44.14170562z m-191.37845062-38.27803593a16.20718313 16.20718313 0 1 0-1e-8 32.41436625h191.37845063a16.20718313 16.20718313 0 0 0 0-32.41436625z m672.76227656-41.37405375h-82.41974063V126.14708a41.20987031 41.20987031 0 0 1 82.41974063 0z m-76.55607094-5.86366969h70.69240125V126.14708a35.34620062 35.34620062 0 0 0-70.69240125 0z" ></path>
    <path d="M845.18543844 989.06816563H126.14708A91.32079125 91.32079125 0 0 1 34.93183437 897.85292v-91.21524562h106.62496969V126.14708A91.30906406 91.30906406 0 0 1 232.77204969 34.93183437h727.23576843a91.32079125 91.32079125 0 0 1 91.2387 91.21524563v91.21524562h-114.85756125v680.49059438a91.32079125 91.32079125 0 0 1-91.20351843 91.21524562zM64.25018281 835.95602281v61.89689719a61.96726125 61.96726125 0 0 0 61.89689719 61.89689719h719.03835844a61.96726125 61.96726125 0 0 0 61.89689718-61.89689719V188.03225h114.84583407V126.14708a61.96726125 61.96726125 0 0 0-61.92035157-61.89689719H232.7603225a61.95553406 61.95553406 0 0 0-61.89689719 61.89689719v709.80894281z m780.93525563 114.83410688h-38.27803594a52.99584656 52.99584656 0 0 1-52.93720969-52.93720969v-61.89689719H179.82311281V126.14708a52.99584656 52.99584656 0 0 1 52.93720969-52.9254825h686.40117281l-12.759345 22.01221594a61.59198656 61.59198656 0 0 0-8.29122937 30.90153937v771.71756719a52.99584656 52.99584656 0 0 1-52.9254825 52.93720969zM209.14146125 806.63767438h574.14708v91.21524562a23.64231656 23.64231656 0 0 0 23.61886125 23.61886125h38.27803594a23.64231656 23.64231656 0 0 0 23.60713406-23.61886125v-771.65893031a90.83997094 90.83997094 0 0 1 3.107745-23.60713406h-639.139995a23.64231656 23.64231656 0 0 0-23.61886125 23.60713406z m556.91961844 144.15245531H126.14708a52.99584656 52.99584656 0 0 1-52.96066406-52.93720969v-52.93721062h671.82408937v52.9254825a61.54507687 61.54507687 0 0 0 8.29122938 30.92499468zM102.50476437 874.23405781v23.60713406a23.64231656 23.64231656 0 0 0 23.60713407 23.61886219h592.68800344a90.73442437 90.73442437 0 0 1-3.107745-23.64231656v-23.58367969z m646.98558469-156.1964325h-421.01148281a33.79819219 33.79819219 0 1 1 0-67.59638437h421.01148281a33.79819219 33.79819219 0 0 1 0 67.59638437z m-421.01148281-38.27803594a4.47984375 4.47984375 0 1 0 0 8.9596875h421.01148281a4.47984375 4.47984375 0 0 0 0-8.9596875z m267.9345225-114.83410687H328.45541188a33.79819219 33.79819219 0 1 1-1e-8-67.58465625h267.92279438a33.79819219 33.79819219 0 1 1 0 67.58465625zM328.45541188 526.65917375a4.47984375 4.47984375 0 1 0-1e-8 8.94796031h267.92279438a4.47984375 4.47984375 0 1 0 0-8.94796031z m421.01148281-114.83410688h-421.01148282a33.79819219 33.79819219 0 1 1 1e-8-67.59638343h421.01148281a33.79819219 33.79819219 0 0 1 0 67.59638343z m-421.01148282-38.278035a4.47984375 4.47984375 0 1 0 1e-8 8.95968657h421.01148281a4.47984375 4.47984375 0 0 0 0-8.95968657z m191.37845063-114.82237968H328.45541188a33.79819219 33.79819219 0 1 1-1e-8-67.59638438h191.37845063a33.79819219 33.79819219 0 0 1 0 67.59638438z m-191.37845062-38.27803594a4.47984375 4.47984375 0 1 0-1e-8 8.9596875h191.37845063a4.47984375 4.47984375 0 0 0 0-8.9596875z m684.48961593-41.37405375h-105.87441937V126.14708a52.93720969 52.93720969 0 0 1 105.87441937 0z m-76.55607093-29.31834844h47.2377225V126.14708a23.61886125 23.61886125 0 0 0-47.2377225 0z"></path>
    <path d="M845.18543844 992H126.14708A94.25262656 94.25262656 0 0 1 32 897.85292v-94.14708094h106.62496969V126.14708A94.25262656 94.25262656 0 0 1 232.77204969 32h727.23576843a94.25262656 94.25262656 0 0 1 94.14708094 94.14708v94.19398969h-114.83410781v677.51185031a94.25262656 94.25262656 0 0 1-94.13535281 94.14708zM37.86366969 809.56950875v88.28341125a88.37722969 88.37722969 0 0 0 88.28341031 88.28341031h719.03835844a88.37722969 88.37722969 0 0 0 88.28341125-88.28341031V214.4774h114.83410687V126.14708A88.37722969 88.37722969 0 0 0 960.00781812 37.86366969H232.7603225a88.37722969 88.37722969 0 0 0-88.28341031 88.28341031v683.42242875zM845.18543844 962.68165156H126.14708A64.89909563 64.89909563 0 0 1 61.31834844 897.85292v-64.8287325h106.62496968V126.14708A64.89909563 64.89909563 0 0 1 232.77204969 61.31834844h727.23576843a64.89909563 64.89909563 0 0 1 64.8287325 64.82873156v64.87564219h-114.83410781v706.83019781a64.89909563 64.89909563 0 0 1-64.81700437 64.82873156zM67.18201813 838.88785719v58.96506281a59.03542594 59.03542594 0 0 0 58.96506187 58.96506188h719.03835844a59.03542594 59.03542594 0 0 0 58.96506281-58.96506188V185.1590525h114.83410687V126.14708A59.03542594 59.03542594 0 0 0 960.00781812 67.18201813H232.7603225a59.02369875 59.02369875 0 0 0-58.96506187 58.96506187v712.74077719z m778.00342031 114.83410687h-38.27803594a55.92768187 55.92768187 0 0 1-55.88077125-55.86904406v-58.96506281H176.8912775V126.14708a55.92768187 55.92768187 0 0 1 55.869045-55.91595375h691.49083781l-15.3041775 26.40996844a58.63669688 58.63669688 0 0 0-7.89249937 29.43562125v771.77620406a55.92768187 55.92768187 0 0 1-55.869045 55.86904406zM182.75494719 833.0241875h574.13535375v64.8287325a50.06401219 50.06401219 0 0 0 50.00537437 50.00537438h38.27803594a50.06401219 50.06401219 0 0 0 49.99364719-50.00537438V126.12362562a64.50036656 64.50036656 0 0 1 8.68995937-32.37918374l10.20278438-17.59100907h-681.35841657a50.06401219 50.06401219 0 0 0-50.00537531 49.99364719z m588.39579843 120.69777656h-645.00366562a55.92768187 55.92768187 0 0 1-55.85731688-55.86904406v-55.869045h677.6525775v55.85731688a58.63669688 58.63669688 0 0 0 7.89249938 29.45907656zM76.14170563 847.84754469v49.99364719a50.06401219 50.06401219 0 0 0 49.99364718 50.00537531H760.97141469l-10.2145125-17.59100906a64.50036656 64.50036656 0 0 1-8.68995844-32.40263813v-50.00537531z m769.04373281 76.55607094h-38.27803594a26.58587813 26.58587813 0 0 1-26.56242281-26.55069563v-88.28341125H206.20962594V126.14708a26.57415094 26.57415094 0 0 1 26.55069656-26.59760531h642.96310781l-0.98509687 3.69411187a87.82604437 87.82604437 0 0 0-3.00219844 22.8448575v771.65892938a26.57415094 26.57415094 0 0 1-26.55069656 26.65624218zM212.07329563 803.70583906h574.13535374v94.14708094a20.71048125 20.71048125 0 0 0 20.68702594 20.68702594h38.27803594a20.71048125 20.71048125 0 0 0 20.67529969-20.68702594v-771.65893031a93.66625969 93.66625969 0 0 1 2.3454675-20.67529969H232.7603225a20.71048125 20.71048125 0 0 0-20.68702687 20.67529969zM722.62301469 924.40361563H126.14708a26.57415094 26.57415094 0 0 1-26.53896844-26.55069563v-26.55069656h619.01588063v26.52724125a87.77913469 87.77913469 0 0 0 3.00219843 22.88003906zM105.46005406 877.16589313v20.67529875a20.71048125 20.71048125 0 0 0 20.67529875 20.68702687h588.92352844a93.60762281 93.60762281 0 0 1-2.34546844-20.71048125v-20.65184437z m644.030295-156.19643344h-421.01148281a36.73002656 36.73002656 0 1 1 0-73.46005313h421.01148281a36.73002656 36.73002656 0 0 1 0 73.46005313z m-421.01148281-67.59638344a30.86635688 30.86635688 0 1 0 0 61.73271375h421.01148281a30.86635688 30.86635688 0 0 0 0-61.73271375z m421.01148281 38.278035h-421.01148281a7.41167812 7.41167812 0 1 1 0-14.82335625h421.01148281a7.41167812 7.41167812 0 0 1 0 14.82335625z m-421.01148281-8.95968656a1.54800844 1.54800844 0 1 0 0 3.09601687h421.01148281a1.54800844 1.54800844 0 0 0 0-3.09601687z m267.9345225-114.83410688H328.45541188a36.73002656 36.73002656 0 1 1-1e-8-73.44832687h267.92279438a36.73002656 36.73002656 0 1 1 0 73.44832687zM328.45541188 500.27266063a30.86635688 30.86635688 0 1 0-1e-8 61.72098749h267.92279438a30.86635688 30.86635688 0 1 0 0-61.7209875z m267.9345225 38.26630875H328.45541188a7.41167812 7.41167812 0 1 1-1e-8-14.81163h267.92279438a7.41167812 7.41167812 0 1 1 0 14.81163z m-267.93452251-8.94796032a1.54800844 1.54800844 0 1 0 1e-8 3.08429063h267.92279437a1.54800844 1.54800844 0 1 0 0-3.08429063z m421.01148282-114.83410687h-421.01148282a36.73002656 36.73002656 0 1 1 1e-8-73.46005407h421.01148281a36.73002656 36.73002656 0 0 1 0 73.46005407z m-421.01148282-67.59638438a30.86635688 30.86635688 0 1 0 1e-8 61.73271469h421.01148281a30.86635688 30.86635688 0 0 0 0-61.73271469z m421.01148282 38.27803594h-421.01148282a7.41167812 7.41167812 0 1 1 1e-8-14.82335719h421.01148281a7.41167812 7.41167812 0 0 1 0 14.82335719z m-421.01148282-8.9596875a1.54800844 1.54800844 0 1 0 1e-8 3.09601781h421.01148281a1.54800844 1.54800844 0 0 0 0-3.09601781z m191.37845063-114.82237969H328.45541188a36.73002656 36.73002656 0 1 1-1e-8-73.46005406h191.37845063a36.73002656 36.73002656 0 0 1 0 73.46005406z m-191.37845062-67.59638437a30.86635688 30.86635688 0 1 0-1e-8 61.73271468h191.37845063a30.86635688 30.86635688 0 0 0 0-61.73271468z m191.37845062 38.27803593H328.45541188a7.41167812 7.41167812 0 1 1-1e-8-14.82335718h191.37845063a7.41167812 7.41167812 0 0 1 0 14.82335718z m-191.37845062-8.9596875a1.54800844 1.54800844 0 1 0-1e-8 3.09601782h191.37845063a1.54800844 1.54800844 0 0 0 0-3.09601782z m687.42145125-41.37405281h-111.73809V126.14708a55.869045 55.869045 0 0 1 111.73809 0z m-105.87442032-5.86366969h100.01075063V126.14708a50.00537531 50.00537531 0 0 0-100.01075063 0z m76.55607188-23.45467875h-53.10139313V126.14708a26.55069656 26.55069656 0 0 1 53.10139313 0z m-47.23772344-5.86366968h41.37405375V126.14708a20.68702687 20.68702687 0 0 0-41.37405375 0z" ></path>
</svg>
src/icons/svg/example.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>
src/icons/svg/excel.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg>
src/icons/svg/exit-fullscreen.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>
src/icons/svg/eye-open.svg
New file
@@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>
src/icons/svg/eye.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>
src/icons/svg/form.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.068 23.784c-1.02 0-1.877-.32-2.572-.96a8.588 8.588 0 0 1-1.738-2.237 11.524 11.524 0 0 1-1.042-2.621c-.232-.895-.348-1.641-.348-2.238V0h.278c.834 0 1.622.085 2.363.256.742.17 1.645.575 2.711 1.214 1.066.64 2.363 1.535 3.892 2.686 1.53 1.15 3.453 2.664 5.77 4.54 2.502 2.045 4.494 3.771 5.977 5.178 1.483 1.406 2.618 2.6 3.406 3.58.787.98 1.274 1.812 1.46 2.494.185.682.277 1.278.277 1.79v2.046H84.068zM127.3 84.01c.278.682.464 1.535.556 2.558.093 1.023-.37 2.003-1.39 2.94-.463.427-.88.832-1.25 1.215-.372.384-.696.704-.974.96a6.69 6.69 0 0 1-.973.767l-11.816-10.741a44.331 44.331 0 0 0 1.877-1.535 31.028 31.028 0 0 1 1.737-1.406c1.112-.938 2.317-1.343 3.615-1.215 1.297.128 2.363.405 3.197.83.927.427 1.923 1.173 2.989 2.239 1.065 1.065 1.876 2.195 2.432 3.388zM78.23 95.902c2.038 0 3.752-.511 5.143-1.534l-26.969 25.83H18.037c-1.761 0-3.684-.47-5.77-1.407a24.549 24.549 0 0 1-5.838-3.709 21.373 21.373 0 0 1-4.518-5.306c-1.204-2.003-1.807-4.07-1.807-6.202V16.495c0-1.79.44-3.665 1.32-5.626A18.41 18.41 0 0 1 5.04 5.562a21.798 21.798 0 0 1 5.213-3.964C12.198.533 14.237 0 16.37 0h53.24v15.984c0 1.62.278 3.367.834 5.242a16.704 16.704 0 0 0 2.572 5.179c1.159 1.577 2.665 2.898 4.518 3.964 1.853 1.066 4.078 1.598 6.673 1.598h20.295v42.325L85.458 92.45c1.02-1.364 1.529-2.856 1.529-4.476 0-2.216-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1c-2.409 0-4.448.789-6.116 2.366-1.668 1.577-2.502 3.474-2.502 5.69 0 2.217.834 4.092 2.502 5.626 1.668 1.535 3.707 2.302 6.117 2.302h52.13zM26.1 47.951c-2.41 0-4.449.789-6.117 2.366-1.668 1.577-2.502 3.473-2.502 5.69 0 2.216.834 4.092 2.502 5.626 1.668 1.534 3.707 2.302 6.117 2.302h52.13c2.409 0 4.47-.768 6.185-2.302 1.715-1.534 2.572-3.41 2.572-5.626 0-2.217-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1zm52.407 64.063l1.807-1.663 3.476-3.196a479.75 479.75 0 0 0 4.587-4.284 500.757 500.757 0 0 1 5.004-4.667c3.985-3.666 8.48-7.758 13.485-12.276l11.677 10.741-13.485 12.404-5.004 4.603-4.587 4.22a179.46 179.46 0 0 0-3.267 3.068c-.88.853-1.367 1.322-1.46 1.407-.463.341-.973.703-1.529 1.087-.556.383-1.112.703-1.668.959-.556.256-1.413.575-2.572.959a83.5 83.5 0 0 1-3.545 1.087 72.2 72.2 0 0 1-3.475.895c-1.112.256-1.946.426-2.502.511-1.112.17-1.854.043-2.224-.383-.371-.426-.464-1.151-.278-2.174.092-.511.278-1.279.556-2.302.278-1.023.602-2.067.973-3.132l1.042-3.005c.325-.938.58-1.577.765-1.918a10.157 10.157 0 0 1 2.224-2.941z"/></svg>
src/icons/svg/fullscreen.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>
src/icons/svg/gitee.svg
New file
@@ -0,0 +1,4 @@
<svg width="90" height="90"
    xmlns="http://www.w3.org/2000/svg">
     <path d="M86,37 L40,37 L40,37.0010476 C37.7912701,37.0010476 36.0005785,38.7912703 36.0000001,41.0000001 L35.9963338,51.0000001 L35.9952531,50.9999999 C35.9946746,53.2091388 37.7850666,55.0004687 39.9942055,55.0010472 C39.9945658,55.0010473 39.994926,55.0010473 39.9952863,55.0010473 L68,54.9997333 L68,54.9997 C70.209139,54.9996816 72.0000149,56.7905277 72.0000333,58.9996667 C72.0000333,58.9996778 72.0000333,58.9996889 72.0000333,58.9997 L72,59.9997 L72,61 L72,61 C72,67.627417 66.627417,73 60,73 L21.9970229,73 L21.9970229,72.9998897 C19.788201,72.9998897 17.99758,71.2093181 17.9975191,69.0004962 L17.9963613,31 L17.9960306,31 C17.9958479,24.372583 23.3682828,18.9998519 29.9956998,18.9996692 C29.99581,18.9996692 29.9959203,18.9996692 29.9960306,18.9996692 L85.988836,19 L85.988836,18.9975183 C88.1970062,18.9975183 89.9874656,17.208169 89.9888352,14.9999992 L89.9975191,4.99999923 L89.9999992,5.00000077 C90.0013694,2.79086219 88.2116195,0.998890802 86.0024809,0.997520639 C86.0016539,0.997520127 86.000827,0.99751987 86,0.99751987 L30,1 L30,1.00016539 C13.4315221,1.00016539 9.13442924e-05,14.4315221 4.58559413e-10,31 L-0.000474127419,87 L-0.000496179797,87 C-0.000508359022,89.209139 1.79034277,91.0000099 3.99948177,91.0000221 C3.99948912,91.0000221 3.99949647,91.0000221 3.99950382,91.0000221 L63,91 L63,91 C77.9116882,91 90,78.9116882 90,64 L90,41 L90,41 C90,38.790861 88.209139,37 86,37 Z"  fill="#C71D23"></path>
</svg>
src/icons/svg/guide.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.482 70.131l36.204 16.18 69.932-65.485-61.38 70.594 46.435 18.735c1.119.425 2.397-.17 2.797-1.363v-.085L127.998.047 1.322 65.874c-1.12.597-1.519 1.959-1.04 3.151.32.511.72.937 1.2 1.107zm44.676 57.821L64.22 107.26l-18.062-7.834v28.527z"/></svg>
src/icons/svg/home.svg
New file
@@ -0,0 +1,5 @@
<svg width="1024" height="1024"
    xmlns="http://www.w3.org/2000/svg">
    <path d="M1006.1 412.7l-187.5-141c0-0.2 0.1-0.3 0.1-0.4V121.4c0-2.3-1.9-4.2-4.2-4.2h-92.4c-2.3 0-4.2 1.9-4.2 4.2V196L535.3 58.8c-7.3-5.5-16-8.4-25.1-8.4-9.2 0-17.9 2.9-25.2 8.5L16.7 412.5C7.8 419.2 2.1 429 0.5 440.1c-2.1 14.8 3.8 29.5 16.2 39.3 4.3 3.3 9.2 5.7 14.5 7 13 3.2 25.8 0.6 36-7.1L505 148.6c3.1-2.3 7.4-2.3 10.4 0l441.8 332c7.3 5.5 16 8.4 25.1 8.4 13.7 0 26.3-6.5 34.2-17.7 13.3-18.8 7.9-44.9-10.4-58.6z" ></path>
    <path d="M906.7 499.4l-193.2-140-196.7-142.5c-3.4-2.5-8.1-2.5-11.5 0L308.7 359.4l-193.2 140c-5.6 4.1-9 10.6-9 17.6v392.1c0 35.5 29 64.5 64.5 64.5h246.7V716.2c0-30 24.6-54.6 54.6-54.6h77.5c30 0 54.6 24.6 54.6 54.6v257.4h246.7c35.5 0 64.5-29 64.5-64.5V517c0.1-6.9-3.3-13.5-8.9-17.6z" ></path>
</svg>
src/icons/svg/icon.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.147.062a13 13 0 0 1 4.94.945c1.55.63 2.907 1.526 4.069 2.688a13.148 13.148 0 0 1 2.761 4.069c.678 1.55 1.017 3.245 1.017 5.086v102.3c0 3.681-1.187 6.733-3.56 9.155-2.373 2.422-5.352 3.633-8.937 3.633H12.992c-3.875 0-7-1.26-9.373-3.779-2.373-2.518-3.56-5.667-3.56-9.445V12.704c0-3.39 1.163-6.345 3.488-8.863C5.872 1.32 8.972.062 12.847.062h102.3zM81.434 109.047c1.744 0 3.003-.412 3.778-1.235.775-.824 1.163-1.914 1.163-3.27 0-1.26-.388-2.325-1.163-3.197-.775-.872-2.034-1.307-3.778-1.307H72.57c.097-.194.145-.485.145-.872V27.09h9.01c1.743 0 2.954-.436 3.633-1.308.678-.872 1.017-1.938 1.017-3.197 0-1.26-.34-2.325-1.017-3.197-.679-.872-1.89-1.308-3.633-1.308H46.268c-1.743 0-2.954.436-3.632 1.308-.678.872-1.018 1.938-1.018 3.197 0 1.26.34 2.325 1.018 3.197.678.872 1.889 1.308 3.632 1.308h8.138v72.075c0 .193.024.339.073.436.048.096.072.242.072.436H46.56c-1.744 0-3.003.435-3.778 1.307-.775.872-1.163 1.938-1.163 3.197 0 1.356.388 2.446 1.163 3.27.775.823 2.034 1.235 3.778 1.235h34.875z"/></svg>
src/icons/svg/international.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M83.287 103.01c-1.57-3.84-6.778-10.414-15.447-19.548-2.327-2.444-2.182-4.306-1.338-9.862v-.64c.553-3.81 1.513-6.05 14.313-8.087 6.516-1.018 8.203 1.57 10.589 5.178l.785 1.193a12.625 12.625 0 0 0 6.43 5.207c1.134.524 2.53 1.164 4.421 2.24 4.596 2.53 4.596 5.41 4.596 11.753v.727a26.91 26.91 0 0 1-5.178 17.454 59.055 59.055 0 0 1-19.025 11.026c3.49-6.546.814-14.313 0-16.553l-.146-.087zM64 5.12a58.502 58.502 0 0 1 25.484 5.818 54.313 54.313 0 0 0-12.859 10.327c-.93 1.28-1.716 2.473-2.472 3.579-2.444 3.694-3.637 5.352-5.818 5.614a25.105 25.105 0 0 1-4.219 0c-4.276-.29-10.094-.64-11.956 4.422-1.193 3.23-1.396 11.956 2.444 16.495.66 1.077.778 2.4.32 3.578a7.01 7.01 0 0 1-2.066 3.229 18.938 18.938 0 0 1-2.909-2.91 18.91 18.91 0 0 0-8.32-6.603c-1.25-.349-2.647-.64-3.985-.93-3.782-.786-8.03-1.688-9.019-3.812a14.895 14.895 0 0 1-.727-5.818 21.935 21.935 0 0 0-1.396-9.25 8.873 8.873 0 0 0-5.557-4.946A58.705 58.705 0 0 1 64 5.12zM0 64c0 35.346 28.654 64 64 64 35.346 0 64-28.654 64-64 0-35.346-28.654-64-64-64C28.654 0 0 28.654 0 64z"/></svg>
src/icons/svg/language.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></svg>
src/icons/svg/link.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg>
src/icons/svg/list.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M1.585 12.087c0 6.616 3.974 11.98 8.877 11.98 4.902 0 8.877-5.364 8.877-11.98 0-6.616-3.975-11.98-8.877-11.98-4.903 0-8.877 5.364-8.877 11.98zM125.86.107H35.613c-1.268 0-2.114 1.426-2.114 2.852v18.255c0 1.712 1.057 2.853 2.114 2.853h90.247c1.268 0 2.114-1.426 2.114-2.853V2.96c0-1.711-1.057-2.852-2.114-2.852zM.106 62.86c0 6.615 3.974 11.979 8.876 11.979 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zM124.17 50.88H33.921c-1.268 0-2.114 1.425-2.114 2.851v18.256c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852V53.73c0-1.426-.846-2.852-2.114-2.852zM.106 115.913c0 6.616 3.974 11.98 8.876 11.98 4.903 0 8.877-5.364 8.877-11.98 0-6.616-3.974-11.98-8.877-11.98-4.902 0-8.876 5.364-8.876 11.98zm124.064-11.98H33.921c-1.268 0-2.114 1.426-2.114 2.853v18.255c0 1.711 1.057 2.852 2.114 2.852h90.247c1.268 0 2.114-1.426 2.114-2.852v-18.255c0-1.427-.846-2.853-2.114-2.853z"/></svg>
src/icons/svg/lock.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M119.88 49.674h-7.987V39.52C111.893 17.738 90.45.08 63.996.08 37.543.08 16.1 17.738 16.1 39.52v10.154H8.113c-4.408 0-7.987 2.94-7.987 6.577v65.13c0 3.637 3.57 6.577 7.987 6.577H119.88c4.407 0 7.987-2.94 7.987-6.577v-65.13c-.008-3.636-3.58-6.577-7.987-6.577zm-23.953 0H32.065V39.52c0-14.524 14.301-26.295 31.931-26.295 17.63 0 31.932 11.777 31.932 26.295v10.153z"/></svg>
src/icons/svg/message.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg>
src/icons/svg/money.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg>
src/icons/svg/nested.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg>
src/icons/svg/password.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg>
src/icons/svg/pdf.svg
New file
@@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M869.073 277.307H657.111V65.344l211.962 211.963zm-238.232 26.27V65.344l-476.498-.054v416.957h714.73v-178.67H630.841zm-335.836 360.57c-5.07-3.064-10.944-5.133-17.61-6.201-6.67-1.064-13.603-1.6-20.81-1.6h-48.821v85.641h48.822c7.206 0 14.14-.532 20.81-1.6 6.665-1.065 12.54-3.133 17.609-6.202 5.064-3.063 9.134-7.406 12.208-13.007 3.065-5.602 4.6-12.937 4.6-22.011 0-9.07-1.535-16.408-4.6-22.01-3.074-5.603-7.144-9.94-12.208-13.01zM35.82 541.805v416.904h952.358V541.805H35.821zm331.421 191.179c-3.6 11.071-9.343 20.879-17.209 29.413-7.874 8.542-18.078 15.408-30.617 20.61-12.544 5.206-27.747 7.807-45.621 7.807h-66.036v102.45h-62.831V607.517h128.867c17.874 0 33.077 2.6 45.62 7.802 12.541 5.207 22.745 12.076 30.618 20.615 7.866 8.538 13.604 18.277 17.21 29.212 3.6 10.943 5.401 22.278 5.401 34.018 0 11.477-1.8 22.752-5.402 33.819zM644.9 806.417c-5.343 17.61-13.408 32.818-24.212 45.627-10.807 12.803-24.283 22.879-40.423 30.213-16.146 7.343-35.155 11.007-57.03 11.007h-123.26V607.518h123.26c18.41 0 35.552 2.941 51.428 8.808 15.873 5.869 29.618 14.671 41.22 26.412 11.608 11.744 20.674 26.411 27.217 44.02 6.535 17.61 9.803 38.288 9.803 62.035 0 20.81-2.67 40.02-8.003 57.624zm245.362-146.07h-138.07v66.03h119.66v48.829h-119.66v118.058h-62.83V607.518h200.9v52.829h-.001zm-318.2 25.611c-6.402-8.266-14.877-14.604-25.412-19.01-10.544-4.402-23.551-6.602-39.019-6.602h-44.825v180.088h56.029c9.07 0 17.872-1.463 26.415-4.401 8.535-2.932 16.14-7.802 22.812-14.609 6.665-6.8 12.007-15.667 16.007-26.61 4.003-10.94 6.003-24.275 6.003-40.021 0-14.408-1.4-27.416-4.202-39.019-2.8-11.607-7.406-21.542-13.808-29.816zm0 0"/></svg>
src/icons/svg/people.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M104.185 95.254c8.161 7.574 13.145 17.441 13.145 28.28 0 1.508-.098 2.998-.285 4.466h-10.784c.238-1.465.403-2.948.403-4.465 0-8.983-4.36-17.115-11.419-23.216C86 104.66 75.355 107.162 64 107.162c-11.344 0-21.98-2.495-31.22-6.83-7.064 6.099-11.444 14.218-11.444 23.203 0 1.517.165 3 .403 4.465H10.955a35.444 35.444 0 0 1-.285-4.465c0-10.838 4.974-20.713 13.127-28.291C9.294 85.42.003 70.417.003 53.58.003 23.99 28.656.001 64 .001s63.997 23.988 63.997 53.58c0 16.842-9.299 31.85-23.812 41.673zM64 36.867c-29.454 0-53.33-10.077-53.33 15.342 0 25.418 23.876 46.023 53.33 46.023 29.454 0 53.33-20.605 53.33-46.023 0-25.419-23.876-15.342-53.33-15.342zm24.888 25.644c-3.927 0-7.111-2.665-7.111-5.953 0-3.288 3.184-5.954 7.11-5.954 3.928 0 7.111 2.666 7.111 5.954s-3.183 5.953-7.11 5.953zm-3.556 16.372c0 4.11-9.55 7.442-21.332 7.442-11.781 0-21.332-3.332-21.332-7.442 0-1.06.656-2.064 1.8-2.976 3.295 2.626 10.79 4.465 19.532 4.465 8.743 0 16.237-1.84 19.531-4.465 1.145.912 1.801 1.916 1.801 2.976zm-46.22-16.372c-3.927 0-7.11-2.665-7.11-5.953 0-3.288 3.183-5.954 7.11-5.954 3.927 0 7.111 2.666 7.111 5.954s-3.184 5.953-7.11 5.953z"/></svg>
src/icons/svg/peoples.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg>
src/icons/svg/qq.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M18.448 57.545l-.244-.744-.198-.968-.132-.53v-2.181l.236-.859.24-.908.317-.953.428-1.06.561-1.103.794-1.104v-.773l.077-.724.123-.984.34-1.106.313-1.194.25-.548.289-.511.371-.569.405-.423v-2.73l.234-1.407.236-1.633.42-1.955.577-2.035.43-1.118.426-1.217.468-1.135.559-1.216.57-1.332.655-1.247.737-1.331.929-1.33.43-.762.457-.624.995-1.406 1.025-1.403 1.163-1.444 1.246-1.405 1.352-1.384 1.41-1.423 1.708-1.536 1.083-.934 1.322-1.008 1.34-.89 1.448-.855 1.392-.76 1.57-.63 1.667-.775 1.657-.532 1.653-.552 1.787-.548 1.785-.417 1.876-.347L59.128.68l1.879-.245 1.876-.252 2.002-.106h5.912l1.97.243 1.981.231 2.019.207 1.874.441 1.979.413 1.857.475 2.035.53 1.862.646 1.782.738 1.904.78 1.736.853 1.689.95 1.655 1.044 1.425.971.662.548.693.401 1.323 1.1 1.115 1.064 1.112 1.1 1.083 1.214.894 1.178 1.064 1.217.74 1.306.752 1.162.798 1.352.661 1.175 1.113 2.489.546 1.286.428 1.192.428 1.294.384 1.217.267 1.047.347 1.231.607 2.198.388 1.924.253 1.861.217 1.497.342 2.28.077.362.274.41.737 1.18.473.8.42.832.534.892.472 1.07.307 1.093.334 1.2.252 1.232.115.605.106.746v.648l-.106.643v.8l-.192.774-.35 1.5-.403.76-.299.852v.213l.142.264.4.623 1.746 2.53 1.377 1.9.66 1.267.889 1.389.774 1.52.893 1.627.894 1.828 1.006 2.069.567 1.268.518 1.239.447 1.307.44 1.175.336 1.235.342 1.16.432 2.261.343 2.31.235 2.05v2.891l-.158 1.025-.226 1.768-.308 1.59-.48 1.44-.18.588-.336.707-.28.493-.375.607-.33.383-.42.494-.375.4-.401.34-.48.207-.432.207-.355.114h-.543l-.346-.114-.66-.32-.302-.212-.317-.223-.347-.304-.35-.342-.579-.63-.684-.89-.539-.917-.538-.734-.526-.855-.741-1.517-.833-1.579-.098-.055h-.138l-.338.247-.196.415-.326.516-.567 1.533-.856 2.182-1.096 2.626-.824 1.308-.864 1.366-1.027 1.536-1.09 1.503-.557.68-.676.743-1.555 1.497.136.135.21.214.777.446 3.235 1.524 1.41.779 1.347.756 1.332.953 1.187.982.574.443.432.511.445.593.367.643.198.533.242.64.105.554.115.647-.115.433v.44l-.105.454-.242.415-.092.325-.22.394-.587.784-.543.627-.42.47-.35.348-.893.638-1.01.556-1.077.532-1.155.511-1.287.495-.693.207-.608.167-1.496.342-1.545.325-1.552.323-1.689.27-1.74.072-1.785.21h-5.539l-1.998-.114-1.86-.168-2.005-.27-1.99-.209-2.095-.286-2.03-.495-1.981-.374-1.968-.552-2.019-.707-1.98-.585-1.044-.342-.927-.323-.586-.223-.582-.12h-1.647l-1.904-.131-.962-.096-1.24-.135-.795.705-1.085.665-1.471.701-1.628.875-.99.475-1.033.376-2.281.914-1.24.305-1.3.343-1.803.344-1.13.086-1.193.1-1.246.135-1.45.053h-5.926l-3.346-.053-3.25-.321-1.644-.23-1.589-.23-1.546-.227-1.547-.305-1.442-.456-1.434-.325-1.294-.51-1.223-.474-1.142-.533-.99-.583-.984-.71-.336-.343-.44-.415-.334-.362-.3-.417-.278-.415-.215-.42-.311-.89-.109-.46-.138-.51v-.473l.138-.533v-.53l.109-.53v-1.069l.052-.564.259-.647.215-.646.39-.779.286-.3.236-.348.615-.738.49-.38.464-.266.428-.338.676-.21.543-.324.676-.341.77-.227.775-.231.897-.192.85-.11 1.008-.13 1.093-.081.284-.092h.063l.137-.115v-.13l-.2-.266-.58-.27-1.45-1.231-.975-.761-1.127-.967-1.136-1.082-1.181-1.382-1.36-1.558-.508-.843-.672-.87-.58-1.007-.522-1.1-.704-1.047-.459-1.194-.547-1.192-.546-1.33-.397-1.273-.378-1.575-.112-.057h-.115l-.059-.113h-.14l-.23.113-.114.057-.158.264-.057.321-.119.286-.206.477-.664 1.157-.345.701-.546.612-.58.736-.641.816-.677.724-.795.701-.734.658-.814.524-.89.546-.855.325-1.008.247-.99.095h-.233l-.228-.095-.18-.384-.29-.188-.38-.912-.237-.493-.255-.707-.21-.734-.113-.724-.313-1.648-.12-.972v-3.185l.12-2.379.196-1.214.23-1.252.21-1.347.374-1.254.42-1.443.431-1.407.578-1.448.545-1.38.754-1.4.699-1.52.855-1.425 1.006-1.538 1.023-1.382 1.069-1.538.891-1.071 1.142-1.227 1.202-1.237.56-.59.678-.662.985-.836 1.012-.853 1.647-1.446 1.242-.889z"/></svg>
src/icons/svg/question.svg
New file
@@ -0,0 +1,5 @@
<svg width="1024" height="1024"
    xmlns="http://www.w3.org/2000/svg">
    <path d="M888.6 991H158.4c-50.3 0-91.3-38.6-91.3-86V227.2c0-47.4 40.9-86 91.3-86h409.9v36.5H158.4c-30.2 0-54.8 22.2-54.8 49.5V905c0 27.3 24.6 49.5 54.8 49.5h730.3c30.2 0 54.8-22.2 54.8-49.5V572H980v333c-0.1 47.4-41 86-91.4 86z" ></path>
    <path d="M452.4 798.7l9.5-38.4c0.2-0.8 5.1-20.4 11.2-41.9 12.3-43.1 17.7-52 20.4-55.6l350.5-557 0.5-0.7c15.3-20 44-23.8 64-8.5l11.6 8.9c19.7 15.1 23.7 43.3 9.1 63.2l-350.5 557-0.5 0.7c-4.2 5.5-13.3 13.4-51.2 34.1-19.9 10.9-38.4 20.1-39.2 20.5l-35.4 17.7zM523.3 684c-3 4.8-9 23.3-15.2 45.2 19.7-10.8 36.4-20.7 41.1-25.1l350-556.2 0.5-0.7c3-3.9 2.2-9.8-1.7-12.8l-11.6-8.9c-3.8-2.9-9.3-2.3-12.4 1.2L523.6 683.5l-0.3 0.5z" ></path>
</svg>
src/icons/svg/search.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg>
src/icons/svg/shopping.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 0 1 3.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 0 1-3.889 2.843 10.582 10.582 0 0 1-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 0 1-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 0 1-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 0 1 3.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 0 1 3.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 0 1-3.89 2.843 11 11 0 0 1-4.732 1.066 10.58 10.58 0 0 1-4.667-1.066 12.478 12.478 0 0 1-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 0 1-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 0 1 3.824-2.772 11.212 11.212 0 0 1 4.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 0 1 .778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 0 1-2.788 8.743 1236.373 1236.373 0 0 0-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 0 1-1.88-4.478 44.128 44.128 0 0 1-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 0 1-2.14-2.558 10.416 10.416 0 0 1-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 0 1 1.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg>
src/icons/svg/size.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>
src/icons/svg/skill.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M31.652 93.206h33.401c1.44 2.418 3.077 4.663 4.93 6.692h-38.33v-6.692zm0-10.586h28.914a44.8 44.8 0 0 1-1.264-6.688h-27.65v6.688zm0-17.27H59.39c.288-2.286.714-4.532 1.34-6.687H31.65v6.687h.003zm53.913 44.84v5.85c0 2.798-2.095 5.075-4.667 5.075h-70.07c-2.576 0-4.663-2.277-4.663-5.075V31.26l23.22-20.96v22.25H17.16v6.688h18.39V6.688h45.348c2.576 0 4.667 2.277 4.667 5.066v20.009c1.987-.675 4.053-1.128 6.17-1.445v-18.56C91.738 5.28 86.874 0 80.902 0H31.15L0 28.118v87.917c0 6.48 4.859 11.759 10.832 11.759h70.07c5.974 0 10.837-5.27 10.837-11.759v-4.41c-2.117-.312-4.183-.765-6.17-1.435h-.004zM23.279 58.667h-7.96v6.688h7.96v-6.688zm-7.956 41.23h7.96v-6.691h-7.96v6.692zm7.956-23.96h-7.96v6.687h7.96v-6.688zm89.718-15.042l-4.896-4.07-12.447 17.613-11.19-9.305-3.762 5.311 16.091 13.38 16.204-22.929zM128 70.978c0-18.632-13.97-33.782-31.147-33.782-17.168 0-31.135 15.155-31.135 33.782 0 18.628 13.97 33.783 31.135 33.783 17.172 0 31.143-15.15 31.143-33.783H128zm-6.17 0c0 14.933-11.203 27.1-24.981 27.1-13.77 0-24.987-12.158-24.987-27.1 0-14.941 11.195-27.099 24.987-27.099 13.778 0 24.982 12.158 24.982 27.1z"/></svg>
src/icons/svg/social-github.svg
New file
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562563492407" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3510" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M511.957333 12.650667C229.248 12.650667 0 241.877333 0 524.672c0 226.197333 146.688 418.090667 350.165333 485.802667 25.6 4.693333 34.944-11.093333 34.944-24.682667 0-12.16-0.426667-44.352-0.682666-87.082667-142.421333 30.933333-172.48-68.629333-172.48-68.629333C188.672 770.944 155.093333 755.2 155.093333 755.2c-46.485333-31.786667 3.52-31.146667 3.52-31.146667 51.392 3.626667 78.421333 52.778667 78.421334 52.778667 45.674667 78.229333 119.829333 55.637333 149.013333 42.538667 4.650667-33.066667 17.877333-55.658667 32.512-68.437334-113.706667-12.928-233.216-56.853333-233.216-253.056 0-55.893333 19.946667-101.589333 52.693333-137.386666-5.269333-12.949333-22.826667-65.002667 5.013334-135.509334 0 0 42.986667-13.76 140.8 52.48 40.832-11.349333 84.629333-17.024 128.170666-17.216 43.477333 0.213333 87.296 5.866667 128.192 17.237334 97.749333-66.261333 140.650667-52.48 140.650667-52.48 27.946667 70.485333 10.368 122.538667 5.098667 135.466666 32.810667 35.818667 52.629333 81.514667 52.629333 137.408 0 196.693333-119.701333 239.978667-233.770667 252.650667 18.389333 15.786667 34.773333 47.061333 34.773334 94.805333 0 68.458667-0.64 123.669333-0.64 140.458667 0 13.696 9.216 29.632 35.2 24.618667C877.44 942.570667 1024 750.784 1024 524.672 1024 241.877333 794.730667 12.650667 511.957333 12.650667z" fill="#191717" p-id="3511"></path></svg>
src/icons/svg/star.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M70.66 4.328l14.01 29.693c1.088 2.29 3.177 3.882 5.603 4.25l31.347 4.76c6.087.926 8.528 8.756 4.117 13.247L103.05 79.395c-1.75 1.78-2.544 4.352-2.132 6.867l5.352 32.641c1.043 6.337-5.33 11.182-10.778 8.19l-28.039-15.409a7.13 7.13 0 0 0-6.91 0l-28.039 15.41c-5.448 2.99-11.821-1.854-10.777-8.19l5.352-32.642c.415-2.515-.387-5.088-2.136-6.867L2.264 56.278C-2.146 51.787.286 43.957 6.38 43.031l31.343-4.76c2.419-.368 4.51-1.96 5.595-4.25L57.334 4.328c2.728-5.77 10.605-5.77 13.325 0z"/></svg>
src/icons/svg/tab.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.921.052H49.08c-1.865 0-3.198 1.599-3.198 3.464v6.661c0 1.865 1.6 3.464 3.198 3.464h29.84c1.865 0 3.198-1.599 3.198-3.464V3.516C82.385 1.65 80.786.052 78.92.052zm45.563 0H94.642c-1.865 0-3.464 1.599-3.464 3.464v6.661c0 1.865 1.599 3.464 3.464 3.464h29.842c1.865-.266 3.464-1.599 3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464zm0 22.382H40.02c-1.866 0-3.464-1.599-3.464-3.464V3.516c0-1.865-1.599-3.464-3.464-3.464H3.516C1.65.052.052 1.651.052 3.516V124.75c0 1.598 1.599 3.197 3.464 3.197h120.968c1.865 0 3.464-1.599 3.464-3.464V25.898c0-1.865-1.599-3.464-3.464-3.464z"/></svg>
src/icons/svg/table.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg>
src/icons/svg/theme.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M125.5 36.984L95.336 2.83C93.735 1.018 91.565 0 89.3 0c-2.263 0-4.433 1.018-6.033 2.83l-3.786 4.286c-1.6 1.812-3.77 2.83-6.032 2.831H54.553c-2.263 0-4.434-1.018-6.033-2.83L44.734 2.83C43.134 1.018 40.964 0 38.701 0c-2.263 0-4.434 1.018-6.034 2.83L2.5 36.984C.9 38.796 0 41.254 0 43.815c0 2.562.899 5.02 2.5 6.831L14.565 64.31c2.178 2.468 5.367 3.403 8.33 2.444 1.35-.435 2.709.592 2.709 2.18v49.407c0 5.313 3.84 9.66 8.532 9.66h59.726c4.693 0 8.532-4.347 8.532-9.66V68.934c0-1.59 1.36-2.616 2.71-2.181 2.962.96 6.15.024 8.329-2.444L125.5 50.646c1.6-1.811 2.499-4.269 2.499-6.83 0-2.563-.899-5.02-2.5-6.832z"/></svg>
src/icons/svg/tree-table.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M44.8 0h79.543C126.78 0 128 1.422 128 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H44.8c-2.438 0-3.657-1.422-3.657-4.267V4.267C41.143 1.422 42.362 0 44.8 0zm22.857 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 80 64 78.578 64 75.733V52.267C64 49.422 65.219 48 67.657 48zm0 48h56.686c2.438 0 3.657 1.422 3.657 4.267v23.466c0 2.845-1.219 4.267-3.657 4.267H67.657C65.22 128 64 126.578 64 123.733v-23.466C64 97.422 65.219 96 67.657 96zM50.286 68.267c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V32h6.4c2.02 0 3.658-1.91 3.658-4.267V4.267C27.429 1.91 25.79 0 23.77 0H3.657C1.637 0 0 1.91 0 4.267v23.466C0 30.09 1.637 32 3.657 32h6.4v80c0 2.356 1.638 4.267 3.657 4.267h36.572c2.02 0 3.657-1.91 3.657-4.267 0-2.356-1.638-4.267-3.657-4.267H17.37V68.267h32.915z"/></svg>
src/icons/svg/tree.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg>
src/icons/svg/user.svg
New file
@@ -0,0 +1 @@
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>
src/icons/svg/users.svg
New file
@@ -0,0 +1,6 @@
<svg width="1024" height="1024"
    xmlns="http://www.w3.org/2000/svg">
    <path d="M512 240.941176m-210.823529 0a210.823529 210.823529 0 1 0 421.647058 0 210.823529 210.823529 0 1 0-421.647058 0Z" ></path>
    <path d="M512 240.941176m-210.823529 0a210.823529 210.823529 0 1 0 421.647058 0 210.823529 210.823529 0 1 0-421.647058 0Z" ></path>
    <path d="M663.190588 427.068235a236.122353 236.122353 0 0 1-301.17647 0L90.352941 632.470588v361.411765h843.294118v-361.411765z" ></path>
</svg>
src/icons/svg/wechat.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg>
src/icons/svg/zip.svg
New file
@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.527 116.793c.178.008.348.024.527.024h40.233c4.711-.005 8.53-3.677 8.534-8.21V18.895c-.004-4.532-3.823-8.204-8.534-8.209H79.054c-.179 0-.353.016-.527.024V0L0 10.082v107.406l78.527 10.342v-11.037zm0-101.362c.174-.024.348-.052.527-.052h40.233c2.018 0 3.659 1.578 3.659 3.52v89.713c-.003 1.942-1.64 3.517-3.659 3.519H79.054c-.179 0-.353-.028-.527-.052V15.431zM30.262 75.757l-18.721-.46V72.37l11.3-16.673v-.148l-10.266.164v-4.51l17.504-.44v3.264L18.696 70.76v.144l11.566.176v4.678zm9.419.231l-5.823-.144V50.671l5.823-.144v25.461zm22.255-11.632c-2.168 1.922-5.353 2.76-9.02 2.736-.702.004-1.402-.04-2.097-.131v9.303l-5.997-.148V50.743c1.852-.352 4.473-.647 8.218-.743 3.838-.096 6.608.539 8.48 1.913 1.807 1.306 3.032 3.5 3.032 6.112s-.926 4.833-2.612 6.331h-.004zM53.36 54.45c-.856-.01-1.71.083-2.541.275v7.682c.523.116 1.167.152 2.06.152 3.301-.004 5.36-1.614 5.36-4.314 0-2.425-1.772-3.843-4.875-3.791l-.004-.004zm39.847-37.066h9.564v3.795h-9.564v-3.795zm-9.568 5.68h9.564v3.8h-9.564v-3.8zm9.568 6.216h9.564v3.799h-9.564V29.28zm0 12h9.564v3.794h-9.564V41.28zm-9.568-6.096h9.564v3.795h-9.564v-3.795zm9.472 47.064c2.512 0 4.921-.96 6.697-2.67 1.776-1.708 2.773-4.026 2.772-6.442l-1.748-15.263c0-5.033-2.492-9.112-7.725-9.112-5.232 0-7.72 4.079-7.72 9.112l-1.752 15.263c-.001 2.417.996 4.735 2.773 6.444 1.777 1.71 4.187 2.669 6.7 2.668h.003zm-3.135-16.75h6.27v12.743h-6.27V65.5z"/></svg>
src/icons/svgo.yml
New file
@@ -0,0 +1,22 @@
# replace default config
# multipass: true
# full: true
plugins:
  # - name
  #
  # or:
  # - name: false
  # - name: true
  #
  # or:
  # - name:
  #     param1: 1
  #     param2: 2
- removeAttrs:
    attrs:
      - 'fill'
      - 'fill-rule'
src/lang/en.js
New file
@@ -0,0 +1,54 @@
export default {
  start: 'Continue testing',
  submission: 'submit',
  lastQuestion: 'Last',
  nextQuestion: 'Next',
  recommendedTime: 'Recommended Time:',
  minute: ' minute',
  subject: 'subject',
  remainingTime: 'Remaining Time:',
  guideLanguage: 'Guide language',
  theFirst: 'Question ',
  section: '',
  demography: 'Demographic Information',
  required: 'required',
  range32: 'Exceed 32 characters',
  noquestionMsg: 'No question',
  questionExceptionMsg: 'submit excetpion',
  requiredMsg: 'please fill in the required fields',
  guide: 'Instruction',
  submitErro: 'submitting questionnaire error',
  unfinish: 'After submitting, I cannot continue to answer questions. Do you want to submit?',
  prompt: 'prompt',
  determine: 'determine',
  cancel: 'cancel',
  unfinishSubmit: 'The data has been successfully uploaded to the server and has been saved. Thank you.',
  finishSubmit: 'You have completed the work behavior questionnaire, and the data has been successfully uploaded to the server and saved. Thank you.',
  selectData: 'Date',
  startDo: 'Start the Test',
  second: ' second ',
  min: ' minute ',
  hour: ' hour ',
  finishMsg: 'You have completed this test, thank you. You can now close the browser.',
  userinfoErro: 'incorrect user information',
  errorDraghy: 'No demographic information, try switching languages',
  pleaseSelect: 'Select',
  langWelcomeChinese: '欢迎访问TAI的测试网站',
  langOptionChinese: '请先在下拉框处选择语言,然后点击“提交”开始测试。',
  uploadInfo: 'Uploading, saving data, please wait, do not close the browser...',
  examinee: 'Tester:',
  pleaseAnswerCarefully: '*Please answer carefully',
  submitTimeout: ':Submit background timeout',
  submitExceptionInfo: 'The server network is abnormal, please close the browser and click the link to answer the question',
  langWelcomeEnglish: 'Welcome to TAI’s testing website.',
  langOptionEnglish: 'Please select a language, then click “submit.',
  statusInfo2: 'The question is suspended.',
  statusInfo3: 'The answer is over',
  statusInfo4: 'The title has expired',
  timeOutSubmit: 'The answer time has been exceeded, the data has been successfully uploaded to the server, and has been saved. Thank you.',
  nextPartInfo: 'Please wait, please do not close the browser...',
  partRemailTime: 'Remaining Time:',
  finishIntelligenceInfo: 'You have completed the cognitive ability test questionnaire, the data has been successfully uploaded to the server, and has been saved. Thank you.',
  langWelcomeThai: 'ยินดีตอนรับสู่เว็บไซย์สำหรับทำแบบทดสอบของ',
  langOptionThai: 'กรุณาเลือกภาษา และคลิก “ดำเนินการต่อ.'
}
src/lang/index.js
New file
@@ -0,0 +1,50 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
import elementThLocale from 'element-ui/lib/locale/lang/th'// element-ui lang
import enLocale from './en'
import zhLocale from './zh'
import thLocale from './th'
Vue.use(VueI18n)
const messages = {
  en: {
    ...enLocale,
    ...elementEnLocale
  },
  zh: {
    ...zhLocale,
    ...elementZhLocale
  },
  th: {
    ...thLocale,
    ...elementThLocale
  }
}
export function getLanguage () {
  const chooseLanguage = Cookies.get('language')
  if (chooseLanguage) return chooseLanguage
  // if has not choose language
  const language = (navigator.language || navigator.browserLanguage).toLowerCase()
  const locales = Object.keys(messages)
  for (const locale of locales) {
    if (language.indexOf(locale) > -1) {
      return locale
    }
  }
  return 'en'
}
const i18n = new VueI18n({
  // set locale
  // options: en | zh | es
  locale: getLanguage(),
  // set locale messages
  messages
})
export default i18n
src/lang/th.js
New file
@@ -0,0 +1,54 @@
export default {
  start: 'Continue testing',
  submission: 'submit',
  lastQuestion: 'Last',
  nextQuestion: 'Next',
  recommendedTime: 'Recommended Time:',
  minute: ' minute',
  subject: 'subject',
  remainingTime: 'Remaining Time:',
  guideLanguage: 'Guide language',
  theFirst: 'Question ',
  section: '',
  demography: 'Demographic Information',
  required: 'required',
  range32: 'Exceed 32 characters',
  noquestionMsg: 'No question',
  questionExceptionMsg: 'submit excetpion',
  requiredMsg: 'please fill in the required fields',
  guide: 'Instruction',
  submitErro: 'submitting questionnaire error',
  unfinish: 'After submitting, I cannot continue to answer questions. Do you want to submit?',
  prompt: 'prompt',
  determine: 'determine',
  cancel: 'cancel',
  unfinishSubmit: 'The data has been successfully uploaded to the server and has been saved. Thank you.',
  finishSubmit: 'You have completed the work behavior questionnaire, and the data has been successfully uploaded to the server and saved. Thank you.',
  selectData: 'Date',
  startDo: 'Start the Test',
  second: ' second ',
  min: ' minute ',
  hour: ' hour ',
  finishMsg: 'You have completed this test, thank you. You can now close the browser.',
  userinfoErro: 'incorrect user information',
  errorDraghy: 'No demographic information, try switching languages',
  pleaseSelect: 'Select',
  langWelcomeChinese: '欢迎访问TAI的测试网站',
  langOptionChinese: '请先在下拉框处选择语言,然后点击“提交”开始测试。',
  uploadInfo: 'Uploading, saving data, please wait, do not close the browser...',
  examinee: 'Tester:',
  pleaseAnswerCarefully: '*Please answer carefully',
  submitTimeout: ':Submit background timeout',
  submitExceptionInfo: 'The server network is abnormal, please close the browser and click the link to answer the question',
  langWelcomeEnglish: 'Welcome to TAI’s testing website.',
  langOptionEnglish: 'Please select a language, then click “submit.',
  statusInfo2: 'The question is suspended.',
  statusInfo3: 'The answer is over',
  statusInfo4: 'The title has expired',
  timeOutSubmit: 'The answer time has been exceeded, the data has been successfully uploaded to the server, and has been saved. Thank you.',
  nextPartInfo: 'Please wait, please do not close the browser...',
  partRemailTime: 'Remaining Time:',
  finishIntelligenceInfo: 'You have completed the cognitive ability test questionnaire, the data has been successfully uploaded to the server, and has been saved. Thank you.',
  langWelcomeThai: 'ยินดีตอนรับสู่เว็บไซย์สำหรับทำแบบทดสอบของ',
  langOptionThai: 'กรุณาเลือกภาษา และคลิก “ดำเนินการต่อ.'
}
src/lang/zh.js
New file
@@ -0,0 +1,54 @@
export default {
  start: '继续测试',
  submission: ' 提交',
  lastQuestion: '回到上一题',
  nextQuestion: '下一题',
  recommendedTime: '建议时间:',
  minute: '分钟',
  subject: '题目',
  remainingTime: '剩余时间:',
  guideLanguage: '指导语',
  theFirst: '第',
  section: '题',
  demography: '基本信息',
  required: '必填',
  range32: '不能超过32个字符',
  noquestionMsg: '无法获得题目',
  questionExceptionMsg: '提交题目异常',
  requiredMsg: '请填写必填项',
  guide: '指导语',
  submitErro: '提交问卷后台错误',
  unfinish: '提交之后无法继续答题,是否提交?',
  prompt: '提示',
  determine: '确定',
  cancel: '取消',
  unfinishSubmit: '数据已经成功的上传至服务器,并已经保存好。谢谢。',
  finishSubmit: '您已经完成了工作行为问卷,数据已经成功的上传至服务器,并已经保存好。谢谢。',
  selectData: '选择日期',
  startDo: '开始测试',
  second: '秒',
  min: '分',
  hour: '小时',
  finishMsg: '您已经完成了本次测试,谢谢。您现在可以关闭浏览器。',
  userinfoErro: '获取用户信息填写错误',
  errorDraghy: '无人口学信息,可尝试切换语言',
  pleaseSelect: '请选择',
  langWelcomeChinese: '欢迎访问TAI的测试网站',
  langOptionChinese: '请先在下拉框处选择语言,然后点击“提交”开始测试。',
  uploadInfo: '正在上传,保存数据,请稍等,不要关闭浏览器...',
  examinee: '测试者:',
  pleaseAnswerCarefully: '*请认真作答',
  submitTimeout: ':提交后台超时',
  submitExceptionInfo: '保存服务器网络异常,请关闭浏览重新点击链接答题',
  langWelcomeEnglish: 'Welcome to TAI’s testing website.',
  langOptionEnglish: 'Please select a language, then click “submit.',
  statusInfo2: '题目暂停答题',
  statusInfo3: '答题已结束',
  statusInfo4: '题目已经失效',
  timeOutSubmit: '已超过答题时间,数据已经成功的上传至服务器,并已经保存好。谢谢。',
  nextPartInfo: '请稍等,请不要关闭浏览器...',
  partRemailTime: '剩余时间:',
  finishIntelligenceInfo: '您已经完成了认知能力测试问卷,数据已经成功的上传至服务器,并已经保存好。谢谢。',
  langWelcomeThai: 'ยินดีตอนรับสู่เว็บไซย์สำหรับทำแบบทดสอบของ',
  langOptionThai: 'กรุณาเลือกภาษา และคลิก “ดำเนินการต่อ.'
}
src/layout/index.vue
New file
@@ -0,0 +1,72 @@
<template>
  <el-container>
    <el-header height="60" class="student-header header" style="background-color: #3c8dbc">
      <div class="head-language" v-if="langFlag">
        <!--<lang-select class="right-menu-item" />-->
        <!--国际化的语言选择-->
        <!--<component :is="langSelect"></component>-->
      </div>
      <div class="head-user">
        <el-dropdown trigger="click" placement="bottom">
          <el-badge>
            <el-avatar  class="el-dropdown-avatar" size="medium"  :src="userInfo.imagePath === null ? require('@/assets/logo-t.jpg') : userInfo.imagePath"></el-avatar>
          </el-badge>
          <el-dropdown-menu slot="dropdown">
<!--            <el-dropdown-item divided>保存退出</el-dropdown-item>-->
          </el-dropdown-menu>
        </el-dropdown>
      </div>
    </el-header>
    <el-main class="student-main">
      <router-view/>
    </el-main>
    <el-footer height="340" class="student-footer">
    </el-footer>
  </el-container>
</template>
<script>
import LangSelect from '@/components/LangSelect'
import { mapState } from 'vuex'
export default {
  name: 'Layout',
  data () {
    return {
      defaultUrl: '/index',
      userInfo: {
        imagePath: null
      },
      langSelect: 'langSelect'
    }
  },
  created () {
  },
  methods: {
    logout: function () {
      window.opener = null
      window.open('about:blank', '_top').close()
    }
  },
  components: {
    'langSelect': LangSelect
  },
  computed: {
    ...mapState('user', { langFlag: state => state.langFlag })
  }
}
</script>
<style lang="scss" scoped>
  .header {
    position: fixed;
    width: 100%;
    z-index: 1000;
    top: 0%;
  .head-language {
     position: absolute;
     bottom: 10px;
     right: 80px;
    }
  }
</style>
src/layout/indexold.vue
New file
@@ -0,0 +1,96 @@
<template>
  <el-container>
    <el-header height="61" class="student-header">
      <div class="head-user">
        <el-dropdown trigger="click" placement="bottom">
          <el-badge :is-dot="messageCount!==0" >
            <el-avatar  class="el-dropdown-avatar" size="medium"  :s0rc="userInfo.imagePath === null ? require('@/assets/avatar.png') : userInfo.imagePath"></el-avatar>
          </el-badge>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item @click.native="$router.push({path:'/user/index'})">个人中心</el-dropdown-item>
            <el-dropdown-item @click.native="$router.push({path:'/user/message'})">
              <el-badge :value="messageCount" v-if="messageCount!==0">
                <span>消息中心</span>
              </el-badge>
              <span  v-if="messageCount===0">消息中心</span>
            </el-dropdown-item>
            <el-dropdown-item @click.native="logout" divided>退出</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
      <el-menu class="el-menu-title" mode="horizontal" :default-active="defaultUrl" :router="true">
        <el-menu-item index="/index">首页</el-menu-item>
        <el-menu-item index="/record/index">考试记录</el-menu-item>
        <el-menu-item index="/demography/ed860660-95a7-4feb-aab9-f56b1eff1a3c/11/index">人口学变量</el-menu-item>
        <el-menu-item index="/guide/index">指导语</el-menu-item>
      </el-menu>
      <div>
        <img src="@/assets/logo2.png" height="56"/>
      </div>
    </el-header>
    <el-main class="student-main">
      <router-view/>
    </el-main>
    <el-footer height="340" class="student-footer">
    </el-footer>
  </el-container>
</template>
<script>
import { mapActions, mapMutations, mapState } from 'vuex'
import loginApi from '@/api/login'
import userApi from '@/api/user'
export default {
  name: 'Layout',
  data () {
    return {
      defaultUrl: '/index',
      userInfo: {
        imagePath: null
      }
    }
  },
  created () {
    let _this = this
    this.defaultUrl = this.routeSelect(this.$route.path)
    this.getUserMessageInfo()
    userApi.getCurrentUser().then(re => {
      _this.userInfo = re.response
    })
  },
  watch: {
    $route (to, from) {
      this.defaultUrl = this.routeSelect(to.path)
    }
  },
  methods: {
    routeSelect (path) {
      let topPath = ['/', '/index', '/paper/index', '/record/index', '/question/index', '/demography/index']
      if (topPath.indexOf(path)) {
        return path
      }
      return null
    },
    logout () {
      let _this = this
      loginApi.logout().then(function (result) {
        if (result && result.code === 1) {
          _this.clearLogin()
          _this.$router.push({ path: '/login' })
        }
      })
    },
    ...mapActions('user', ['getUserMessageInfo']),
    ...mapMutations('user', ['clearLogin'])
  },
  computed: {
    ...mapState('user', {
      messageCount: state => state.messageCount
    })
  }
}
</script>
<style lang="scss" scoped>
</style>
src/main.js
New file
@@ -0,0 +1,65 @@
import Vue from 'vue'
import App from './App.vue'
import { router } from './router'
import store from './store'
import 'normalize.css/normalize.css'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import '@/assets/custom-theme/indexExtend.css'
import Cookies from 'js-cookie'
import i18n from './lang'
import '@/styles/index.scss' // global css
import './icons' // icon
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import 'babel-polyfill'
import promise from 'es6-promise'
promise.polyfill()
Vue.use(Element, {
  size: Cookies.get('size') || 'medium', // set element-ui default size
  i18n: (key, value) => i18n.t(key, value)
})
Vue.config.productionTip = false
NProgress.configure({ showSpinner: false }) // NProgress Configuration
router.beforeEach(async (to, from, next) => {
  // start progress bar
  NProgress.start()
  if (to.meta.title !== undefined) {
    document.title = to.meta.title
  } else {
    document.title = '\u200E'
  }
  if (to.meta.bodyBackground !== undefined) {
    document.querySelector('body').setAttribute('style', 'background: ' + to.meta.bodyBackground)
  } else {
    document.querySelector('body').removeAttribute('style')
  }
  if (to.path) {
    // eslint-disable-next-line no-undef
    _hmt.push(['_trackPageview', '/#' + to.fullPath])
  }
  next()
})
router.afterEach((to, from, next) => {
  // finish progress bar
  NProgress.done()
})
Vue.prototype.$$router = router
new Vue({
  router: router,
  store: store,
  i18n,
  render: h => h(App)
}).$mount('#app')
src/router.js
New file
@@ -0,0 +1,125 @@
import Vue from 'vue'
import Router from 'vue-router'
import Layout from '@/layout'
Vue.use(Router)
const router = new Router({
  routes: [
    {
      path: '/ots/:memberToken',
      component: Layout,
      children: [
        {
          path: 'login',
          component: () => import('@/views/demography/login'),
          name: 'login',
          meta: { title: '' }
        }
      ]
    },
    {
      path: '/demography/fill',
      component: Layout,
      children: [
        {
          path: 'index',
          component: () => import('@/views/demography/index'),
          name: 'Demography',
          meta: { title: 'Demography' }
        },
        {
          path: 'langOption',
          component: () => import('@/views/demography/langOption'),
          name: 'langOption',
          meta: { title: 'langOption' }
        }
      ]
    },
    {
      path: '/guide',
      component: Layout,
      children: [
        {
          path: 'index',
          component: () => import('@/views/exam/paper/guide'),
          name: 'QuestionGuide',
          meta: { title: '' }
        }
      ]
    },
    {
      path: '/exam',
      component: Layout,
      children: [
        {
          path: 'do',
          component: () => import('@/views/exam/paper/do'),
          name: 'ExamPaperDo',
          meta: { title: '' }
        }
      ]
    },
    {
      path: '/exam',
      component: Layout,
      children: [
        {
          path: 'job',
          component: () => import('@/views/exam/paper/job'),
          name: 'ExamPaperJob',
          meta: { title: 'job' }
        }
      ]
    },
    {
      path: '/exam',
      component: Layout,
      children: [
        {
          path: 'personality',
          component: () => import('@/views/exam/paper/personality'),
          name: 'ExamPaperPersonality',
          meta: { title: 'personality' }
        }
      ]
    },
    {
      path: '/exam',
      component: Layout,
      children: [
        {
          path: 'intelligence',
          component: () => import('@/views/exam/paper/intelligence'),
          name: 'ExamPaperIntelligence',
          meta: { title: 'intelligence' }
        }
      ]
    },
    {
      path: '/exam',
      component: Layout,
      children: [
        {
          path: 'finish',
          component: () => import('@/views/exam/paper/finish'),
          name: 'ExamPaperFinish',
          meta: { title: '' }
        }
      ]
    },
    {
      path: '/exam',
      component: Layout,
      children: [
        {
          path: 'exception',
          component: () => import('@/views/exam/paper/exception'),
          name: 'ExamPaperException',
          meta: { title: '' }
        }
      ]
    }
  ]
})
export { router }
src/store/index.js
New file
@@ -0,0 +1,23 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// https://webpack.js.org/guides/dependency-management/#requirecontext
const modulesFiles = require.context('./modules', true, /\.js$/)
// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})
const store = new Vuex.Store({
  modules
})
export default store
src/store/modules/enumItem.js
New file
@@ -0,0 +1,61 @@
// initial state
const state = {
  user: {
    sexEnum: [{ key: 1, value: '男' }, { key: 2, value: '女' }],
    levelEnum: [{ key: 1, value: '一年级' }, { key: 2, value: '二年级' }, { key: 3, value: '三年级' }, { key: 4, value: '四年级' }, { key: 5, value: '五年级' }, { key: 6, value: '六年级' },
      { key: 7, value: '初一' }, { key: 8, value: '初二' }, { key: 9, value: '初三' },
      { key: 10, value: '高一' }, { key: 11, value: '高二' }, { key: 12, value: '高三' }],
    roleEnum: [{ key: 1, value: '学生' }, { key: 2, value: '教师' }, { key: 3, value: '管理员' }],
    message: {
      readTag: [{ key: true, value: 'success' }, { key: false, value: 'warning' }],
      readText: [{ key: true, value: '已读' }, { key: false, value: '未读' }]
    }
  },
  exam: {
    examPaper: {
      paperTypeEnum: [{ key: 1, value: '固定试卷' }, { key: 4, value: '时段试卷' }]
    },
    examPaperAnswer: {
      statusEnum: [{ key: 1, value: '待批改' }, { key: 2, value: '完成' }],
      statusTag: [{ key: 1, value: 'warning' }, { key: 2, value: 'success' }]
    },
    question: {
      typeEnum: [{ key: 1, value: '单选题' }, { key: 2, value: '多选题' }, { key: 3, value: '判断题' }, { key: 4, value: '填空题' }, { key: 5, value: '简答题' }],
      answer: {
        doRightTag: [{ key: true, value: 'success' }, { key: false, value: 'danger' }, { key: null, value: 'warning' }],
        doRightEnum: [{ key: true, value: '正确' }, { key: false, value: '错误' }, { key: null, value: '待批改' }],
        doCompletedTag: [{ key: false, value: 'info' }, { key: true, value: 'success' }]
      }
    }
  }
}
// getters
const getters = {
  enumFormat: (state) => (arrary, key) => {
    return format(arrary, key)
  }
}
// actions
const actions = {}
// mutations
const mutations = {}
const format = function (array, key) {
  for (let item of array) {
    if (item.key === key) {
      return item.value
    }
  }
  return null
}
export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}
src/store/modules/exam.js
New file
@@ -0,0 +1,48 @@
import subjectApi from '@/api/subject'
const state = {
  subjects: [],
  doUrl: 'job',
  reportTemplateId: ''
}
const getters = {
  subjectEnumFormat: (state) => (key) => {
    for (let item of state.subjects) {
      if (item.id === key) {
        return item.name + ' ( ' + item.levelName + ' )'
      }
    }
    return null
  }
}
// actions
const actions = {
  initSubject ({ commit }) {
    subjectApi.list().then(re => {
      commit('setSubjects', re.response)
    })
  }
}
// mutations
const mutations = {
  setSubjects: (state, subjects) => {
    state.subjects = subjects
  },
  setDoUrl: (state, url) => {
    state.doUrl = url
  },
  setReportTemplateId: (state, reportTemplateId) => {
    state.reportTemplateId = reportTemplateId
  }
}
export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}
src/store/modules/user.js
New file
@@ -0,0 +1,84 @@
import Cookies from 'js-cookie'
import userApi from '@/api/user'
// initial state
const state = {
  userName: Cookies.get('studentUserName'),
  userInfo: Cookies.get('studentUserInfo'),
  imagePath: Cookies.get('studentImagePath'),
  messageCount: 0,
  memberToken: Cookies.get('memberTocken'),
  langType: 'English',
  langFlag: true,
  memberId: null
}
// actions
const actions = {
  initUserInfo ({ commit }) {
    userApi.getCurrentUser().then(re => {
      commit('setUserInfo', re.response)
    })
  },
  getUserMessageInfo ({ commit }) {
    userApi.getMessageCount().then(re => {
      commit('setMessageCount', re.response)
    })
  }
}
// mutations
const mutations = {
  setUserName (state, userName) {
    state.userName = userName
    Cookies.set('studentUserName', userName, { expires: 30 })
  },
  setMemberTocken (state, paraMemberToken) {
    state.memberToken = paraMemberToken
    Cookies.set('memberTocken', paraMemberToken, { expires: 30 })
  },
  setMemberId (state, memberId) {
    state.memberId = memberId
  },
  setLangType (state, paraLangType) {
    state.langType = paraLangType
  },
  getLangType () {
    let language = (navigator.language || navigator.browserLanguage).toLowerCase()
    if (language === 'zh-cn') {
      state.langType = 'Chinese'
      console.log(language)
    } else {
      state.langType = 'English'
      console.log(language)
    }
  },
  setLangFlag (state, paraLangFlag) {
    state.langFlag = paraLangFlag
  },
  setUserInfo: (state, userInfo) => {
    state.userInfo = userInfo
    Cookies.set('studentUserInfo', userInfo, { expires: 30 })
  },
  setImagePath: (state, imagePath) => {
    state.imagePath = imagePath
    Cookies.set('studentImagePath', imagePath, { expires: 30 })
  },
  setMessageCount: (state, messageCount) => {
    state.messageCount = messageCount
  },
  messageCountSubtract: (state, num) => {
    state.messageCount = state.messageCount - num
  },
  clearLogin (state) {
    Cookies.remove('studentUserName')
    Cookies.remove('studentUserInfo')
    Cookies.remove('studentImagePath')
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}
src/styles/index.scss
New file
@@ -0,0 +1,328 @@
body {
  height: 100%;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
  background-color: #fff;
}
html {
  height: 100%;
  box-sizing: border-box;
}
#app {
  height: 100%;
}
*,
*:before,
*:after {
  box-sizing: inherit;
}
.student-header {
  border-bottom: solid 1px #e6e6e6 !important;
  padding: 0px !important;
  .el-menu-title {
    border-bottom: 0px !important;
    float: right;
    .el-menu-item {
      font-size: 15px !important;
      line-height: 62px !important;
    }
  }
  .head-user {
    color: #909399;
    float: right;
    margin: 0px 0px 0px 10px;
    .el-dropdown-avatar {
      margin-top: 12px;
      margin-right: 20px;
      cursor: pointer;
    }
    .is-dot {
      right: 10px;
      top: 5px;
    }
  }
}
.student-main {
  padding: 0px !important;
}
@media only screen and (max-width: 768px) {
  .student-footer {
    padding: 40px 20px;
  }
}
@media only screen and (min-width: 768px) and (max-width: 1200px) {
  .student-footer {
    padding: 40px 70px;
  }
}
@media only screen and (min-width: 1200px) {
  .student-footer {
    padding: 40px 150px;
  }
}
.student-footer {
  margin-top: 150px;
  background-color: #f7fbfd;
  width: 100%;
  box-sizing: border-box;
  .footer-main {
    display: inline-block;
    vertical-align: top;
    margin-right: 110px;
    .footer-main-link {
      display: block;
      margin: 0;
      line-height: 2;
      font-size: 14px;
      color: #666;
    }
  }
  .footer-social {
    float: right;
    text-align: right;
    .footer-social-title {
      color: #666;
      font-size: 18px;
      line-height: 1;
      margin: 0 0 20px;
      padding: 0;
      font-weight: 700;
    }
    a {
      margin-left: 5px;
    }
  }
}
@media only screen and (max-width: 768px) {
  .app-item-contain {
    padding: 30px 0px 0px 0px;
  }
  .app-contain {
    padding: 30px 0px 0px 0px;
  }
}
@media only screen and (min-width: 768px) and (max-width: 1200px) {
  .app-item-contain {
    padding: 30px 30px 0px 30px;
  }
  .app-contain {
    padding: 30px 30px 0px 30px;
  }
}
@media only screen and (min-width: 1200px) {
  .app-item-contain {
    padding: 30px 100px 0px 100px;
  }
  .app-contain {
    padding: 30px 100px 0px 100px;
  }
}
.q-title {
  margin: 5px 5px 0px 5px;
  p {
    display: inline !important;
  }
}
.q-content {
  margin: 10px 35px 15px 25px;
  p {
    display: inline !important;
  }
  .el-checkbox__input {
    line-height: 1.8;
    vertical-align: top;
  }
}
.q-item-prefix {
  margin: 5px 10px;
}
.q-item-content {
  margin: 5px;
  p {
    display: inline !important;
  }
}
.q-item-span-content {
  white-space: normal;
  line-height: 2.1;
  p {
    display: inline !important;
  }
}
.question-prefix {
  padding-right: 22px;
  font-weight: 600 !important;
}
.gapfilling-span {
  color: red;
  padding: 0px 30px;
  margin: 0px 5px;
  border-bottom: 3px double red;
}
.question-item-margin {
  margin-top: 10px;
  margin-bottom: 10px;
}
.exam-question-item {
  .el-form-item__label {
    font-size: 15px !important;
    font-weight: 600 !important;
    color: #1f2f3d !important;
  }
}
.record-answer-info {
  margin-top: 40px;
  min-height: 200px;
  .el-form-item {
    margin-bottom: 0px !important;
  }
}
.question-answer-show-item {
  .question-show-item {
    float: left;
  }
  .el-rate {
    line-height: 1.5;
  }
}
.question-show-item-analyze {
}
.user-info-userName {
  text-align: center;
  margin-top: 15px
}
.user-info-fullInfo {
  label {
    line-height: 1.5;
  }
}
.student-message-list {
  .el-collapse-item__arrow {
    margin: 0px;
  }
}
.index-task-table {
  width: 100%;
}
.index-task-table-paper {
  padding-left: 20px;
}
.index-limit-paper-time {
  font-size: 12px;
  line-height: 1.3;
  color: gray;
  text-align: right;
}
.do-exam-title {
  position: fixed;
  width: 100%;
  background: #fff6f6;
  z-index: 999;
  padding: 5px 0px;
}
.do-exam-title-hidden {
  width: 100%;
  visibility: hidden;
  padding: 5px 0px;
}
.do-exam-title-tag {
  margin-left: 5px;
  cursor: pointer;
}
.do-exam-time {
  float: right;
  line-height: 2;
  font-size: 14px;
  padding-right: 5px;
}
.do-align-center {
  text-align: center;
  margin-top: 40px;
  .el-form-item__content {
    margin-left: 0px !important;
  }
}
.ueditor-p {
  display: inline !important;
}
.carousel-img {
  display: inline-block;
  height: 300px;
  width: 100%;
}
img {
  max-width: 100%;
  max-height: 100%;
}
src/utils/index.js
New file
@@ -0,0 +1,419 @@
/**
 * Created by PanJiaChen on 16/11/18.
 */
/**
 * Parse the time to string
 * @param {(Object|string|number)} time
 * @param {string} cFormat
 * @returns {string}
 */
export function parseTime (time, cFormat) {
  if (arguments.length === 0) {
    return null
  }
  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
      time = parseInt(time)
    }
    if ((typeof time === 'number') && (time.toString().length === 10)) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  // eslint-disable-next-line camelcase
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    // eslint-disable-next-line standard/computed-property-even-spacing
    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  // eslint-disable-next-line camelcase
  return time_str
}
/**
 * @param {number} time
 * @param {string} option
 * @returns {string}
 */
export function formatTime (time, option) {
  if (('' + time).length === 10) {
    time = parseInt(time) * 1000
  } else {
    time = +time
  }
  const d = new Date(time)
  const now = Date.now()
  const diff = (now - d) / 1000
  if (diff < 30) {
    return '刚刚'
  } else if (diff < 3600) {
    // less 1 hour
    return Math.ceil(diff / 60) + '分钟前'
  } else if (diff < 3600 * 24) {
    return Math.ceil(diff / 3600) + '小时前'
  } else if (diff < 3600 * 24 * 2) {
    return '1天前'
  }
  if (option) {
    return parseTime(time, option)
  } else {
    return (
      d.getMonth() +
      1 +
      '月' +
      d.getDate() +
      '日' +
      d.getHours() +
      '时' +
      d.getMinutes() +
      '分'
    )
  }
}
/**
 * @param {string} url
 * @returns {Object}
 */
export function getQueryObject (url) {
  url = url == null ? window.location.href : url
  const search = url.substring(url.lastIndexOf('?') + 1)
  const obj = {}
  const reg = /([^?&=]+)=([^?&=]*)/g
  search.replace(reg, (rs, $1, $2) => {
    const name = decodeURIComponent($1)
    let val = decodeURIComponent($2)
    val = String(val)
    obj[name] = val
    return rs
  })
  return obj
}
/**
 * @param {string} input value
 * @returns {number} output value
 */
export function byteLength (str) {
  // returns the byte length of an utf8 string
  let s = str.length
  for (var i = str.length - 1; i >= 0; i--) {
    const code = str.charCodeAt(i)
    if (code > 0x7f && code <= 0x7ff) s++
    else if (code > 0x7ff && code <= 0xffff) s += 2
    if (code >= 0xDC00 && code <= 0xDFFF) i--
  }
  return s
}
/**
 * @param {Array} actual
 * @returns {Array}
 */
export function cleanArray (actual) {
  const newArray = []
  for (let i = 0; i < actual.length; i++) {
    if (actual[i]) {
      newArray.push(actual[i])
    }
  }
  return newArray
}
/**
 * @param {Object} json
 * @returns {Array}
 */
export function param (json) {
  if (!json) return ''
  return cleanArray(
    Object.keys(json).map(key => {
      if (json[key] === undefined) return ''
      return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
    })
  ).join('&')
}
/**
 * @param {string} url
 * @returns {Object}
 */
export function param2Obj (url) {
  const search = url.split('?')[1]
  if (!search) {
    return {}
  }
  return JSON.parse(
    '{"' +
    decodeURIComponent(search)
      .replace(/"/g, '\\"')
      .replace(/&/g, '","')
      .replace(/=/g, '":"')
      .replace(/\+/g, ' ') +
    '"}'
  )
}
/**
 * @param {string} val
 * @returns {string}
 */
export function html2Text (val) {
  const div = document.createElement('div')
  div.innerHTML = val
  return div.textContent || div.innerText
}
/**
 * Merges two objects, giving the last one precedence
 * @param {Object} target
 * @param {(Object|Array)} source
 * @returns {Object}
 */
export function objectMerge (target, source) {
  if (typeof target !== 'object') {
    target = {}
  }
  if (Array.isArray(source)) {
    return source.slice()
  }
  Object.keys(source).forEach(property => {
    const sourceProperty = source[property]
    if (typeof sourceProperty === 'object') {
      target[property] = objectMerge(target[property], sourceProperty)
    } else {
      target[property] = sourceProperty
    }
  })
  return target
}
/**
 * @param {HTMLElement} element
 * @param {string} className
 */
export function toggleClass (element, className) {
  if (!element || !className) {
    return
  }
  let classString = element.className
  const nameIndex = classString.indexOf(className)
  if (nameIndex === -1) {
    classString += '' + className
  } else {
    classString =
      classString.substr(0, nameIndex) +
      classString.substr(nameIndex + className.length)
  }
  element.className = classString
}
/**
 * @param {string} type
 * @returns {Date}
 */
export function getTime (type) {
  if (type === 'start') {
    return new Date().getTime() - 3600 * 1000 * 24 * 90
  } else {
    return new Date(new Date().toDateString())
  }
}
/**
 * @param {Function} func
 * @param {number} wait
 * @param {boolean} immediate
 * @return {*}
 */
export function debounce (func, wait, immediate) {
  let timeout, args, context, timestamp, result
  const later = function () {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp
    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }
  return function (...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在,重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }
    return result
  }
}
/**
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 * @param {Object} source
 * @returns {Object}
 */
export function deepClone (source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}
/**
 * @param {Array} arr
 * @returns {Array}
 */
export function uniqueArr (arr) {
  return Array.from(new Set(arr))
}
/**
 * @returns {string}
 */
export function createUniqueString () {
  const timestamp = +new Date() + ''
  const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  return (+(randomNum + timestamp)).toString(32)
}
/**
 * Check if an element has a class
 * @param {HTMLElement} elm
 * @param {string} cls
 * @returns {boolean}
 */
export function hasClass (ele, cls) {
  return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
 * Add class to element
 * @param {HTMLElement} elm
 * @param {string} cls
 */
export function addClass (ele, cls) {
  if (!hasClass(ele, cls)) ele.className += ' ' + cls
}
/**
 * Remove class from element
 * @param {HTMLElement} elm
 * @param {string} cls
 */
export function removeClass (ele, cls) {
  if (hasClass(ele, cls)) {
    const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
    ele.className = ele.className.replace(reg, ' ')
  }
}
export function formatSeconds (theTime) {
  let theTime1 = 0
  let theTime2 = 0
  if (theTime > 60) {
    theTime1 = parseInt(theTime / 60)
    theTime = parseInt(theTime % 60)
    if (theTime1 > 60) {
      theTime2 = parseInt(theTime1 / 60)
      theTime1 = parseInt(theTime1 % 60)
    }
  }
  let result = '' + parseInt(theTime) + '秒'
  if (theTime1 > 0) {
    result = '' + parseInt(theTime1) + '分' + result
  }
  if (theTime2 > 0) {
    result = '' + parseInt(theTime2) + '小时' + result
  }
  return result
}
export function formatSecondsG (theTime, hour, min, second) {
  let theTime1 = 0
  let theTime2 = 0
  if (theTime > 60) {
    theTime1 = parseInt(theTime / 60)
    theTime = parseInt(theTime % 60)
    if (theTime1 > 60) {
      theTime2 = parseInt(theTime1 / 60)
      theTime1 = parseInt(theTime1 % 60)
    }
  }
  let result = '' + parseInt(theTime) + second
  if (theTime1 > 0) {
    result = '' + parseInt(theTime1) + min + result
  }
  if (theTime2 > 0) {
    result = '' + parseInt(theTime2) + hour + result
  }
  return result
}
export function formatDate (date, fmt) {
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
  }
  let o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  }
  for (let k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      let str = o[k] + ''
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str))
    }
  }
  return fmt
}
function padLeftZero (str) {
  return ('00' + str).substr(str.length)
}
src/utils/request.js
New file
@@ -0,0 +1,160 @@
import axios from 'axios'
import vue from 'vue'
const request = function (loadtip, query) {
  console.log(query.baseURL)
  console.log(query.url)
  let loading
  if (loadtip) {
    loading = vue.prototype.$loading({
      lock: false,
      text: '正在加载中…',
      spinner: 'el-icon-loading',
      background: 'rgba(0, 0, 0, 0.5)'
    })
  }
  return axios.request(query)
    .then(res => {
      if (loadtip) {
        loading.close()
      }
      if (res.data.code === 401) {
        vue.prototype.$$router.push({ path: '/login' })
        return Promise.reject(res.data)
      } else if (res.data.code === 500) {
        return Promise.reject(res.data)
      } else if (res.data.code === 501) {
        return Promise.reject(res.data)
      } else if (res.data.code === 502) {
        vue.prototype.$$router.push({ path: '/login' })
        return Promise.reject(res.data)
      } else if (res.data.code === 999 || res.data.code === 998) {
        return Promise.reject(res.data)
      } else {
        return Promise.resolve(res.data)
      }
    })
    .catch(e => {
      if (loadtip) {
        loading.close()
      }
      // vue.prototype.$message.error(e.message)
      return Promise.reject(e)
    })
}
const post = function (url, params) {
  const query = {
    baseURL: process.env.VUE_APP_URL,
    url: url,
    method: 'post',
    withCredentials: true,
    timeout: 600000,
    data: params,
    headers: { 'Content-Type': 'application/json', 'request-ajax': true }
  }
  return request(false, query)
}
const postOts = function (url, params) {
  const query = {
    url: url,
    method: 'post',
    withCredentials: true,
    timeout: 600000,
    // timeout: 5000,
    data: params,
    // headers: { 'Content-Type': 'application/json;charset=UTF-8', 'request-ajax': true, 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept' }
    headers: { 'Content-Type': 'application/json', 'request-ajax': true }
  }
  return request(false, query)
}
const getOts = function (url, params) {
  const query = {
    url: url,
    method: 'get',
    withCredentials: true,
    timeout: 600000,
    data: params,
    // headers: { 'Content-Type': 'application/json;charset=UTF-8', 'request-ajax': true, 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept' }
    headers: { 'Content-Type': 'application/json', 'request-ajax': true }
  }
  return request(false, query)
}
const postWithLoadTip = function (url, params) {
  const query = {
    baseURL: process.env.VUE_APP_URL,
    url: url,
    method: 'post',
    withCredentials: true,
    timeout: 600000,
    data: params,
    headers: { 'Content-Type': 'application/json', 'request-ajax': true }
  }
  return request(true, query)
}
const postWithLoadTipOts = function (url, params) {
  const query = {
    // baseURL: process.env.VUE_APP_URL,
    url: url,
    method: 'post',
    withCredentials: true,
    timeout: 600000,
    data: params,
    headers: { 'Content-Type': 'application/json', 'request-ajax': true }
  }
  return request(true, query)
}
const postWithOutLoadTip = function (url, params) {
  const query = {
    baseURL: process.env.VUE_APP_URL,
    url: url,
    method: 'post',
    withCredentials: true,
    timeout: 600000,
    data: params,
    headers: { 'Content-Type': 'application/json', 'request-ajax': true }
  }
  return request(false, query)
}
const get = function (url, params) {
  const query = {
    baseURL: process.env.VUE_APP_URL,
    url: url,
    method: 'get',
    withCredentials: true,
    timeout: 600000,
    params: params,
    headers: { 'request-ajax': true }
  }
  return request(false, query)
}
const form = function (url, params) {
  const query = {
    baseURL: process.env.VUE_APP_URL,
    url: url,
    method: 'post',
    withCredentials: true,
    timeout: 600000,
    data: params,
    headers: { 'Content-Type': 'multipart/form-data', 'request-ajax': true }
  }
  return request(false, query)
}
export {
  post,
  postOts,
  getOts,
  postWithLoadTip,
  postWithOutLoadTip,
  postWithLoadTipOts,
  get,
  form
}
src/utils/scroll-to.js
New file
@@ -0,0 +1,58 @@
Math.easeInOutQuad = function (t, b, c, d) {
  t /= d / 2
  if (t < 1) {
    return c / 2 * t * t + b
  }
  t--
  return -c / 2 * (t * (t - 2) - 1) + b
}
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function () {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60) }
})()
/**
 * Because it's so fucking difficult to detect the scrolling element, just move them all
 * @param {number} amount
 */
function move (amount) {
  document.documentElement.scrollTop = amount
  document.body.parentNode.scrollTop = amount
  document.body.scrollTop = amount
}
function position () {
  return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}
/**
 * @param {number} to
 * @param {number} duration
 * @param {Function} callback
 */
export function scrollTo (to, duration, callback) {
  const start = position()
  const change = to - start
  const increment = 20
  let currentTime = 0
  duration = (typeof (duration) === 'undefined') ? 500 : duration
  var animateScroll = function () {
    // increment the time
    currentTime += increment
    // find the value with the quadratic in-out easing function
    var val = Math.easeInOutQuad(currentTime, start, change, duration)
    // move the document.body
    move(val)
    // do the animation unless its over
    if (currentTime < duration) {
      requestAnimFrame(animateScroll)
    } else {
      if (callback && typeof (callback) === 'function') {
        // the animation is done so lets callback
        callback()
      }
    }
  }
  animateScroll()
}
src/utils/validate.js
New file
@@ -0,0 +1,89 @@
/**
 * Created by PanJiaChen on 16/11/18.
 */
/**
 * @param {string} path
 * @returns {Boolean}
 */
export function isExternal (path) {
  return /^(https?:|mailto:|tel:)/.test(path)
}
/**
 * @param {string} str
 * @returns {Boolean}
 */
export function validUsername (str) {
  // eslint-disable-next-line camelcase
  const valid_map = ['admin', 'editor']
  return valid_map.indexOf(str.trim()) >= 0
}
/**
 * @param {string} url
 * @returns {Boolean}
 */
export function validURL (url) {
  const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  return reg.test(url)
}
/**
 * @param {string} str
 * @returns {Boolean}
 */
export function validLowerCase (str) {
  const reg = /^[a-z]+$/
  return reg.test(str)
}
/**
 * @param {string} str
 * @returns {Boolean}
 */
export function validUpperCase (str) {
  const reg = /^[A-Z]+$/
  return reg.test(str)
}
/**
 * @param {string} str
 * @returns {Boolean}
 */
export function validAlphabets (str) {
  const reg = /^[A-Za-z]+$/
  return reg.test(str)
}
/**
 * @param {string} email
 * @returns {Boolean}
 */
export function validEmail (email) {
  // eslint-disable-next-line no-useless-escape
  const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
  return reg.test(email)
}
/**
 * @param {string} str
 * @returns {Boolean}
 */
export function isString (str) {
  if (typeof str === 'string' || str instanceof String) {
    return true
  }
  return false
}
/**
 * @param {Array} arg
 * @returns {Boolean}
 */
export function isArray (arg) {
  if (typeof Array.isArray === 'undefined') {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
  return Array.isArray(arg)
}
src/views/demography/index.vue
New file
@@ -0,0 +1,217 @@
<template>
  <div class="demography">
    <div hidden>{{goLangType}}</div>
    <el-form :model="demographyMenber" ref="demographyMenber" label-width="25%" style="padding: 4%">
      <h2 class="align-center">{{$t('demography')}}</h2>
      <el-row :key="index" v-for="(item, index) in demographyMenber.data">
        <div v-if="item.types== 'text' && item.isRequired=='1'">
          <el-form-item :label="item.paramName"
                        :prop="'data.'+index+'.paramValue'"
                        :rules="[{ required: true, message: $t('required'),trigger: 'change'},{max: 32, message: $t('range32'), trigger: 'change'}]">
            <el-input v-model="item.paramValue"></el-input>
          </el-form-item>
        </div>
        <div v-if="item.types== 'text' && item.isRequired !='1'">
          <el-form-item :label="item.paramName">
            <el-input v-model="item.paramValue"></el-input>
          </el-form-item>
        </div>
        <div v-if="item.types=='radio' && item.isRequired=='1'">
          <el-form-item :label="item.paramName"
                        :prop="'data.'+index+'.paramValue'"
                        :rules="[{ required: true, message: $t('required'),trigger: 'change'}]">
            <el-radio-group v-model="item.paramValue">
              <el-radio v-for="(itemIter, index) in item.dictData" :key="index" :label="itemIter.dictLabel">
                <span class="question-prefix">{{itemIter.dictLabel}}</span>
              </el-radio>
            </el-radio-group>
          </el-form-item>
        </div>
        <div v-if="item.types=='radio' && item.isRequired !='1'">
          <el-form-item :label="item.paramName">
            <el-radio-group v-model="item.paramValue">
              <el-radio v-for="(itemIter, index) in item.dictData" :key="index" :label="itemIter.dictLabel">
                <span class="question-prefix">{{itemIter.dictLabel}}</span>
              </el-radio>
            </el-radio-group>
          </el-form-item>
        </div>
        <div v-if="item.types=='date' && item.isRequired =='1'">
          <el-form-item :label="item.paramName"
                        :prop="'data.'+index+'.paramValue'"
                        :rules="[{ required: true, message: $t('required'),trigger: 'change'}]">
            <el-col>
              <el-date-picker type="date" :placeholder="$t('selectData')" v-model="item.paramValue"
                              style="width: 100%;"></el-date-picker>
            </el-col>
          </el-form-item>
        </div>
        <div v-if="item.types=='date' && item.isRequired !='1'">
          <el-form-item :label="item.paramName">
            <el-col>
              <el-date-picker type="date" :placeholder="$t('selectData')" v-model="item.paramValue"
                              style="width: 100%;"></el-date-picker>
            </el-col>
          </el-form-item>
        </div>
        <div v-if="item.types=='select' && item.isRequired =='1'">
          <el-form-item :label="item.paramName"
                        :rules="[{ required: true, message: $t('required'),trigger: 'change'}]">
            <el-select v-model="item.paramValue" :placeholder= "$t('pleaseSelect')">
              <el-option
                v-for="itemIter in item.dictData"
                :key="itemIter.dictValue"
                :label="itemIter.dictLabel"
                :value="itemIter.dictValue">
              </el-option>
            </el-select>
          </el-form-item>
        </div>
        <div v-if="item.types=='select' && item.isRequired !='1'">
          <el-form-item :label="item.paramName">
            <el-select v-model="item.paramValue" :placeholder= "$t('pleaseSelect')">
              <el-option
                v-for="itemIter in item.dictData"
                :key="itemIter.dictValue"
                :label="itemIter.dictLabel"
                :value="itemIter.dictValue">
              </el-option>
            </el-select>
          </el-form-item>
        </div>
        <div v-else></div>
      </el-row>
      <el-row class="do-align-center">
        <el-button @click="submitDemography" type="danger" :disabled="submitDisable">{{$t('submission')}}</el-button>
      </el-row>
    </el-form>
  </div>
</template>
<script>
import demographyApi from '@/api/demography'
import { mapMutations, mapState } from 'vuex'
import examPaperApi from '@/api/examPaper'
// import QuestionEdit from "./childCompoment/QuestionEdit";
export default {
  // components: { QuestionEdit },
  data () {
    return {
      demographyMenber: { data: [] },
      productId: 0,
      paramsInfo: {},
      parasInfos: [],
      parasInfoIds: {},
      formDo: {},
      submitDisable: false,
      isStart: this.$route.query.isStart
    }
  },
  created () {
  },
  methods: {
    submitDemography: function () {
      let _this = this
      _this.submitDisable = true
      this.$refs.demographyMenber.validate(vaild => {
        if (vaild) {
          _this.parasInfos = []
          for (let i = 0; i < this.demographyMenber.data.length; i++) {
            let _paramsInfo = {}
            if (_this.parasInfoIds[_this.demographyMenber.data[i].paramId] !== undefined) {
              _paramsInfo.infoId = _this.parasInfoIds[_this.demographyMenber.data[i].paramId].infoId
            }
            _paramsInfo.userId = _this.demographyMenber.data[i].userId
            _paramsInfo.memberId = _this.demographyMenber.data[i].memberId
            _paramsInfo.paramId = _this.demographyMenber.data[i].paramId
            _paramsInfo.paramName = _this.demographyMenber.data[i].paramName
            _paramsInfo.paramValue = _this.demographyMenber.data[i].paramValue
            if (_paramsInfo.paramValue.trim() === '') {
              continue
            }
            _this.parasInfos.push(_paramsInfo)
          }
          demographyApi.submitDemography(_this.memberToken, _this.langType, _this.parasInfos).then(function (result) {
            if (result && result.code === 1) {
              examPaperApi.select(_this.memberToken, _this.langType).then(re => {
                _this.formDo = re.response
                _this.setDoUrl(re.response.questionTemplateId)
                if (_this.isStart) {
                  _this.$router.push({ path: '/exam/' + _this.doUrl })
                  return
                }
                _this.$router.push({ path: '/guide/index', query: { formDo: _this.formDo } });
              }).catch(e => {
                _this.$message.error(_this.$t('noquestionMsg'))
              })
            } else {
              _this.$message.error(result.message + 'submitDemography')
            }
          }).catch(e => {
            _this.$message.error(_this.$t('questionExceptionMsg'))
          })
        } else {
          this.$message.error(_this.$t('requiredMsg'))
          _this.submitDisable = false
        }
      })
    },
    createDemography: function () {
      let _this = this
      demographyApi.demography(_this.memberToken, _this.langType).then(re => {
        for (let i = 0; i < re.response.length; i++) {
          if (re.response[i].paramValue === null) {
            re.response[i].paramValue = ''
          }
        }
        _this.demographyMenber.data = re.response
        demographyApi.queryDemographyParamInfo(_this.memberToken).then(
          re => {
            if (re.code !== 1) {
              return
            }
            for (let i = 0; i < re.response.length; i++) {
              _this.parasInfoIds[re.response[i].paramId] = re.response[i]
            }
            for (let i = 0; i < _this.demographyMenber.data.length; i++) {
              if (_this.parasInfoIds[_this.demographyMenber.data[i].paramId] !== undefined) {
                _this.demographyMenber.data[i].paramValue = _this.parasInfoIds[_this.demographyMenber.data[i].paramId].paramValue
              }
            }
          }
        ).catch(e => {
        })
      }).catch(e => {
        _this.$message.error(_this.$t('userinfoErro'))
      })
    },
    ...mapMutations('user', ['setMemberTocken']),
    ...mapMutations('exam', ['setDoUrl'])
  },
  computed: {
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('exam', { doUrl: state => state.doUrl }),
    ...mapState('user', { langType: state => state.langType }),
    goLangType: function () {
      // `this` 指向 vm 实例
      let _this = this
      _this.createDemography()
      return _this.langType
    }
  }
}
</script>
<style lang="scss" scoped>
  .demography {
    width: 90%;
    margin: 0 auto;
    margin-top: 5%;
  }
  .align-center {
    text-align: center
  }
</style>
src/views/demography/langOption.vue
New file
@@ -0,0 +1,155 @@
<template>
  <div class="demography">
    <div hidden>{{goLangType}}</div>
    <el-form class="align-center" style="padding: 3%">
      <h1 v-bind:class="{langKaiStyle : isLangKaiStyle}">{{langWelcome}}</h1>
      <h3 v-bind:class="{langKaiStyle : isLangKaiStyle}" style="color: #3a8ee6">{{langOption}}</h3>
            <el-select v-model="langSelectValue"   :placeholder= "$t('pleaseSelect')" style="width: 55%">
              <el-option
                v-for="itemIter in langTypes"
                :key="itemIter.dictValue"
                :label="itemIter.dictValue"
                :value="itemIter.dictValue">
              </el-option>
            </el-select>
      <el-footer class="do-align-center">
        <el-button @click="submitLangOption" type="danger">{{submissionLang}}</el-button>
      </el-footer>
    </el-form>
  </div>
</template>
<script>
import demographyApi from '@/api/demography'
import { mapMutations, mapState } from 'vuex'
// import examPaperApi from '@/api/examPaper'
// import QuestionEdit from "./childCompoment/QuestionEdit";
import examPaperApi from '@/api/examPaper'
export default {
  // components: { QuestionEdit },
  data () {
    return {
      langTypes: [],
      langSelectValue: this.$route.query.langSelectValue,
      langWelcome: this.$t('langWelcomeEnglish'),
      langOption: this.$t('langOptionEnglish'),
      timer: null,
      submissionLang: this.$t('submission'),
      isLangKaiStyle: false
    }
  },
  created: function () {
    let _this = this
    window.clearInterval(this.timer)
    _this.getLangType()
    _this.getLangAndRportTemplateId()
    let num = 0
    this.timer = setInterval(function () {
      if (num > 9) {
        num = 0
      }
      if (num === 0) {
        _this.langWelcome = _this.$t('langWelcomeEnglish')
        _this.langOption = _this.$t('langOptionEnglish')
        _this.isLangKaiStyle = false
      }
      if (num === 3) {
        _this.langWelcome = _this.$t('langWelcomeChinese')
        _this.langOption = _this.$t('langOptionChinese')
        _this.isLangKaiStyle = true
      }
      if (num === 6) {
        _this.langWelcome = _this.$t('langWelcomeThai')
        _this.langOption = _this.$t('langOptionThai')
        _this.isLangKaiStyle = false
      }
      num++
    }, 1000)
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  methods: {
    submitLangOption: function () {
      let _this = this
      _this.setLangType(_this.langSelectValue)
      _this.$router.push({ path: '/demography/fill/index', query: { demographyMenber: _this.memberToken, isStart: false } })
      window.clearInterval(this.timer)
    },
    getLangAndRportTemplateId () {
      let _this = this
      if (_this.reportTemplateId.trim() === '' || _this.reportTemplateId.trim() === null) {
        examPaperApi.selectTokenId(_this.memberToken).then(re => {
          if (re.response.finish === 1) {
            _this.$router.push({ path: '/exam/finish', query: { name: '', content: _this.$t('finishMsg') } })
          } else {
            _this.setReportTemplateId(re.response.reportTemplateId)
            demographyApi.queryLangList(_this.memberToken, _this.reportTemplateId).then(function (result) {
              if (result && result.code === 1) {
                _this.langTypes = result.response
              }
            }
            ).catch(e => {
              _this.$message.error(_this.$t('noquestionMsg'))
            })
          }
        }).catch(e => {
        })
      } else {
        demographyApi.queryLangList(_this.memberToken, _this.reportTemplateId).then(function (result) {
          if (result && result.code === 1) {
            _this.langTypes = result.response
          }
        }
        ).catch(e => {
          _this.$message.error(_this.$t('noquestionMsg'))
        })
      }
    },
    ...mapMutations('user', ['setMemberTocken']),
    ...mapMutations('user', ['setLangType']),
    ...mapMutations('user', ['getLangType']),
    ...mapMutations('exam', ['setReportTemplateId'])
  },
  computed: {
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('exam', { doUrl: state => state.doUrl }),
    ...mapState('exam', { reportTemplateId: state => state.reportTemplateId }),
    ...mapState('user', { langType: state => state.langType }),
    goLangType: function () {
      // `this` 指向 vm 实例
      let _this = this
      if (_this.langSelectValue === 'Chinese') {
        _this.$i18n.locale = 'zh'
        _this.submissionLang = '提交'
      } else if (_this.langSelectValue === 'Thai') {
        _this.$i18n.locale = 'th'
        _this.submissionLang = 'เสนอ'
      } else {
        _this.$i18n.locale = 'en'
        _this.submissionLang = 'submit'
      }
      return _this.langType
    }
  }
}
</script>
<style lang="scss" scoped>
  .demography {
    width: 90%;
    margin: 0 auto;
    margin-top: 5%;
  }
  .align-center {
    text-align: center
  }
  .langKaiStyle {
    font-family: Kai
  }
</style>
src/views/demography/login.vue
New file
@@ -0,0 +1,113 @@
<template>
  <div>
  </div>
</template>
<script>
import demographyApi from '@/api/demography'
import { mapMutations, mapState } from 'vuex'
import examPaperApi from '@/api/examPaper'
export default {
  // components: { QuestionEdit },
  data () {
    return {
      demographyMenber: { data: [] },
      paraMemberToken: this.$route.params.memberToken,
      productId: 0,
      paramsInfo: {},
      parasInfos: [],
      parasInfoIds: {},
      formDo: {},
      statusInfo: { '2': this.$t('statusInfo2'), '3': this.$t('statusInfo3'), '4': this.$t('statusInfo4') }
    }
  },
  created () {
    let _this = this
    _this.getLangType()
    // 记录登陆日志
    examPaperApi.recordLogininfor(_this.paraMemberToken).then(re => {
    }).catch(e => {
    })
    // 已经答完了不能答题
    examPaperApi.selectTokenId(_this.paraMemberToken).then(re => {
      if (re.response.finish === 1) {
        _this.$router.push({ path: '/exam/finish', query: { name: '', content: _this.$t('finishMsg') } })
      } else if (re.response.status !== '1') {
        _this.$router.push({ path: '/exam/exception', query: { name: '', content: _this.statusInfo[re.response.status] } })
      } else {
        _this.setMemberTocken(_this.paraMemberToken)
        _this.setDoUrl(re.response.doUrl)
        _this.setReportTemplateId(re.response.reportTemplateId)
        _this.queryCreate()
      }
    }).catch(e => {
    })
  },
  methods: {
    queryCreate: function () {
      // 先查询人口学是否已经填完
      let _this = this
      demographyApi.queryDemographyParamInfo(_this.paraMemberToken).then(
        reParam => {
          if (reParam.code !== 1) {
            // 先选择密码
            _this.$router.push({ path: '/demography/fill/langOption', query: { langSelectValue: _this.langType } })
            _this.setMemberTocken(_this.paraMemberToken)
            return
          }
          if (reParam.response[0].langType === 'Chinese') {
            this.$i18n.locale = 'zh'
            _this.setLangType('Chinese')
          } else {
            _this.setLangType('English')
            this.$i18n.locale = 'en'
          }
          _this.setMemberId(reParam.response[0].memberId)
          // 判断如果是在第一题,那么就展示指导语
          examPaperApi.selectTokenId(_this.memberToken).then(re => {
            let answered = re.response.examItemOrderAndContentList
            if (answered === null) {
              examPaperApi.select(_this.memberToken, _this.langType).then(re => {
                _this.formDo = re.response
                _this.setDoUrl(re.response.questionTemplateId)
                _this.$router.push({ path: '/guide/index', query: { formDo: _this.formDo } })
              }).catch(e => {
                _this.$message.error(_this.$t('noquestionMsg'))
              })
            } else {
              _this.$router.push({ path: '/exam/' + _this.doUrl })
            }
          }).catch(e => {
          })
        }
      ).catch(e => {
      })
    },
    ...mapMutations('user', ['setMemberTocken']),
    ...mapMutations('user', ['setMemberId']),
    ...mapMutations('exam', ['setDoUrl']),
    ...mapMutations('exam', ['setReportTemplateId']),
    ...mapMutations('user', ['setLangType']),
    ...mapMutations('user', ['getLangType'])
  },
  computed: {
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('user', { memberId: state => state.memberId }),
    ...mapState('exam', { doUrl: state => state.doUrl }),
    ...mapState('exam', { reportTemplateId: state => state.reportTemplateId }),
    ...mapState('user', { langType: state => state.langType })
  }
}
</script>
<style lang="scss" scoped>
  .demography {
    width: 90%;
    margin: 0 auto;
    margin-top: 5%;
  }
  .align-center {
    text-align: center
  }
</style>
src/views/error-page/401.vue
New file
@@ -0,0 +1,99 @@
<template>
  <div class="errPage-container">
    <el-button icon="arrow-left" class="pan-back-btn" @click="back">
      返回
    </el-button>
    <el-row>
      <el-col :span="12">
        <h1 class="text-jumbo text-ginormous">
          Oops!
        </h1>
        gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
        <h2>你没有权限去该页面</h2>
        <h6>如有不满请联系你领导</h6>
        <ul class="list-unstyled">
          <li>或者你可以去:</li>
          <li class="link-type">
            <router-link to="/dashboard">
              回首页
            </router-link>
          </li>
          <li class="link-type">
            <a href="https://www.taobao.com/">随便看看</a>
          </li>
          <li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li>
        </ul>
      </el-col>
      <el-col :span="12">
        <img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
      </el-col>
    </el-row>
    <el-dialog :visible.sync="dialogVisible" title="随便看">
      <img :src="ewizardClap" class="pan-img">
    </el-dialog>
  </div>
</template>
<script>
import errGif from '@/assets/401_images/401.gif'
export default {
  name: 'Page401',
  data () {
    return {
      errGif: errGif + '?' + +new Date(),
      ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
      dialogVisible: false
    }
  },
  methods: {
    back () {
      if (this.$route.query.noGoBack) {
        this.$router.push({ path: '/dashboard' })
      } else {
        this.$router.go(-1)
      }
    }
  }
}
</script>
<style lang="scss" scoped>
  .errPage-container {
    width: 800px;
    max-width: 100%;
    margin: 100px auto;
    .pan-back-btn {
      background: #008489;
      color: #fff;
      border: none!important;
    }
    .pan-gif {
      margin: 0 auto;
      display: block;
    }
    .pan-img {
      display: block;
      margin: 0 auto;
      width: 100%;
    }
    .text-jumbo {
      font-size: 60px;
      font-weight: 700;
      color: #484848;
    }
    .list-unstyled {
      font-size: 14px;
      li {
        padding-bottom: 5px;
      }
      a {
        color: #008489;
        text-decoration: none;
        &:hover {
          text-decoration: underline;
        }
      }
    }
  }
</style>
src/views/error-page/404.vue
New file
@@ -0,0 +1,225 @@
<template>
  <div class="wscn-http404-container">
    <div class="wscn-http404">
      <div class="pic-404">
        <img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
        <img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
        <img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
        <img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
      </div>
      <div class="bullshit">
        <div class="bullshit__oops">OOPS!</div>
        <div class="bullshit__headline">{{ message }}</div>
        <div class="bullshit__info">请检查你的访问地址是否正确, 或点击下面按钮返回主页.</div>
        <a href="" class="bullshit__return-home">返回主页</a>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Page404',
  computed: {
    message () {
      return '页面未找到...'
    }
  }
}
</script>
<style lang="scss" scoped>
.wscn-http404-container{
  transform: translate(-50%,-50%);
  position: absolute;
  top: 40%;
  left: 50%;
}
.wscn-http404 {
  position: relative;
  width: 1200px;
  padding: 0 50px;
  overflow: hidden;
  .pic-404 {
    position: relative;
    float: left;
    width: 600px;
    overflow: hidden;
    &__parent {
      width: 100%;
    }
    &__child {
      position: absolute;
      &.left {
        width: 80px;
        top: 17px;
        left: 220px;
        opacity: 0;
        animation-name: cloudLeft;
        animation-duration: 2s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
        animation-delay: 1s;
      }
      &.mid {
        width: 46px;
        top: 10px;
        left: 420px;
        opacity: 0;
        animation-name: cloudMid;
        animation-duration: 2s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
        animation-delay: 1.2s;
      }
      &.right {
        width: 62px;
        top: 100px;
        left: 500px;
        opacity: 0;
        animation-name: cloudRight;
        animation-duration: 2s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
        animation-delay: 1s;
      }
      @keyframes cloudLeft {
        0% {
          top: 17px;
          left: 220px;
          opacity: 0;
        }
        20% {
          top: 33px;
          left: 188px;
          opacity: 1;
        }
        80% {
          top: 81px;
          left: 92px;
          opacity: 1;
        }
        100% {
          top: 97px;
          left: 60px;
          opacity: 0;
        }
      }
      @keyframes cloudMid {
        0% {
          top: 10px;
          left: 420px;
          opacity: 0;
        }
        20% {
          top: 40px;
          left: 360px;
          opacity: 1;
        }
        70% {
          top: 130px;
          left: 180px;
          opacity: 1;
        }
        100% {
          top: 160px;
          left: 120px;
          opacity: 0;
        }
      }
      @keyframes cloudRight {
        0% {
          top: 100px;
          left: 500px;
          opacity: 0;
        }
        20% {
          top: 120px;
          left: 460px;
          opacity: 1;
        }
        80% {
          top: 180px;
          left: 340px;
          opacity: 1;
        }
        100% {
          top: 200px;
          left: 300px;
          opacity: 0;
        }
      }
    }
  }
  .bullshit {
    position: relative;
    float: left;
    width: 300px;
    padding: 30px 0;
    overflow: hidden;
    &__oops {
      font-size: 32px;
      font-weight: bold;
      line-height: 40px;
      color: #1482f0;
      opacity: 0;
      margin-bottom: 20px;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-fill-mode: forwards;
    }
    &__headline {
      font-size: 20px;
      line-height: 24px;
      color: #222;
      font-weight: bold;
      opacity: 0;
      margin-bottom: 10px;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-delay: 0.1s;
      animation-fill-mode: forwards;
    }
    &__info {
      font-size: 13px;
      line-height: 21px;
      color: grey;
      opacity: 0;
      margin-bottom: 30px;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-delay: 0.2s;
      animation-fill-mode: forwards;
    }
    &__return-home {
      display: block;
      float: left;
      width: 110px;
      height: 36px;
      background: #1482f0;
      border-radius: 100px;
      text-align: center;
      color: #ffffff;
      opacity: 0;
      font-size: 14px;
      line-height: 36px;
      cursor: pointer;
      animation-name: slideUp;
      animation-duration: 0.5s;
      animation-delay: 0.3s;
      animation-fill-mode: forwards;
    }
    @keyframes slideUp {
      0% {
        transform: translateY(60px);
        opacity: 0;
      }
      100% {
        transform: translateY(0);
        opacity: 1;
      }
    }
  }
}
</style>
src/views/exam/components/QuestionEdit.vue
New file
@@ -0,0 +1,121 @@
<template>
  <div style="line-height:1.3">
    <div v-if="qType==1" v-loading="qLoading">
      <div class="q-title" v-html="qLabel+'&nbsp;&nbsp;'+question.title" style="font-size: 23px"/>
      <div class="q-content">
        <el-radio-group v-model="answer.content" @change="answer.completed = true">
          <el-radio  v-for="item in question.items"  :key="item.prefix"  :label="item.prefix" @change="jumpNext" :disabled="answer.readOnly" style="display:block;">
            <span class="question-prefix" style="font-size: 18px">{{abcValue[item.prefix]}}.</span>
            <span v-html="item.content" class="q-item-span-content" style="font-size: 18px"></span>
          </el-radio>
        </el-radio-group>
      </div>
    </div>
    <div v-else-if="qType==2" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div class="q-content">
        <el-checkbox-group v-model="answer.contentArray" @change="answer.completed = true" >
          <el-checkbox v-for="item in question.items" :label="item.prefix" :key="item.prefix" @change="jumpNext"  >
            <span class="question-prefix">{{item.prefix}}.</span>
            <span v-html="item.content" class="q-item-span-content"></span>
          </el-checkbox>
        </el-checkbox-group>
      </div>
    </div>
    <div v-else-if="qType==3" v-loading="qLoading">
      <div class="q-title" v-html="question.title" style="display: inline;margin-right: 10px"/>
      <span style="padding-right: 10px;">(</span>
      <el-radio-group v-model="answer.content" @change="answer.completed = true" >
        <el-radio  v-for="item in question.items"  :key="item.prefix"  :label="item.prefix" @change="jumpNext" >
          <span v-html="item.content" class="q-item-span-content"></span>
        </el-radio>
      </el-radio-group>
      <span style="padding-left: 10px;">)</span>
    </div>
    <div v-else-if="qType==4" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div>
        <el-form-item :label="item.prefix" :key="item.prefix"  v-for="item in question.items"  label-width="50px" style="margin-top: 10px;margin-bottom: 10px;">
          <el-input v-model="answer.contentArray[item.prefix-1]"  @change="answer.completed = true" />
        </el-form-item>
      </div>
    </div>
    <div v-else-if="qType==5" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div>
        <el-input v-model="answer.content" type="textarea" rows="5"  @change="answer.completed = true"/>
      </div>
    </div>
    <div v-else>
    </div>
  </div>
</template>
<script>
export default {
  name: 'QuestionShow',
  data () {
    return {
      abcValue: { '1': 'A', '2': 'B', '3': 'C', '4': 'D', '5': 'E', '6': 'F' }
    }
  },
  props: {
    question: {
      type: Object,
      default: function () {
        return {}
      }
    },
    answer: {
      type: Object,
      default: function () {
        return { id: null, content: '', contentArray: [] }
      }
    },
    qLoading: {
      type: Boolean,
      default: false
    },
    qType: {
      type: Number,
      default: 0
    },
    qLabel: {
      type: String,
      default: ''
    }
  },
  methods: {
    jumpNext () {
      let _this = this
      this.answer.readOnly = true
      if (this.answer.content === null || this.answer.content.trim() === '') {
        return
      }
      setTimeout(function () {
        _this.$emit('callNextSubject')
      }, 500)
    }
  }
}
</script>
<style lang="scss" scoped>
  span:hover {
    font-weight: bold;
  }
  .q-content {
  margin: 10px 10px 15px 25px;
  p {
    display: inline !important;
  }
  .el-checkbox__input {
    line-height: 1.8;
    vertical-align: top;
  }
}
</style>
src/views/exam/components/QuestionIntelligenceEdit.vue
New file
@@ -0,0 +1,121 @@
<template>
  <div style="line-height:1.8">
    <div v-if="qType==1" v-loading="qLoading">
      <div class="q-title" v-html="qLabel+'&nbsp;&nbsp;'+question.title" style="font-size: x-large"/>
      <div class="q-content" :hidden="question.template==='intelligence_title'">
        <el-radio-group v-model="answer.content" @change="answer.completed = true">
          <el-radio class="intelligence-el_radio"  v-for="(item, index) in question.items"  :key="item.prefix"  :label="item.prefix" @change="jumpNext" :disabled="answer.readOnly" :style="{display:displayVal}">
            <span class="question-prefix" style="font-size: 18px">{{abcValue[index+1]}}.</span>
            <span v-html="item.content" class="q-item-span-content" style="font-size: 18px"></span>
            <br>
          </el-radio>
        </el-radio-group>
      </div>
    </div>
    <div v-else-if="qType==2" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div class="q-content">
        <el-checkbox-group v-model="answer.contentArray" @change="answer.completed = true" >
          <el-checkbox v-for="item in question.items" :label="item.prefix" :key="item.prefix" @change="jumpNext"  >
            <span class="question-prefix">{{item.prefix}}.</span>
            <span v-html="item.content" class="q-item-span-content"></span>
          </el-checkbox>
        </el-checkbox-group>
      </div>
    </div>
    <div v-else-if="qType==3" v-loading="qLoading">
      <div class="q-title" v-html="question.title" style="display: inline;margin-right: 10px"/>
      <span style="padding-right: 10px;">(</span>
      <el-radio-group v-model="answer.content" @change="answer.completed = true" >
        <el-radio  v-for="item in question.items"  :key="item.prefix"  :label="item.prefix" @change="jumpNext" >
          <span v-html="item.content" class="q-item-span-content"></span>
        </el-radio>
      </el-radio-group>
      <span style="padding-left: 10px;">)</span>
    </div>
    <div v-else-if="qType==4" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div>
        <el-form-item :label="item.prefix" :key="item.prefix"  v-for="item in question.items"  label-width="50px" style="margin-top: 10px;margin-bottom: 10px;">
          <el-input v-model="answer.contentArray[item.prefix-1]"  @change="answer.completed = true" />
        </el-form-item>
      </div>
    </div>
    <div v-else-if="qType==5" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div>
        <el-input v-model="answer.content" type="textarea" rows="5"  @change="answer.completed = true"/>
      </div>
    </div>
    <div v-else>
    </div>
  </div>
</template>
<script>
export default {
  name: 'QuestionIntelligenceShow',
  data () {
    return {
      abcValue: { 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'F' }
      // abcValue: { '1': 'A', '2': 'B', '3': 'C', '4': 'D', '5': 'E', '6': 'F' }
    }
  },
  props: {
    question: {
      type: Object,
      default: function () {
        return {}
      }
    },
    answer: {
      type: Object,
      default: function () {
        return { id: null, content: '', contentArray: [] }
      }
    },
    qLoading: {
      type: Boolean,
      default: false
    },
    qType: {
      type: Number,
      default: 0
    },
    qLabel: {
      type: String,
      default: ''
    },
    order: {
      type: Number,
      default: 0
    },
    displayVal: {
      type: String,
      default: 'block'
    }
  },
  methods: {
    jumpNext () {
      let _this = this
      // this.answer.readOnly = true
      // if (this.answer.content === null || this.answer.content.trim() === '') {
      //   return
      // }
      setTimeout(function () {
        _this.$emit('callNextSubject', [_this.order])
      }, 500)
    }
  }
}
</script>
<style lang="scss" scoped>
  span:hover {
    font-weight: bold;
  }
  .intelligence-el_radio {
    margin-right: 60px;
  }
</style>
src/views/exam/components/QuestionJobEdit.vue
New file
@@ -0,0 +1,116 @@
<template>
  <div style="line-height:1.8">
    <div v-if="qType==1" v-loading="qLoading">
      <div class="q-title" v-html="qLabel+'&nbsp;&nbsp;'+question.title" style="font-size: large"/>
      <div class="q-content">
        <el-radio-group v-model="answer.content" @change="answer.completed = true">
          <el-radio  v-for="item in question.items"  :key="item.prefix"  :label="item.prefix" @change="jumpNext" :disabled="answer.readOnly">
            <span class="question-prefix" style="font-size: 18px">{{abcValue[item.prefix]}}.</span>
            <span v-html="item.content" class="q-item-span-content" style="font-size: 15px"></span>
          </el-radio>
        </el-radio-group>
      </div>
    </div>
    <div v-else-if="qType==2" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div class="q-content">
        <el-checkbox-group v-model="answer.contentArray" @change="answer.completed = true" >
          <el-checkbox v-for="item in question.items" :label="item.prefix" :key="item.prefix" @change="jumpNext"  >
            <span class="question-prefix">{{item.prefix}}.</span>
            <span v-html="item.content" class="q-item-span-content"></span>
          </el-checkbox>
        </el-checkbox-group>
      </div>
    </div>
    <div v-else-if="qType==3" v-loading="qLoading">
      <div class="q-title" v-html="question.title" style="display: inline;margin-right: 10px"/>
      <span style="padding-right: 10px;">(</span>
      <el-radio-group v-model="answer.content" @change="answer.completed = true" >
        <el-radio  v-for="item in question.items"  :key="item.prefix"  :label="item.prefix" @change="jumpNext" >
          <span v-html="item.content" class="q-item-span-content"></span>
        </el-radio>
      </el-radio-group>
      <span style="padding-left: 10px;">)</span>
    </div>
    <div v-else-if="qType==4" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div>
        <el-form-item :label="item.prefix" :key="item.prefix"  v-for="item in question.items"  label-width="50px" style="margin-top: 10px;margin-bottom: 10px;">
          <el-input v-model="answer.contentArray[item.prefix-1]"  @change="answer.completed = true" />
        </el-form-item>
      </div>
    </div>
    <div v-else-if="qType==5" v-loading="qLoading">
      <div class="q-title" v-html="question.title"/>
      <div>
        <el-input v-model="answer.content" type="textarea" rows="5"  @change="answer.completed = true"/>
      </div>
    </div>
    <div v-else>
    </div>
  </div>
</template>
<script>
export default {
  name: 'QuestionJobEdit',
  data () {
    return {
      abcValue: { '1': 'A', '2': 'B', '3': 'C', '4': 'D', '5': 'E', '6': 'F' }
    }
  },
  props: {
    question: {
      type: Object,
      default: function () {
        return {}
      }
    },
    answer: {
      type: Object,
      default: function () {
        return { id: null, content: '', contentArray: [] }
      }
    },
    qLoading: {
      type: Boolean,
      default: false
    },
    isReadOnly: {
      type: Boolean,
      default: true
    },
    qType: {
      type: Number,
      default: 0
    },
    qLabel: {
      type: String,
      default: ''
    }
  },
  methods: {
    jumpNext () {
      let _this = this
      if (this.isReadOnly) {
        this.answer.readOnly = true
      } else {
        this.answer.readOnly = false
      }
      if (this.answer.content === null || this.answer.content.trim() === '') {
        return
      }
      setTimeout(function () {
        _this.$emit('callNextSubject')
      }, 500)
    }
  }
}
</script>
<style lang="scss" scoped>
  span:hover {
    font-weight: bold;
  }
</style>
src/views/exam/paper/do.vue
New file
@@ -0,0 +1,295 @@
<template>
  <div>
    <el-container  class="app-item-contain">
      <el-header class="align-center" v-if="guide">
        <h1 v-html="titleItems[0].name+ ' 指导语' "></h1>
      </el-header>
      <el-header class="align-center" style="height: 80px; padding: 2%" v-else>
        <h1>{{form.name}}</h1>
        <div>
          <!--<span class="question-title-padding">答卷总分:{{form.score}}</span>-->
          <span class="question-title-padding">{{$t('recommendedTime')}}:{{form.suggestTime}}{{$t('minute')}}</span>
        </div>
      </el-header>
      <el-main>
        <div v-html="titleItems[0].guide" v-if="guide"></div>
        <el-row class="do-align-center" v-if="guide">
          <el-button @click="startDo" type="primary">{{$t('start')}}</el-button>
        </el-row>
        <el-form :model="form" ref="form" v-loading="formLoading" label-width="100%" v-else>
          <el-row :key="index"  v-for="(titleItem,index) in titleItems">
            <h3>{{$t('theFirst')}}{{order+1}}{{$t('section')}}:{{titleItem.name}}</h3>
            <div class="q-content" v-html="titleItem.description"></div>
            <el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
              <el-form-item :key="questionItem.itemOrder" class="exam-question-item" label-width="50px"
                            v-for="questionItem in titleItem.questionItems" :id="'question-'+ questionItem.itemOrder">
                <QuestionEdit :qType="questionItem.questionType" :question="questionItem"
                              :answer="answer.answerItems[questionItem.itemOrder-1]"/>
              </el-form-item>
            </el-card>
          </el-row>
          <el-row class="do-align-center" v-if="order < form.titleItems.length - 1">
            <el-button @click="preSubject" type="warning" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="nextSubject" type="primary" round>{{$t('nextQuestion')}}</el-button>
          </el-row>
          <el-row class="do-align-center" v-else>
            <el-button @click="preSubject" type="warning" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="submitForm" type="primary" :disabled="submitDisable" round>{{$t('submission')}}</el-button>
          </el-row>
        </el-form>
      </el-main>
    </el-container>
    <el-row class="do-exam-title footer">
      <el-col :span="24">
        <el-popover placement="top" title="题目列表" width="200" trigger="click" ref="popovertag">
          <el-tag type="success" slot="reference">题目</el-tag>
          <span :key="item.itemOrder" v-for="(item) in answer.answerItems">
             <el-tag :type="questionCompleted(item.completed)" class="do-exam-title-tag"
                     @click="goAnchor('#question-'+item.itemOrder)">{{item.itemOrder}}</el-tag>
        </span>
        </el-popover>
        <span class="do-exam-time">
          <label>剩余时间:</label>
          <label>{{formatSeconds(remainTime)}}</label>
        </span>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import { formatSeconds } from '@/utils'
import QuestionEdit from '../components/QuestionEdit'
import examPaperApi from '@/api/examPaper'
import examPaperAnswerApi from '@/api/examPaperAnswer'
export default {
  components: { QuestionEdit },
  data () {
    return {
      form: {},
      formLoading: false,
      answer: {
        questionId: null,
        doTime: 0,
        answerItems: [],
        verifyStatus: 2,
        finish: 0
      },
      timer: null,
      problemLength: 0,
      remainTime: 0,
      titleItemName: '', // 考试第二标题
      itemOrder: '', // 题目的序号
      questionItem: {}, // 题目
      titleItems: [],
      order: 0,
      guide: false,
      answered: [],
      submitDisable: false,
      doTime: 0
    }
  },
  created () {
    let _this = this
    _this.form = this.$route.query.formDo
    if (_this.form !== {} && _this.form !== undefined) {
      _this.formLoading = true
      _this.remainTime = _this.form.suggestTime * 60
      _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
      _this.titleItems.push(_this.form.titleItems[0])
      _this.timeReduce()
      _this.initAnswer()
      _this.formLoading = false
    } else {
      _this.formLoading = true
      examPaperApi.select(_this.memberToken).then(re => {
        _this.form = re.response
        _this.remainTime = re.response.suggestTime * 60
        _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
        _this.titleItems.push(_this.form.titleItems[0])
        _this.initAnswer()
        _this.formLoading = false
        examPaperApi.selectTokenId(_this.memberToken).then(re => {
          _this.answered = re.response.examItemOrderAndContentList
          _this.doTime = re.response.doTime
          _this.timeReduce()
          _this.answered.forEach(it => {
            _this.answer.answerItems[parseInt(it.itemOrder) - 1].content = it.content
            _this.answer.answerItems[parseInt(it.itemOrder) - 1].completed = true
          })
        }).catch(e => {
        })
      }).catch(e => {
        _this.$message.info('无测试题目')
      })
    }
  },
  mounted () {
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  methods: {
    formatSeconds (theTime) {
      return formatSeconds(theTime)
    },
    timeReduce () {
      let _this = this
      _this.answer.doTime += _this.doTime
      _this.remainTime -= _this.doTime
      this.timer = setInterval(function () {
        if (_this.remainTime <= 0) {
          _this.remainTime = 0
          ++_this.answer.doTime
        } else {
          ++_this.answer.doTime
          --_this.remainTime
        }
      }, 1000)
    },
    questionCompleted (completed) {
      return this.enumFormat(this.doCompletedTag, completed)
    },
    goAnchor (selector) {
      let _this = this
      let num = parseInt(selector.substr(10))
      for (let i = 0; i < _this.form.titleItems.length; i++) {
        _this.form.titleItems[i].questionItems.forEach(it => {
          if (it.itemOrder === num) {
            _this.titleItems = []
            _this.titleItems.push(_this.form.titleItems[i])
            _this.order = i
          }
        })
      }
      setTimeout(function () {
        _this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
      }, 200)
      _this.$refs['popovertag'].doClose()
    },
    initAnswer () {
      this.answer.id = this.form.id
      let titleItemArray = this.form.titleItems
      for (let tIndex in titleItemArray) {
        let questionArray = titleItemArray[tIndex].questionItems
        for (let qIndex in questionArray) {
          let question = questionArray[qIndex]
          this.answer.answerItems.push({ questionId: question.id, content: null, contentArray: [], completed: false, itemOrder: question.itemOrder, lastContent: null })
        }
      }
    },
    nextSubject: function () {
      let _this = this
      _this.guide = false
      if (_this.order < _this.form.titleItems.length - 1) {
        _this.titleItems = []
        let num = ++_this.order
        _this.titleItems.push(_this.form.titleItems[num])
      }
      examPaperAnswerApi.answerSubmit(this.answer, _this.memberToken).then(re => {
        if (re.code === 1) {
        } else {
          _this.$message.error(re.message + 'answerSubmit')
        }
      }).catch(e => {
        _this.$message.error('提交问卷后台错误')
      })
    },
    preSubject () {
      let _this = this
      _this.guide = false
      if (_this.order > 0) {
        _this.titleItems = []
        let num = --_this.order
        _this.titleItems.push(_this.form.titleItems[num])
        _this.titleItems.forEach(it => {
          it.questionItems.forEach(i => {
            _this.answer.answerItems[parseInt(i.itemOrder) - 1].lastContent = _this.answer.answerItems[parseInt(i.itemOrder) - 1].content
          })
        })
      }
    },
    submitForm () {
      let _this = this
      _this.submitDisable = true
      window.clearInterval(_this.timer)
      _this.formLoading = true
      let unFinish = false
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (_this.answer.answerItems[i].content === null) {
          unFinish = true
          _this.answer.finish = 0
        }
      }
      if (unFinish) {
        _this.$confirm('你有未答完的题目,是否提交保存?', {
          title: '提示',
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          showClose: false
        }).then(async () => {
          _this.answer.verifyStatus = 2
          _this.submitAnswer('数据已经成功的上传至服务器,并已经保存好。谢谢。')
        }).catch(async () => {
          _this.formLoading = false
          _this.submitDisable = false
        })
      } else {
        _this.answer.verifyStatus = 1
        _this.answer.finish = 1
        _this.submitAnswer('您已经完成了工作行为问卷,数据已经成功的上传至服务器,并已经保存好。谢谢。')
      }
    },
    startDo: function () {
      let _this = this
      _this.guide = false
    },
    submitAnswer: function (mess) {
      let _this = this
      examPaperAnswerApi.answerSubmit(this.answer, _this.memberToken).then(re => {
        if (re.code === 1) {
          _this.$router.push({ path: '/exam/finish', query: { name: _this.form.name, content: mess } })
        } else {
          _this.$message.error(re.message + '/exam/finish')
        }
        _this.formLoading = false
      }).catch(e => {
        _this.formLoading = false
      })
    }
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', { doCompletedTag: state => state.exam.question.answer.doCompletedTag }),
    ...mapState('user', { memberToken: state => state.memberToken })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center;
  }
  .exam-question-item {
    padding: 10px;
    .el-form-item__label {
      font-size: 15px !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
  .footer {
    position: absolute;
    position: fixed;
    z-index: 1000;
    bottom: 0px;
  }
</style>
src/views/exam/paper/doPuzzle.vue
New file
@@ -0,0 +1,285 @@
<template>
  <div>
    <el-container  class="app-item-contain">
      <el-header class="align-center" v-if="guide">
        <h1 v-html="titleItems[0].name+ ' 指导语' "></h1>
      </el-header>
      <el-header class="align-center" style="height: 80px; padding: 2%" v-else>
        <h1>{{form.name}}</h1>
        <div>
          <span class="question-title-padding">试卷总分:{{form.score}}</span>
          <span class="question-title-padding">考试时间:{{form.suggestTime}}分钟</span>
        </div>
      </el-header>
      <el-main>
        <div v-html="titleItems[0].guide" v-if="guide"></div>
        <el-row class="do-align-center" v-if="guide">
          <el-button @click="startDo" type="primary">开始</el-button>
        </el-row >
        <el-form :model="form" ref="form" v-loading="formLoading" label-width="100%" v-else>
          <el-row :key="index"  v-for="(titleItem,index) in titleItems">
            <h3>第1段:{{titleItem.name}}</h3>
            <div class="q-content">{{titleItem.description}}</div>
            <el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
              <el-form-item :key="itemOrder" :label="itemOrder+'.'" class="exam-question-item" label-width="50px">
                <QuestionEdit :qType="questionItem.questionType" :question="questionItem"
                              :answer="answer.answerItems[questionItem.itemOrder-1]"/>
              </el-form-item>
            </el-card>
          </el-row>
          <el-row class="do-align-center" v-if="order < problemLength">
            <el-button @click="preSubject" type="warning" round>上一题</el-button>
            <el-button @click="nextSubject" type="primary" round>下一题</el-button>
          </el-row>
          <el-row class="do-align-center" v-else>
            <el-button @click="preSubject" type="warning" round>上一题</el-button>
            <el-button @click="submitForm" type="primary" :disabled="submitDisable" round>提交</el-button>
          </el-row>
        </el-form>
      </el-main>
    </el-container>
    <el-row class="do-exam-title footer">
      <el-col :span="24">
        <el-popover placement="top" title="题目列表" width="200" trigger="click" ref="popovertag">
          <el-tag type="success" slot="reference">题目</el-tag>
          <span :key="item.itemOrder" v-for="(item) in answer.answerItems">
             <el-tag :type="questionCompleted(item.completed)" class="do-exam-title-tag"
                     @click="goAnchor('#question-'+item.itemOrder)">{{item.itemOrder}}</el-tag>
        </span>
        </el-popover>
        <span class="do-exam-time">
          <label>剩余时间:</label>
          <label>{{formatSeconds(remainTime)}}</label>
        </span>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import { formatSeconds } from '@/utils'
import QuestionEdit from '../components/QuestionEdit'
import examPaperApi from '@/api/examPaper'
import examPaperAnswerApi from '@/api/examPaperAnswer'
export default {
  components: { QuestionEdit },
  data () {
    return {
      form: {},
      formLoading: false,
      answer: {
        questionId: null,
        doTime: 0,
        answerItems: [],
        verifyStatus: 2
      },
      timer: null,
      problemLength: 0,
      titleItemsLength: 0,
      remainTime: 0,
      titleItemName: '', // 考试第二标题
      itemOrder: '', // 题目的序号
      questionItem: {}, // 题目
      titleItems: [],
      order: 0,
      guide: false,
      answered: [],
      submitDisable: false,
      doTime: 0
    }
  },
  created () {
    let _this = this
    _this.form = this.$route.query.formDo
    if (_this.form !== {} && _this.form !== undefined) {
      _this.formLoading = true
      _this.remainTime = _this.form.suggestTime * 60
      _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
      _this.titleItemsLength = _this.form.titleItems.length
      _this.titleItems.push(_this.form.titleItems[0])
      _this.timeReduce()
      _this.initAnswer()
      _this.formLoading = false
    } else {
      _this.formLoading = true
      examPaperApi.select(_this.memberToken).then(re => {
        _this.form = re.response
        _this.remainTime = re.response.suggestTime * 60
        _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
         _this.titleItemsLength = _this.form.titleItems.length
        _this.titleItems.push(_this.form.titleItems[0])
      _this.itemOrder = _this.form.titleItems[0].questionItems[_this.order].itemOrder
        _this.questionItem = _this.form.titleItems[0].questionItems[_this.order]
        _this.initAnswer()
        _this.formLoading = false
        examPaperApi.selectTokenId(_this.memberToken).then(re => {
          _this.answered = re.response.examItemOrderAndContentList
          _this.doTime = re.response.doTime
          _this.timeReduce()
          _this.answered.forEach(it => {
            _this.answer.answerItems[parseInt(it.itemOrder) - 1].content = it.content
            _this.answer.answerItems[parseInt(it.itemOrder) - 1].completed = true
          })
        }).catch(e => {
          // _this.$message.error('题目查询异常')
        })
      }).catch(e => {
        _this.$message.info('无测试题目')
      })
    }
  },
  mounted () {
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  methods: {
    formatSeconds (theTime) {
      return formatSeconds(theTime)
    },
    timeReduce () {
      let _this = this
      _this.answer.doTime += _this.doTime
      _this.remainTime -= _this.doTime
      this.timer = setInterval(function () {
        if (_this.remainTime <= 0) {
          _this.remainTime = 0
          ++_this.answer.doTime
        } else {
          ++_this.answer.doTime
          --_this.remainTime
        }
      }, 1000)
    },
    questionCompleted (completed) {
      return this.enumFormat(this.doCompletedTag, completed)
    },
    goAnchor (selector) {
      let _this = this
      let num = parseInt(selector.substr(10)) -1
      this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
      this.questionItem = _this.form.titleItems[0].questionItems[num]
      setTimeout(function () {
        _this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
      }, 200)
      _this.$refs['popovertag'].doClose()
    },
    initAnswer () {
      this.answer.id = this.form.id
      let titleItemArray = this.form.titleItems
      for (let tIndex in titleItemArray) {
        let questionArray = titleItemArray[tIndex].questionItems
        for (let qIndex in questionArray) {
          let question = questionArray[qIndex]
          this.answer.answerItems.push({ questionId: question.id, content: null, contentArray: [], completed: false, itemOrder: question.itemOrder, lastContent: null })
        }
      }
    },
    nextSubject: function () {
      let _this = this
      _this.guide = false
      if (_this.order < _this.problemLength) {
        let num = ++_this.order
        this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
        this.questionItem = _this.form.titleItems[0].questionItems[num]
      }
      examPaperAnswerApi.answerSubmit(this.answer, _this.memberToken).then(re => {
        if (re.code === 1) {
        } else {
          _this.$message.error(re.message)
        }
      }).catch(e => {
        _this.$message.error('提交问卷后台错误')
      })
    },
    preSubject () {
      let _this = this
      _this.guide = false
      if (_this.order > 0) {
        let num = --_this.order
        this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
        this.questionItem = _this.form.titleItems[0].questionItems[num]
      }
    },
    submitForm () {
      let _this = this
      _this.submitDisable = true
      window.clearInterval(_this.timer)
      _this.formLoading = true
      let unFinish = false
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (_this.answer.answerItems[i].content === null) {
          unFinish = true
        }
      }
      if (unFinish) {
        _this.$confirm('你有未答完的题目,是否提交保存?', {
          title: '提示',
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          showClose: false
        }).then(async () => {
          _this.answer.verifyStatus = 2
          _this.submitAnswer('数据已经成功的上传至服务器,并已经保存好。谢谢。')
        }).catch(async () => {
          _this.formLoading = false
          _this.submitDisable = false
        })
      } else {
        _this.answer.verifyStatus = 1
        _this.submitAnswer('您已经完成了工作行为问卷,数据已经成功的上传至服务器,并已经保存好。谢谢。')
      }
    },
    startDo: function () {
      let _this = this
      _this.guide = false
    },
    submitAnswer: function (mess) {
      let _this = this
      examPaperAnswerApi.answerSubmit(this.answer, _this.memberToken).then(re => {
        if (re.code === 1) {
          _this.$router.push({ path: '/exam/finish', query: { name: _this.form.name, content: mess } })
        } else {
          _this.$message.error(re.message)
        }
        _this.formLoading = false
      }).catch(e => {
        _this.formLoading = false
      })
    }
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', { doCompletedTag: state => state.exam.question.answer.doCompletedTag }),
    ...mapState('user', { memberToken: state => state.memberToken })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center;
  }
  .exam-question-item {
    padding: 10px;
    .el-form-item__label {
      font-size: 15px !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
  .footer {
    position: absolute;
    position: fixed;
    z-index: 1000;
    bottom: 0px;
  }
</style>
src/views/exam/paper/exception.vue
New file
@@ -0,0 +1,55 @@
<template>
  <div>
    <el-container class="app-item-contain">
      <el-header class="align-center" style="height: 0px; margin: 8%;">
      </el-header>
      <el-main class="align-center">
        <H1 v-html="'id:'+memberId" v-if="memberId!==null"></H1>
        <H1 v-html="content"></H1>
      </el-main>
    </el-container>
  </div>
</template>
<script>
import { mapState } from 'vuex'
export default {
  data () {
    return {
      name: this.$route.query.name,
      content: this.$route.query.content
    }
  },
  created () {
  },
  mounted () {
  },
  beforeDestroy () {
  },
  methods: {
  },
  computed: {
    ...mapState('user', { memberId: state => state.memberId })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center
  }
  .exam-question-item {
    padding: 10px;
    .el-form-item__label {
      font-size: 15px !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
</style>
src/views/exam/paper/finish.vue
New file
@@ -0,0 +1,53 @@
<template>
  <div>
    <el-container class="app-item-contain">
      <el-header class="align-center" style="height: 0px; margin: 8%;">
      </el-header>
      <el-main class="align-center">
        <H1 v-if="content!==null && content.trim()!==''">{{content}}</H1>
        <H1 v-else>{{$t('finishMsg')}}</H1>
      </el-main>
    </el-container>
  </div>
</template>
<script>
export default {
  data () {
    return {
      name: this.$route.query.name,
      content: this.$route.query.content
    }
  },
  created () {
  },
  mounted () {
  },
  beforeDestroy () {
  },
  methods: {
  },
  computed: {
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center
  }
  .exam-question-item {
    padding: 10px;
    .el-form-item__label {
      font-size: 15px !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
</style>
src/views/exam/paper/guide.vue
New file
@@ -0,0 +1,68 @@
<template>
  <div>
    <el-container class="app-item-contain">
      <el-header class="align-center" style="height: 50px;padding: 1%;">
        <H2 v-html="$t('guide')" v-if="formDo.questionTemplateId !== 'job'"></H2>
      </el-header>
      <el-main style="padding-top:0%">
        <div v-html="formDo.guide"></div>
        <el-row class="do-align-center">
          <el-button @click="doExam" type="primary" round>{{$t('startDo')}}</el-button>
        </el-row>
      </el-main>
    </el-container>
  </div>
</template>
<script>
import { mapState } from 'vuex'
import examPaperApi from '@/api/examPaper'
export default {
  data () {
    return {
      formDo: this.$route.query.formDo
    }
  },
  created () {
  },
  mounted () {
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  methods: {
    doExam: function () {
      let _this = this
      examPaperApi.updateTestMemberStatus(_this.memberToken).then(re => {
        _this.$router.push({ path: '/exam/' + _this.doUrl, query: { formDo: _this.$route.query.formDo } })
      }).catch(re => {
        _this.$message.error(re.message + ' 更新状态失败')
      })
    }
  },
  computed: {
    ...mapState('exam', { doUrl: state => state.doUrl }),
    ...mapState('user', { memberToken: state => state.memberToken })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center
  }
  .exam-question-item {
    padding: 10px;
    .el-form-item__label {
      font-size: 15px !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
</style>
src/views/exam/paper/intelligence.vue
New file
@@ -0,0 +1,814 @@
<template>
  <div>
    <el-container  class="app-item-contain">
      <el-header class="align-center" v-if="guide">
        <h1></h1>
      </el-header>
      <el-main style="padding-top: 3%;">
        <div v-html="guideDescirption" v-if="guide" :style="ismobile?'padding-top: 2%;':'padding-top: 2%;margin: 0 auto;width:66%'"></div>
        <el-row class="do-align-center" v-if="guide">
          <el-button @click="startDo" type="primary" :disable="startDoDisable">{{$t('start')}}</el-button>
        </el-row >
        <el-form :model="form" ref="form" v-loading="formLoading" label-width="100%" v-else>
          <el-row :key="index"  v-for="(titleItem,index) in titleItems">
            <div class="q-content">{{titleItem.description}}</div>
            <el-card class="exampaper-item-box" style="border:2px solid #2b90ff;">
              <div class="align-center"><span style="font-size: small; float: left">{{$t('examinee')}}{{memberName}}</span><span style="margin-top: 0px;padding-right: 11%">●{{form.name}}●</span><span style="font-size: small; float: right">{{$t('partRemailTime')}}{{formatSecondsG(remainTime,$t('hour'),$t('min'),$t('second'))}}</span></div>
              <hr style="border : 1px dashed #b3d8ff;" />
              <el-form-item :key="index" label-width="1px" v-for="(questionItem, index) in questionItems">
                <QuestionIntelligenceEdit @callNextSubject="onChoice" :qType="questionItem.questionType" :question="questionItem"
                              :answer="answer.answerItems[questionItem.itemOrder-1]" :qLabel="questionItem.questionNumber" :order = "questionItem.itemOrder-1" :displayVal="displayVal"/>
              </el-form-item>
              <hr style="border : 0.5px dashed #b3d8ff;" />
              <div style="color: red;font-size: smaller">{{$t('pleaseAnswerCarefully')}}</div>
              <el-row class="do-align-center" v-if="order < problemLength">
            <el-button @click="preSubject" type="warning"  v-if ="preDisable" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="nextOrder" type="primary" v-if ="nextDisable" round>{{$t('nextQuestion')}}</el-button>
            <el-button @click="submitClick" type="success" v-if ="submitDisable"  round>{{$t('submission')}}</el-button>
          </el-row>
          <el-row class="do-align-center" v-else>
            <el-button @click="preSubject" type="warning" v-if ="preDisable" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="submitForm" type="success" v-if ="submitDisable"  round>{{$t('submission')}}</el-button>
          </el-row>
            </el-card>
          </el-row>
        </el-form>
      </el-main>
    </el-container>
    <el-row class="do-exam-title footer">
      <el-col :span="24">
        <!--测试的时候打开-->
        <!--<span class="do-exam-time">-->
          <!--<label>{{$t('remainingTime')}}</label>-->
          <!--<label>{{formatSecondsG(remainTime,$t('hour'),$t('min'),$t('second'))}}</label>-->
        <!--</span>-->
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
import { formatSeconds, formatSecondsG, formatDate } from '@/utils'
import QuestionIntelligenceEdit from '../components/QuestionIntelligenceEdit'
import examPaperApi from '@/api/examPaper'
import examPaperAnswerApi from '@/api/examPaperAnswer'
import demographyApi from '@/api/demography'
export default {
  name: 'intelligence',
  components: { QuestionIntelligenceEdit },
  data () {
    return {
      form: {},
      formLoading: false,
      answer: {
        questionId: null,
        doTime: 0,
        answerItems: [],
        verifyStatus: 2
      },
      timer: null,
      problemLength: 0,
      titleItemsLength: 0,
      remainTime: 999999,
      titleItemName: '', // 考试第二标题
      itemOrder: '', // 题目的序号
      questionItem: {}, // 题目
      titleItems: [],
      order: 0,
      guide: false,
      answered: [],
      submitDisable: false,
      doTime: 0,
      preDisable: false,
      nextDisable: true,
      partOrder: 1,
      parts: [],
      guideTitle: '',
      guideDescirption: '',
      isCallTain: '',
      memberName: '',
      startDoDisable: true,
      ismobile: false,
      questionItems: [],
      isNotToTime: false,
      originalTime: 0,
      isSubmiting: false,
      displayVal: 'block',
      isSubmitClick: false,
      timeoutValue: 6
    }
  },
  created: function () {
    let _this = this
    window.addEventListener('beforeunload', e => this.beforeunloadFn(e))
    _this.ismobile = /ipad|iphone|midp|rv:1.2.3.4|ucweb|android|windows ce|windows mobile/.test(navigator.userAgent.toLowerCase())
    // 多语言
    _this.setLangFlag(false)
    _this.form = this.$route.query.formDo
    if (_this.form !== {} && _this.form !== undefined) {
      _this.timeReduce()
      _this.initAnswer()
      this.initCreate()
      _this.showNextGuide(false)
    } else {
      // 防止刷新成了不同的语言
      demographyApi.queryDemographyParamInfo(_this.memberToken).then(
        re => {
          if (re.response[0].langType !== null || re.response[0].langType.trim() !== '') {
            _this.setLangType(re.response[0].langType)
          }
          // 加载题目
          examPaperApi.select(_this.memberToken, _this.langType).then(re => {
            _this.form = re.response
            _this.initAnswer()
            examPaperApi.selectTokenId(_this.memberToken).then(re => {
              _this.answered = re.response.examItemOrderAndContentList
              _this.remainTime = re.response.remainTime
              let intelligenceRemainTime = this.getLocalValue('intelligenceRemainTime')
              if (this.isNotEmpty(intelligenceRemainTime) && intelligenceRemainTime > 0 && intelligenceRemainTime <= _this.remainTime) {
                _this.remainTime = intelligenceRemainTime
              }
              this.initCreate()
              _this.originalTime = _this.remainTime // 中间变量,原来的时间
              _this.isNotToTime = true // 先不记时
              _this.timeReduce()
              if (_this.answered !== null) {
                _this.answered.forEach(it => {
                  _this.answer.answerItems[parseInt(it.itemOrder) - 1].content = it.content
                  _this.answer.answerItems[parseInt(it.itemOrder) - 1].completed = true
                })
              }
              for (let i = 0; i < _this.answer.answerItems.length; i++) {
                if (!_this.answer.answerItems[i].completed || _this.answer.answerItems[i].content === null || _this.answer.answerItems[i].content === '') {
                  _this.questionItem = _this.form.titleItems[0].questionItems[i]
                  _this.partOrder = _this.questionItem.partOrder
                  _this.itemOrder = _this.form.titleItems[0].questionItems[i].itemOrder
                  _this.order = i
                  _this.preDisable = true
                  break
                }
                _this.isNotToTime = false
                if (_this.isForFinish(i)) {
                  _this.setSubmiting(true)
                  _this.$router.push({ path: '/exam/finish', query: { name: '', content: _this.$t('finishMsg') } })
                }
              }
              // 如果上一题不是本段,则不能返回上一段
              if (this.isDiffPartOrder()) {
                _this.isNotToTime = true
                _this.preDisable = false
              }
              if (this.isDiffPartOrder()) {
                _this.showNextGuide(false)
              }
              this.determineWhetherIntelligenceTitle()
            }).catch(e => {
              // _this.$message.error('题目查询异常')
            })
          }).catch(e => {
            _this.$message.info(_this.$t('noquestionMsg'))
          })
        }
      ).catch(e => {
      })
    }
  },
  mounted () {
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  destroyed () {
    window.removeEventListener('beforeunload', e => this.beforeunloadFn(e))
  },
  methods: {
    beforeunloadFn (e) {
      if (!this.isSubmiting) {
        if (this.isLastQuestionOrderNum(this.order)) {
          this.answer.answerItems[this.order].content = ''
          this.answer.answerItems[this.order].completed = false
        }
        this.submitStage()
      }
    },
    autoChoiceTitleAndSwer: function (order) {
      let _this = this
      if (_this.form.titleItems[0].questionItems[order].template === 'intelligence_title') {
        _this.answer.answerItems[order].content = '1'
        _this.answer.answerItems[order].completed = true
      }
    },
    doCanNotOption: function (flag) {
      for (let i = 0; i < this.answer.answerItems.length; i++) {
        this.answer.answerItems[i].readOnly = flag
      }
    },
    determineWhetherIntelligenceTitle: function () {
      let _this = this
      _this.questionItems = []
      if (_this.remainTime === 0) {
        this.doCanNotOption(true)
      }
      while (_this.isNotEmpty(_this.form.titleItems[0].questionItems[_this.order].parentQuestionId)) {
        _this.order--
        // 如果是第一题,上一题不显示了
        if (_this.order === 0) {
          _this.preDisable = false
        }
      }
      if (_this.form.titleItems[0].questionItems[_this.order].template === 'intelligence_title') {
        _this.autoChoiceTitleAndSwer(_this.order)
        // 答题要放进去先
        _this.questionItems.push(_this.form.titleItems[0].questionItems[_this.order])
        let id = _this.form.titleItems[0].questionItems[_this.order].id
        for (let i = _this.order + 1; i < _this.form.titleItems[0].questionItems.length; i++) {
          // 后台已经排好顺序了。
          if (_this.form.titleItems[0].questionItems[i].parentQuestionId !== id.toString()) {
            break
          }
          _this.order = i
          _this.questionItems.push(_this.form.titleItems[0].questionItems[_this.order])
          _this.partOrder = _this.form.titleItems[0].questionItems[_this.order].partOrder
        }
        // 横排还是竖排
        _this.displayVal = 'block'
      } else {
        _this.questionItems.push(_this.form.titleItems[0].questionItems[_this.order])
        _this.partOrder = _this.form.titleItems[0].questionItems[_this.order].partOrder
        _this.displayVal = null
      }
      // 如果是本段的最后一题,显示提交按钮
      if (_this.isLastQuestionOrder()) {
        _this.submitDisable = true
      } else {
        _this.submitDisable = false
      }
    },
    initCreate: function () {
      let _this = this
      _this.formLoading = true
      if (_this.isEmpty(_this.remainTime)) {
        _this.remainTime = _this.form.parts[0].timeOut * 60
      }
      _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
      _this.titleItemsLength = _this.form.titleItems.length
      _this.titleItems.push(_this.form.titleItems[0])
      _this.questionItem = _this.form.titleItems[0].questionItems[0]
      _this.partOrder = _this.questionItem.partOrder
      _this.itemOrder = _this.form.titleItems[0].questionItems[0].itemOrder
      _this.formLoading = false
      _this.memberName = _this.form.memberName
      _this.questionItems.push(_this.form.titleItems[0].questionItems[0])
      this.determineWhetherIntelligenceTitle()
    },
    formatSeconds (theTime) {
      return formatSeconds(theTime)
    },
    formatSecondsG (theTime, hour, min, second) {
      return formatSecondsG(theTime, hour, min, second)
    },
    formatDate (time, formatsrc) {
      return formatDate(time, formatsrc)
    },
    // 进入第二段的时候,把第一段没有答的都答一下。
    setPartDefaulAnswer: function (answerSubmit) {
      let _this = this
      answerSubmit.answerItems.forEach(it => {
        if (_this.partOrder > it.partOrder && it.content === null) {
          it.content = '0'
        }
      })
    },
    submitNextSubject: function (answerSubmit, nowPartOrder) {
      let _this = this
      _this.setSubmiting(true)
      // _this.setPartDefaulAnswer(answerSubmit)
      examPaperAnswerApi.answerSubmit(answerSubmit, _this.memberToken).then(re => {
        _this.formLoading = false // 控制不转圈
        _this.setSubmiting(false)
        if (re.code === 1) {
          // 跳到下一段
          if (!_this.isNull(re.response) && re.response.jump === 'next') {
          }
        } else {
          _this.$message.error(re.message)
        }
        // 展示指导语
        if (_this.isNextShowGuide(nowPartOrder)) {
          // 展示指导语
          _this.showNextGuide(true)
        } else {
          _this.nextQuestionOrder(nowPartOrder)
        }
      }).catch(e => {
        _this.reloadPage(e)
      })
    },
    submitStage: function () {
      let _this = this
      let nowPartOrder = _this.partOrder
      let answerSubmit = _this.getSubmitAnswerValue(nowPartOrder)
      _this.preDisable = false
      _this.questionItems = []
      this.submitNextSubject(answerSubmit, nowPartOrder)
    },
    timeOutNextPart: function () {
      let _this = this
      // 自动跳到下一段
      let questionNum = 0
      while (_this.isLastQuestionOrderNum(questionNum) || _this.form.titleItems[0].questionItems[questionNum].partOrder <= _this.partOrder) {
        if (!_this.isContentNotEmpty(questionNum)) {
          _this.answer.answerItems[questionNum].content = '0'
          _this.answer.answerItems[questionNum].completed = true
        }
        if (_this.isLastQuestionOrderNum(questionNum)) {
          _this.answer.finish = _this.timeoutValue
          _this.submitForm()
          return
        }
        _this.order = questionNum++
      }
      // if (!_this.isContentNotEmpty(_this.order)) {
      //   _this.answer.answerItems[_this.order].content = '0'
      //   _this.answer.answerItems[_this.order].completed = true
      // }
      this.submitStage()
      _this.isNotToTime = true
    },
    setLocalValue: function (key, value) {
      localStorage.setItem(key, value)
    },
    getLocalValue: function (key) {
      return localStorage.getItem(key)
    },
    removeLocalValue: function (key) {
      localStorage.removeItem(key)
    },
    timeReduce: function () {
      let _this = this
      _this.answer.doTime += _this.doTime
      _this.remainTime -= _this.doTime
      this.timer = setInterval(function () {
        if (_this.isNotToTime) {
          _this.remainTime = 0
        } else if (_this.remainTime <= 0) {
          _this.remainTime = 0
          ++_this.answer.doTime
          _this.formLoading = true
          _this.originalTime = 0 // 跳到下一段之后,就不能用后台返回的时间了
          _this.timeOutNextPart()
        } else {
          ++_this.answer.doTime
          --_this.remainTime
        }
        // 测试的时候去掉
        _this.setLocalValue('intelligenceRemainTime', _this.remainTime)
      }, 1000)
    },
    questionCompleted (completed) {
      return this.enumFormat(this.doCompletedTag, completed)
    },
    goAnchor (selector) {
      let _this = this
      let num = parseInt(selector.substr(10)) - 1
      this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
      this.questionItem = _this.form.titleItems[0].questionItems[num]
      _this.order = num
      setTimeout(function () {
        _this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
      }, 200)
      _this.$refs['popovertag'].doClose()
    },
    initAnswer () {
      this.answer.id = this.form.id
      this.answer.questionOrder = this.form.questionOrder
      this.answer.optionOrder = this.form.optionOrder
      let titleItemArray = this.form.titleItems
      for (let tIndex in titleItemArray) {
        let questionArray = titleItemArray[tIndex].questionItems
        for (let qIndex in questionArray) {
          let question = questionArray[qIndex]
          this.answer.answerItems.push({ title: question.title, permanentId: question.permanentId, questionId: question.id, content: null, contentArray: [], completed: false, itemOrder: question.itemOrder, lastContent: null, startTime: question.startTime, endTime: question.endTime, partOrder: question.partOrder, questionReport: {}, readOnly: false })
        }
      }
      // 初始化开始时间,设置第一个时间
      for (let i = 0; i < this.answer.answerItems.length; i++) {
        if (this.answer.answerItems[i].startTime == null || this.answer.answerItems[i].startTime.trim() === '') {
          this.answer.answerItems[i].startTime = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
          break
        }
      }
    },
    isContentNotEmpty: function (num) {
      let _this = this
      return _this.answer.answerItems[num].content !== '' && _this.answer.answerItems[num].content !== null && _this.answer.answerItems[num].content.trim() !== ''
    },
    nextQuestion: function (nowPartOrder) {
      let _this = this
      let num = ++_this.order
      // 跳到没有答的题目的段
      while (_this.isContentNotEmpty(num) && num !== _this.answer.answerItems.length - 1) {
        num = ++_this.order
        this.getEmptySubjct(num)
      }
      this.determineWhetherIntelligenceTitle()
      if (_this.partOrder !== nowPartOrder) {
        _this.isShowGuide(true)
      }
      _this.preDisable = true
      _this.isShowNextQuestionButton(_this)
      _this.answer.answerItems[_this.order].startTime = _this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      // 如果是最后一个并且已经完成了,就提交
      if (num === _this.answer.answerItems.length - 1 && _this.isForFinish(num)) {
        this.submitForm()
      }
    },
    nextQuestionOrder: function (nowPartOrder) {
      if (this.order < this.problemLength) {
        this.nextQuestion(nowPartOrder)
      }
    },
    isNextShowGuide: function (nowPartOrder) {
      let _this = this
      // 本段的每一题都打完要指导语
      let showGuideFlag = true
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (_this.form.titleItems[0].questionItems[i].partOrder === nowPartOrder && !_this.isContentNotEmpty(i)) {
          showGuideFlag = false
        }
      }
      return showGuideFlag
    },
    catThaiSubmit: function (answerSubmit) {
      answerSubmit.isCallTain = 'CallThai'
    },
    getEmptySubjct: function (order) {
      let _this = this
      _this.itemOrder = _this.form.titleItems[0].questionItems[order].itemOrder
      _this.questionItem = _this.form.titleItems[0].questionItems[order]
      _this.partOrder = _this.questionItem.partOrder
      _this.preDisable = false
      _this.order = order
      _this.determineWhetherIntelligenceTitle()
    },
    onChoice () { // 是否显示提交按钮
      if (this.isLastQuestionOrder()) {
        return
      }
      this.isShowNextQuestionButton(this)
    },
    // 下一题
    nextSubject: function (data) {
      let _this = this
      _this.isShowGuide(false)
      let nowPartOrder = _this.partOrder
      // 设置上一题的结束时间
      this.answer.answerItems[data[0]].endTime = _this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      // 跳到还没完成的拿到题的
      let complateOrder = _this.order
      // 停留在这道题
      if (_this.form.titleItems[0].questionItems[_this.order].parentQuestionId !== '') {
        complateOrder++
      }
      for (let i = 0; i < complateOrder; i++) {
        if (!_this.isContentNotEmpty(i)) {
          return
        }
      }
      // 循环没有答的题目
      for (let i = 0; i < _this.order; i++) {
        if (!this.isContentNotEmpty(i)) {
          // 返回之前没做的那道题
          this.getEmptySubjct(i)
          return
        }
        // 如果循环到最后一题就提交
        if (_this.isForFinish(i)) {
          _this.submitForm()
          return
        }
      }
      // 全部答完了就可以提交了,但是如果是最后一题就自己提交
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (!_this.isContentNotEmpty(i)) {
          break
        }
        if (i === _this.answer.answerItems.length - 1 && _this.order !== _this.answer.answerItems.length - 1) {
          _this.submitForm()
          return
        }
      }
      // this.answer.answerItems[_this.order].readOnly = true
      let answerSubmit = _this.getSubmitAnswerValue(nowPartOrder)
      // 已经答到了最后一题了,不用自动跳到下一题
      if (this.order + 1 > this.problemLength) {
        return
      }
      _this.preDisable = false
      this.submitNextSubject(answerSubmit, nowPartOrder)
    },
    isLastQuestionOrder: function () {
      let _this = this
      return _this.order >= _this.form.titleItems[0].questionItems.length - 1
    },
    isLastQuestionOrderNum: function (num) {
      let _this = this
      return num >= _this.form.titleItems[0].questionItems.length - 1
    },
    isSamePartOrder: function () {
      return this.form.titleItems[0].questionItems[this.order].partOrder === this.form.titleItems[0].questionItems[this.order + 1].partOrder
    },
    isPreSamePartOrder: function () {
      if (this.isEmpty(this.form.titleItems[0].questionItems[this.order - 1])) {
        return false
      }
      return this.form.titleItems[0].questionItems[this.order].partOrder === this.form.titleItems[0].questionItems[this.order - 1].partOrder
    },
    isShowNextQuestionButton: function (_this) {
      if (_this.isLastQuestionOrder()) {
        _this.nextDisable = false
        return
      }
      _this.nextDisable = true
    },
    isFinishThisPart () {
      for (let i = 0; i <= this.order; i++) {
        if (!this.isContentNotEmpty(i)) {
          return false
        }
      }
      return true
    },
    nextOrder () {
      let _this = this
      _this.order++
      // 如果是已经完成本段,提交展示指导语
      if (_this.isPreSamePartOrder(this)) {
        _this.preDisable = true
      } else {
        _this.preDisable = false
      }
      _this.determineWhetherIntelligenceTitle()
      // 展示指导语
      if (_this.isDiffPartOrder()) {
        this.showGuideDetail()
        this.submitStage() // 跳到下一段的时候,关闭浏览器有保存不上的现象,所以这里中途保存一下
      }
      _this.isShowNextQuestionButton(_this)
    },
    getSubmitAnswerValue (nowPartOrder) {
      let _this = this
      let answerSubmit = JSON.parse(JSON.stringify(_this.answer))
      answerSubmit.answerItems.forEach(answerItem => {
        // 题目报表,如果没有结束时间,开始时间也不需要
        if (answerItem.endTime == null || answerItem.endTime === '') {
          answerItem.startTime = ''
        }
        answerItem.questionReport = { questionOrder: answerItem.questionId, questionTitle: answerItem.title, permanentId: answerItem.permanentId, startTime: answerItem.startTime, endTime: answerItem.endTime, langType: _this.langType, partOrder: answerItem.partOrder, answer: answerItem.content }
      })
      answerSubmit.langType = _this.langType
      answerSubmit.isCallTain = _this.isCallTain
      answerSubmit.partOrder = nowPartOrder
      answerSubmit.itemOrder = _this.itemOrder
      answerSubmit.remainTime = _this.remainTime
      answerSubmit.submitClick = _this.isSubmitClick
      return answerSubmit
    },
    preSubject () {
      let _this = this
      _this.isShowGuide(false)
      if (_this.order <= 0) {
        _this.preDisable = false
        return
      }
      --this.order
      // 循环一直找到上一题
      if (_this.isNotEmpty(_this.form.titleItems[0].questionItems[_this.order].parentQuestionId)) {
        while (_this.isNotEmpty(_this.form.titleItems[0].questionItems[_this.order].parentQuestionId) && _this.form.titleItems[0].questionItems[_this.order].template !== 'intelligence_title') {
          --this.order
        }
        --this.order
        // 回到上一题的上一题
        while (_this.isNotEmpty(_this.form.titleItems[0].questionItems[_this.order].parentQuestionId) && _this.form.titleItems[0].questionItems[_this.order].template !== 'intelligence_title') {
          --this.order
        }
      }
      // 控制回到上一题的按钮的出现
      if (_this.order > 0 && _this.form.titleItems[0].questionItems[_this.order].partOrder === _this.form.titleItems[0].questionItems[_this.order - 1].partOrder) {
        _this.preDisable = true
      } else {
        _this.preDisable = false
      }
      _this.determineWhetherIntelligenceTitle()
      _this.isShowNextQuestionButton(_this)
    },
    isNotEmpty (obj) {
      return obj !== null && obj !== undefined && obj !== ''
    },
    isEmpty (obj) {
      return !this.isNotEmpty(obj)
    },
    submitClick () {
      let _this = this
      _this.isSubmitClick = true
      _this.$confirm(_this.$t('unfinish'), {
        title: _this.$t('prompt'),
        confirmButtonText: _this.$t('determine'),
        cancelButtonText: _this.$t('cancel'),
        showClose: false
      }).then(async () => {
        _this.submitForm()
      }).catch(async () => {
      })
    },
    submitForm () {
      let _this = this
      _this.submitDisable = false
      window.clearInterval(_this.timer)
      _this.formLoading = true
      if (_this.answer.finish === _this.timeoutValue) {
        _this.answer.verifyStatus = 1
        _this.submitAnswer(_this.$t('timeOutSubmit'))
      } else {
        _this.answer.verifyStatus = 1
        _this.answer.finish = 1
        for (let i = 0; i < _this.answer.answerItems.length; i++) {
          if (_this.answer.answerItems[i].content === null) {
            _this.answer.answerItems[i].content = '0'
          }
        }
        _this.submitAnswer(_this.$t('finishIntelligenceInfo'))
      }
    },
    startDo: function () {
      let _this = this
      _this.guide = false
      _this.isCallTain = ''
      _this.isNotToTime = false
      // _this.originalTime 跳到下一段之后会返回0的
      _this.remainTime = _this.originalTime !== 0 ? _this.originalTime : _this.form.parts[_this.partOrder - 1].timeOut * 60
      this.doCanNotOption(false)
    },
    isTimeoutFinish: function () { // 超时提交
      let _this = this
      return _this.answer.finish === _this.timeoutValue
    },
    submitAnswer: function (mess) {
      let _this = this
      this.answer.answerItems[_this.order].endTime = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      // 提交之前提交最后一段
      let answerSubmit = _this.getSubmitAnswerValue(_this.partOrder)
      _this.catThaiSubmit(answerSubmit)
      let loading = _this.$loading({
        lock: false,
        text: _this.$t('uploadInfo'),
        spinner: 'el-icon-loading'
      })
      // 超时马上返回
      if (this.isTimeoutFinish()) {
        loading.close()
        _this.$router.push({ path: '/exam/finish', query: { name: _this.form.name, content: mess } })
      }
      _this.setSubmiting(true)
      examPaperAnswerApi.answerSubmit(answerSubmit, _this.memberToken).then(re => {
        this.removeLocalValue('intelligenceRemainTime')
        if (re.code === 1) {
          loading.close()
          if (re.response.signalObject.code === 'fail') {
            _this.$router.push({
              path: '/exam/exception',
              query: { name: _this.form.name, content: _this.$t('submitExceptionInfo') }
            })
            return
          }
          if (this.isTimeoutFinish()) {
            return
          }
          _this.$router.push({ path: '/exam/finish', query: { name: _this.form.name, content: mess } })
        } else {
          _this.$message.error(re.message + '/exam/finish')
        }
        _this.formLoading = false
      }).catch(e => {
        _this.reloadPage(e)
        loading.close()
      })
    },
    setSubmiting (flag) {
      this.isSubmiting = flag
    },
    guideView: function () {
      // 数组的下标要减1
      let _this = this
      if (_this.isNotEmpty(_this.guideDescirption)) {
        _this.isNotToTime = true
        _this.isShowGuide(true)
        _this.preDisable = false
      } else {
        _this.isShowGuide(false)
        _this.preDisable = false
      }
    },
    showGuideDetail: function () {
      let _this = this
      if (_this.form.parts[_this.partOrder - 1].signals.length > 0) {
        _this.guideDescirption = _this.form.parts[_this.partOrder - 1].signals[0].signalDescription
      }
      // 指导语展示
      _this.guideView()
      _this.startDoDisable = false
    },
    showNextGuide: function (nextQuestion) {
      let _this = this
      if (!nextQuestion) {
        this.showGuideDetail()
        return
      }
      _this.isShowGuide(true)
      _this.nextQuestionOrder()
      this.showGuideDetail()
    },
    isNull: function (obj) {
      return obj === null || obj === undefined
    },
    isShowGuide (flag) {
      let _this = this
      _this.guide = flag
    },
    reloadPage (e) {
      let _this = this
      let mes = ''
      if (e.message !== null && e.message !== undefined && e.message.indexOf('timeout') !== -1) {
        mes = _this.$t('submitTimeout') + ',' + 'order:' + _this.order + ',' + e.message// 提交后台超时
      } else {
        mes = JSON.stringify(e) + ',' + 'order:' + _this.order + ',' + e.message
      }
      // 调试用
      // _this.$router.push({ path: '/exam/exception', query: { name: _this.form.name, content: _this.$t('submitExceptionInfo') + '<br>' + mes } })
      _this.$router.push({ path: '/exam/exception', query: { name: _this.form.name, content: _this.$t('submitExceptionInfo') } })
      _this.formLoading = false
    },
    isForFinish (i) {
      return i === this.answer.answerItems.length - 1 && (this.answer.answerItems[i].content !== null && this.answer.answerItems[i].content !== '')
    },
    isDiffPartOrder: function () {
      let _this = this
      return _this.form.titleItems[0].questionItems[_this.order].partOrder !== _this.form.titleItems[0].questionItems[_this.order - 1].partOrder
    },
    isLastPartOrder: function () {
      let _this = this
      // 最后一题和第一题不显示提交按钮
      if (_this.isEmpty(_this.form.titleItems[0].questionItems[_this.order + 1]) || _this.form.titleItems[0].questionItems[_this.order].partOrder === 0) {
        return false
      }
      return _this.form.titleItems[0].questionItems[_this.order].partOrder !== _this.form.titleItems[0].questionItems[_this.order + 1].partOrder
    },
    ...mapMutations('user', ['setLangType']),
    ...mapMutations('user', ['setLangFlag'])
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', { doCompletedTag: state => state.exam.question.answer.doCompletedTag }),
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('user', { langType: state => state.langType }),
    ...mapState('user', { langFlag: state => state.langFlag }),
    ...mapState('exam', { doUrl: state => state.doUrl })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center;
  }
  .exam-question-item {
    padding: 10px;
    font-size: large;
    .el-form-item__label {
      font-size: large !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
  .footer {
    position: absolute;
    position: fixed;
    z-index: 1000;
    bottom: 0px;
  }
</style>
src/views/exam/paper/job.vue
New file
@@ -0,0 +1,431 @@
<template>
  <div>
    <el-container  class="app-item-contain">
      <el-header class="align-center" v-if="guide">
        <h1 v-html="titleItems[0].name+ +' '+ $t('guide') "></h1>
      </el-header>
      <el-header class="align-center" style="height: 90px; padding: 2%" v-else>
        <h1>{{form.name}}</h1>
        <div>
          <span class="question-title-padding" v-if="form.suggestTime !== null && form.suggestTime != '' && form.suggestTime > 0">{{$t('recommendedTime')}}{{form.suggestTime}}{{$t('minute')}}</span>
        </div>
      </el-header>
      <el-main>
        <div v-html="titleItems[0].guide" v-if="guide"></div>
        <el-row class="do-align-center" v-if="guide">
          <el-button @click="startDo" type="primary">{{$t('start')}}</el-button>
        </el-row>
        <el-form :model="form" ref="form" v-loading="formLoading" label-width="100%" v-else>
          <el-row :key="index"  v-for="(titleItem,index) in titleItems">
            <h3 v-bind:class="{langKaiStyle : isLangKaiStyle}" v-if="langType === 'Chinese'">第{{order+1}}题/总{{totalTitleNum}}题: {{titleItem.name}}</h3>
            <h3 v-bind:class="{langKaiStyle : isLangKaiStyle}" v-else>{{order+1}}/{{totalTitleNum}}:&nbsp;&nbsp;{{titleItem.name}}</h3>
            <div class="q-content" v-html="titleItem.description"></div>
            <el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
              <el-form-item :key="questionItem.itemOrder" class="exam-question-item" label-width="0%"
                            v-for="questionItem in titleItem.questionItems" :id="'question-'+ questionItem.itemOrder">
                <QuestionJobEdit @callNextSubject="callNext" :qType="questionItem.questionType" :question="questionItem"
                              :answer="answer.answerItems[questionItem.itemOrder-1]"  :isReadOnly="autoNext"/>
              </el-form-item>
            </el-card>
          </el-row>
          <el-row class="do-align-center" v-if="order < form.titleItems.length - 1">
            <el-button @click="preSubject" type="warning" v-if ="preDisable" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="nextSubject" type="primary" v-if ="nextDisable" round>{{$t('nextQuestion')}}</el-button>
          </el-row>
          <el-row class="do-align-center" v-else>
            <el-button @click="preSubject" type="warning" v-if ="preDisable" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="submitForm" type="primary" :disabled="submitDisable" round>{{$t('submission')}}</el-button>
          </el-row>
        </el-form>
      </el-main>
    </el-container>
    <el-row class="do-exam-title footer">
      <el-col :span="24">
        <!--<el-popover placement="top" title="题目列表" trigger="click" ref="popovertag">-->
          <!--<el-tag type="success" slot="reference">题目</el-tag>-->
          <!--<span :key="item.itemOrder" v-for="(item) in answer.titleItems">-->
             <!--<el-tag :type="questionCompleted(item.completed)" class="do-exam-title-tag"-->
                     <!--@click="goAnchor(item.itemOrder)">{{item.itemOrder + 1}}</el-tag>-->
        <!--</span>-->
        <!--</el-popover>-->
        <span class="do-exam-time">
          <label v-if="form.suggestTime !== null && form.suggestTime != '' && form.suggestTime > 0">{{$t('remainingTime')}}</label>
          <label v-if="form.suggestTime !== null && form.suggestTime != '' && form.suggestTime > 0">{{formatSecondsG(remainTime,$t('hour'),$t('min'),$t('second'))}}</label>
        </span>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
import { formatSeconds, formatSecondsG, formatDate } from '@/utils'
import QuestionJobEdit from '../components/QuestionJobEdit'
import examPaperApi from '@/api/examPaper'
import examPaperAnswerApi from '@/api/examPaperAnswer'
export default {
  name: 'job',
  components: { QuestionJobEdit },
  data () {
    return {
      form: {},
      formLoading: false,
      answer: {
        questionId: null,
        doTime: 0,
        titleItems: null,
        answerItems: [],
        verifyStatus: 2,
        finish: 0,
        questionOrder: []
      },
      timer: null,
      problemLength: 0,
      remainTime: 0,
      titleItemName: '', // 考试第二标题
      itemOrder: '', // 题目的序号
      questionItem: {}, // 题目
      titleItems: [],
      order: 0,
      guide: false,
      answered: [],
      submitDisable: false,
      doTime: 0,
      preDisable: false,
      nextDisable: false,
      autoNext: true,
      isCallTain: false, // 是否要调用泰国团队接口
      isLangKaiStyle: false,
      totalTitleNum: 0
    }
  },
  created () {
    let _this = this
    // 多语言
    _this.setLangFlag(false)
    _this.form = this.$route.query.formDo
    if (_this.form !== {} && _this.form !== undefined) {
      _this.formLoading = true
      _this.remainTime = _this.form.suggestTime * 60
      _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
      _this.titleItems.push(_this.form.titleItems[0])
      _this.timeReduce()
      _this.initAnswer()
      _this.formLoading = false
      // 总题目
      _this.totalTitleNum = _this.form.titleItems.length
    } else {
      _this.formLoading = true
      examPaperApi.select(_this.memberToken, _this.langType).then(re => {
        _this.form = re.response
        // 总题目
        _this.totalTitleNum = _this.form.titleItems.length
        _this.remainTime = re.response.suggestTime * 60
        _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
        _this.titleItems.push(_this.form.titleItems[0])
        _this.initAnswer()
        _this.formLoading = false
        examPaperApi.selectTokenId(_this.memberToken).then(re => {
          _this.answered = re.response.examItemOrderAndContentList
          _this.doTime = re.response.doTime
          _this.timeReduce()
          _this.answered.forEach(it => {
            _this.answer.answerItems[parseInt(it.itemOrder) - 1].content = it.content
            _this.answer.answerItems[parseInt(it.itemOrder) - 1].completed = true
          })
          // 回到未答的题目
          let notContinue = false
          for (let i = 0; i < _this.answer.answerItems.length; i++) {
            // 判断是否勾选
            if (!_this.answer.answerItems[i].completed) {
              // 定位到未勾选的地方
              for (let j = 0; j < _this.form.titleItems.length; j++) {
                _this.form.titleItems[j].questionItems.forEach(it => {
                  if (it.itemOrder === _this.answer.answerItems[i].itemOrder) {
                    _this.goHere(j)
                    notContinue = true
                  }
                })
              }
            }
            if (notContinue) {
              _this.preDisable = true
              _this.autoNext = true
              break
            }
          }
        }).catch(e => {
        })
      }).catch(e => {
        _this.$message.info(_this.$t('noquestionMsg'))
      })
    }
    // 中文的样式不一样
    if (_this.langType === 'Chinese') {
      _this.isLangKaiStyle = true
    }
  },
  mounted () {
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  methods: {
    formatSeconds (theTime) {
      return formatSeconds(theTime)
    },
    formatSecondsG (theTime, hour, min, second) {
      return formatSecondsG(theTime, hour, min, second)
    },
    formatDate (time, formatsrc) {
      return formatDate(time, formatsrc)
    },
    timeReduce () {
      let _this = this
      _this.answer.doTime += _this.doTime
      _this.remainTime -= _this.doTime
      this.timer = setInterval(function () {
        if (_this.remainTime <= 0) {
          _this.remainTime = 0
          ++_this.answer.doTime
        } else {
          ++_this.answer.doTime
          --_this.remainTime
        }
      }, 1000)
    },
    questionCompleted (completed) {
      return this.enumFormat(this.doCompletedTag, completed)
    },
    goAnchor (selector) {
      let _this = this
      let num = selector
      _this.goHere(num)
      _this.$refs['popovertag'].doClose()
    },
    initAnswer () {
      this.answer.id = this.form.id
      this.answer.questionOrder = this.form.questionOrder
      let titleItemArray = this.form.titleItems
      this.answer.titleItems = this.form.titleItems
      for (let tIndex in titleItemArray) {
        let questionArray = titleItemArray[tIndex].questionItems
        // titleItem 是一段一段的题目
        for (let qIndex in questionArray) {
          let question = questionArray[qIndex]
          this.answer.answerItems.push({ questionId: question.id, content: null, contentArray: [], permanentId: question.permanentId, completed: false, itemOrder: question.itemOrder, lastContent: null, questionReport: {}, readOnly: false })
        }
      }
      // 设置第一题的开始时间
      for (let i = 0; i < this.form.titleItems.length; i++) {
        if (this.form.titleItems[i].startTime == null || this.form.titleItems[i].startTime.trim() === '') {
          this.form.titleItems[i].startTime = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
          break
        }
      }
    },
    nextSubject: function () {
      let _this = this
      // 设置上一题结束时间
      _this.form.titleItems[_this.order].endTime = _this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      _this.isShowGuide(false)
      if (_this.order < _this.form.titleItems.length - 1) {
        _this.titleItems = []
        let num = ++_this.order
        _this.titleItems.push(_this.form.titleItems[num])
        // 设置下一题的开始时间
        _this.form.titleItems[_this.order].startTime = _this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      } else {
        return
      }
      let answerSubmit = _this.getSubmitAnswerValue()
      examPaperAnswerApi.answerSubmit(answerSubmit, _this.memberToken).then(re => {
        if (re.code === 1) {
        } else {
          _this.$message.error(re.message + 'answerSubmit')
        }
      }).catch(e => {
        _this.$message.error(_this.$t('submitErro'))
      })
      _this.preDisable = true
      _this.autoNext = true
      _this.nextDisable = false
    },
    preSubject () {
      let _this = this
      _this.isShowGuide(false)
      if (_this.order > 0) {
        _this.titleItems = []
        let num = --_this.order
        _this.titleItems.push(_this.form.titleItems[num])
        _this.titleItems.forEach(it => {
          it.questionItems.forEach(i => {
            _this.answer.answerItems[parseInt(i.itemOrder) - 1].lastContent = _this.answer.answerItems[parseInt(i.itemOrder) - 1].content
            _this.answer.answerItems[parseInt(i.itemOrder) - 1].readOnly = false
          })
        })
        _this.preDisable = false
        _this.autoNext = false
        _this.nextDisable = true
      }
    },
    setReadOnly: function (readOnly) {
      this.titleItems.forEach(it => {
        it.questionItems.forEach(i => {
          this.answer.answerItems[parseInt(i.itemOrder) - 1].readOnly = readOnly
        })
      })
    },
    // 如果是答完题目了,自动跳到下一题
    callNext () {
      let _this = this
      if (!_this.autoNext) {
        _this.setReadOnly(false)
        return
      }
      let jumpNextFlag = true
      for (let i = 0; i < _this.titleItems.length; i++) {
        _this.titleItems[i].questionItems.forEach(questionItme => {
          if (!_this.answer.answerItems[questionItme.itemOrder - 1].completed) {
            jumpNextFlag = false
          }
        })
      }
      if (jumpNextFlag) {
        _this.nextSubject()
      }
    },
    submitForm () {
      let _this = this
      _this.submitDisable = true
      window.clearInterval(_this.timer)
      _this.formLoading = true
      let unFinish = false
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (_this.answer.answerItems[i].content === null) {
          unFinish = true
          _this.answer.finish = 0
        }
      }
      if (unFinish) {
        _this.$confirm(_this.$t('unfinish'), {
          title: this.$t('prompt'),
          confirmButtonText: _this.$t('determine'),
          cancelButtonText: _this.$t('cancel'),
          showClose: false
        }).then(async () => {
          _this.answer.verifyStatus = 2
          _this.submitAnswer(_this.$t('unfinishSubmit'))
        }).catch(async () => {
          _this.formLoading = false
          _this.submitDisable = false
        })
      } else {
        _this.answer.verifyStatus = 1
        _this.answer.finish = 1
        _this.submitAnswer(_this.$t('finishSubmit'))
      }
    },
    startDo: function () {
      let _this = this
      _this.isShowGuide(false)
    },
    goHere: function (num) {
      let _this = this
      _this.titleItems = []
      _this.titleItems.push(_this.form.titleItems[num])
      _this.order = num
    },
    submitAnswer: function (mess) {
      let _this = this
      this.answer.answerItems[_this.order].endTime = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      let answerSubmit = _this.getSubmitAnswerValue()
      examPaperAnswerApi.answerSubmit(answerSubmit, _this.memberToken).then(re => {
        if (re.code === 1) {
          _this.$router.push({ path: '/exam/finish', query: { name: _this.form.name, content: mess } })
        } else {
          _this.$message.error(re.message + '/exam/finish')
        }
        _this.formLoading = false
      }).catch(e => {
        _this.formLoading = false
        _this.$router.push({ path: '/exam/exception', query: { name: _this.form.name, content: _this.$t('submitExceptionInfo') } })
      })
    },
    getSubmitAnswerValue () {
      let _this = this
      let answerSubmit = JSON.parse(JSON.stringify(this.answer))
      let tagNum = 0
      answerSubmit.titleItems.forEach(titleItem => {
        // titleItem.name = ''
        titleItem.questionItems.forEach(questionItem => {
          questionItem.title = ''
          questionItem.content = answerSubmit.answerItems[tagNum++].content
        })
        // 这个是一个个小题
        answerSubmit.answerItems.forEach(answerItem => {
        // 题目报表,如果没有结束时间,开始时间也不需要
          if (answerItem.endTime == null || answerItem.endTime === '') {
            answerItem.startTime = ''
          }
          answerItem.questionReport = { questionOrder: answerItem.questionId, questionTitle: answerItem.title, permanentId: answerItem.permanentId, startTime: answerItem.startTime, endTime: answerItem.endTime, langType: _this.langType, partOrder: answerItem.partOrder }
        })
        // 题目报表,如果没有结束时间,开始时间也不需要
        if (titleItem.endTime == null) {
          titleItem.startTime = ''
        }
        titleItem.questionReport = { questionOrder: titleItem.questionOrder, questionTitle: titleItem.name, permanentId: titleItem.permanentId, startTime: titleItem.startTime, endTime: titleItem.endTime, langType: _this.langType, questionItems: titleItem.questionItems }
      })
      answerSubmit.langType = _this.langType
      return answerSubmit
    },
    isShowGuide (flag) {
      let _this = this
      _this.guide = flag
      _this.isCallTain = flag
    },
    ...mapMutations('user', ['setLangType']),
    ...mapMutations('user', ['setLangFlag'])
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', { doCompletedTag: state => state.exam.question.answer.doCompletedTag }),
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('user', { langType: state => state.langType }),
    ...mapState('user', { langFlag: state => state.langFlag })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center;
  }
  .exam-question-item {
    padding: 10px;
    .el-form-item__label {
      font-size: 15px !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
  .footer {
    position: absolute;
    position: fixed;
    z-index: 1000;
    bottom: 0px;
  }
  .langKaiStyle {
    font-size:xx-large;
    font-family: 楷体;
  }
</style>
src/views/exam/paper/personality.vue
New file
@@ -0,0 +1,561 @@
<template>
  <div>
    <el-container  class="app-item-contain">
      <el-header class="align-center" v-if="guide">
        <h1></h1>
      </el-header>
      <el-main style="padding-top: 3%;">
        <div v-html="guideDescirption" v-if="guide" :style="ismobile?'padding-top: 2%;':'padding-top: 2%;margin: 0 auto;width:66%'"></div>
        <el-row class="do-align-center" v-if="guide">
          <el-button @click="startDo" type="primary" :disable="startDoDisable">{{$t('start')}}</el-button>
        </el-row >
        <el-form :model="form" ref="form" v-loading="formLoading" label-width="100%" v-else>
          <el-row :key="index"  v-for="(titleItem,index) in titleItems">
            <div class="q-content">{{titleItem.description}}</div>
            <el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0"  style="border:2px solid #2b90ff;">
              <div class="align-center"><span style="font-size: small; float: left">{{$t('examinee')}}{{memberName}}</span><span style="margin-top: 0px;padding-right: 11%">●{{form.name}}●</span></div>
              <hr style="border : 1px dashed #b3d8ff;" />
              <el-form-item :key="itemOrder" label-width="1px">
                <QuestionEdit @callNextSubject="nextSubject" :qType="questionItem.questionType" :question="questionItem"
                              :answer="answer.answerItems[questionItem.itemOrder-1]" :qLabel="itemOrder+'/'+itemSize"/>
              </el-form-item>
              <hr style="border : 0.5px dashed #b3d8ff;" />
              <div style="color: red;font-size: smaller">{{$t('pleaseAnswerCarefully')}}</div>
              <el-row class="do-align-center" v-if="order < problemLength">
            <el-button @click="preSubject" type="warning"  v-if ="preDisable" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="nextSubject" type="primary" v-if ="nextDisable" round>{{$t('nextQuestion')}}</el-button>
          </el-row>
          <el-row class="do-align-center" v-else>
            <el-button @click="preSubject" type="warning" v-if ="preDisable" round>{{$t('lastQuestion')}}</el-button>
            <el-button @click="submitForm" type="primary" :disabled="submitDisable" round>{{$t('submission')}}</el-button>
          </el-row>
            </el-card>
          </el-row>
        </el-form>
      </el-main>
    </el-container>
    <el-row class="do-exam-title footer">
      <el-col :span="24">
        <!--<el-popover placement="top" title="题目列表" width="200" trigger="click" ref="popovertag">-->
          <!--<el-tag type="success" slot="reference">题目</el-tag>-->
          <!--<span :key="item.itemOrder" v-for="(item) in answer.answerItems">-->
             <!--<el-tag :type="questionCompleted(item.completed)" class="do-exam-title-tag"-->
                     <!--&gt;{{item.itemOrder}}</el-tag>-->
        <!--</span>-->
        <!--</el-popover>-->
        <span class="do-exam-time">
          <!--<label>{{$t('remainingTime')}}</label>-->
          <!--<label>{{formatSecondsG(remainTime,$t('hour'),$t('min'),$t('second'))}}</label>-->
        </span>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
import { formatSeconds, formatSecondsG, formatDate } from '@/utils'
import QuestionEdit from '../components/QuestionEdit'
import examPaperApi from '@/api/examPaper'
import examPaperAnswerApi from '@/api/examPaperAnswer'
import demographyApi from '@/api/demography'
export default {
  name: 'personality',
  components: { QuestionEdit },
  data () {
    return {
      form: {},
      formLoading: false,
      answer: {
        questionId: null,
        doTime: 0,
        answerItems: [],
        verifyStatus: 2
      },
      timer: null,
      problemLength: 0,
      titleItemsLength: 0,
      remainTime: 0,
      titleItemName: '', // 考试第二标题
      itemOrder: '', // 题目的序号
      questionItem: {}, // 题目
      titleItems: [],
      order: 0,
      guide: false,
      answered: [],
      submitDisable: true,
      doTime: 0,
      preDisable: false,
      nextDisable: false,
      partOrder: 0,
      parts: [],
      guideTitle: '',
      guideDescirption: '',
      isCallTain: '',
      memberName: '',
      itemSize: '', // 题目长度
      startDoDisable: true,
      ismobile: false
    }
  },
  created: function () {
    let _this = this
    _this.ismobile = /ipad|iphone|midp|rv:1.2.3.4|ucweb|android|windows ce|windows mobile/.test(navigator.userAgent.toLowerCase())
    // 多语言
    _this.setLangFlag(false)
    _this.form = this.$route.query.formDo
    if (_this.form !== {} && _this.form !== undefined) {
      _this.formLoading = true
      _this.remainTime = _this.form.suggestTime * 60
      _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
      _this.titleItemsLength = _this.form.titleItems.length
      _this.titleItems.push(_this.form.titleItems[0])
      _this.questionItem = _this.form.titleItems[0].questionItems[0]
      _this.partOrder = _this.questionItem.partOrder
      _this.itemOrder = _this.form.titleItems[0].questionItems[0].itemOrder
      _this.timeReduce()
      _this.initAnswer()
      _this.formLoading = false
      _this.memberName = _this.form.memberName
      _this.itemSize = _this.form.titleItems[0].questionItems.length
    } else {
      _this.formLoading = true
      // 防止刷新成了不同的语言
      demographyApi.queryDemographyParamInfo(_this.memberToken).then(
        re => {
          if (re.response[0].langType !== null || re.response[0].langType.trim() !== '') {
            _this.setLangType(re.response[0].langType)
          }
          // 加载题目
          examPaperApi.select(_this.memberToken, _this.langType).then(re => {
            _this.form = re.response
            _this.remainTime = re.response.suggestTime * 60
            _this.problemLength = _this.form.titleItems[0].questionItems.length - 1
            _this.titleItemsLength = _this.form.titleItems.length
            _this.titleItems.push(_this.form.titleItems[0])
            _this.itemOrder = _this.form.titleItems[0].questionItems[_this.order].itemOrder
            _this.itemSize = _this.form.titleItems[0].questionItems.length
            _this.questionItem = _this.form.titleItems[0].questionItems[_this.order]
            _this.partOrder = _this.questionItem.partOrder
            _this.initAnswer()
            _this.formLoading = false
            _this.memberName = _this.form.memberName
            examPaperApi.selectTokenId(_this.memberToken).then(re => {
              _this.answered = re.response.examItemOrderAndContentList
              _this.doTime = re.response.doTime
              _this.timeReduce()
              if (_this.answered !== null) {
                _this.answered.forEach(it => {
                  _this.answer.answerItems[parseInt(it.itemOrder) - 1].content = it.content
                  _this.answer.answerItems[parseInt(it.itemOrder) - 1].completed = true
                })
              }
              for (let i = 0; i < _this.answer.answerItems.length; i++) {
                if (!_this.answer.answerItems[i].completed || _this.answer.answerItems[i].content === null || _this.answer.answerItems[i].content === '') {
                  _this.questionItem = _this.form.titleItems[0].questionItems[i]
                  _this.partOrder = _this.questionItem.partOrder
                  _this.itemOrder = _this.form.titleItems[0].questionItems[i].itemOrder
                  _this.order = i
                  _this.preDisable = true
                  break
                }
                if (_this.isForFinish(i)) {
                  _this.$router.push({ path: '/exam/finish', query: { name: '', content: _this.$t('finishMsg') } })
                }
              }
              // 如果上一题不是本段,则不能返回上一段
              if (this.isDiffPartOrder()) {
                _this.preDisable = false
              }
              if (this.isDiffPartOrder()) {
                _this.showNextGuide(re.response.signalObject, false)
              }
            }).catch(e => {
              // _this.$message.error('题目查询异常')
            })
          }).catch(e => {
            _this.$message.info(_this.$t('noquestionMsg'))
          })
        }
      ).catch(e => {
      })
    }
  },
  mounted () {
  },
  beforeDestroy () {
    window.clearInterval(this.timer)
  },
  methods: {
    formatSeconds (theTime) {
      return formatSeconds(theTime)
    },
    formatSecondsG (theTime, hour, min, second) {
      return formatSecondsG(theTime, hour, min, second)
    },
    formatDate (time, formatsrc) {
      return formatDate(time, formatsrc)
    },
    timeReduce: function () {
      let _this = this
      _this.answer.doTime += _this.doTime
      this.timer = setInterval(function () {
        if (_this.answer.doTime <= 0) {
          _this.answer.doTime = 0
        }
        ++_this.answer.doTime
      }, 1000)
    },
    questionCompleted (completed) {
      return this.enumFormat(this.doCompletedTag, completed)
    },
    goAnchor (selector) {
      let _this = this
      let num = parseInt(selector.substr(10)) - 1
      this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
      this.questionItem = _this.form.titleItems[0].questionItems[num]
      _this.order = num
      setTimeout(function () {
        _this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
      }, 200)
      _this.$refs['popovertag'].doClose()
    },
    initAnswer () {
      this.answer.id = this.form.id
      this.answer.questionOrder = this.form.questionOrder
      let titleItemArray = this.form.titleItems
      for (let tIndex in titleItemArray) {
        let questionArray = titleItemArray[tIndex].questionItems
        for (let qIndex in questionArray) {
          let question = questionArray[qIndex]
          this.answer.answerItems.push({ title: question.title, permanentId: question.permanentId, questionId: question.id, content: null, contentArray: [], completed: false, itemOrder: question.itemOrder, lastContent: null, startTime: question.startTime, endTime: question.endTime, partOrder: question.partOrder, questionReport: {}, readOnly: false })
        }
      }
      // 初始化开始时间,设置第一个时间
      for (let i = 0; i < this.answer.answerItems.length; i++) {
        if (this.answer.answerItems[i].startTime == null || this.answer.answerItems[i].startTime.trim() === '') {
          this.answer.answerItems[i].startTime = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
          break
        }
      }
    },
    isContentNotEmpty: function (num) {
      let _this = this
      return _this.answer.answerItems[num].content !== '' && _this.answer.answerItems[num].content !== null && _this.answer.answerItems[num].content.trim() !== ''
    },
    nextQuestion: function (nowPartOrder) {
      let _this = this
      let num = ++_this.order
      _this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
      // 跳到没有答的题目
      while (_this.isContentNotEmpty(num) && num !== _this.answer.answerItems.length - 1) {
        num = ++_this.order
        this.getEmptySubjct(num)
      }
      _this.questionItem = _this.form.titleItems[0].questionItems[num]
      _this.partOrder = _this.questionItem.partOrder
      if (_this.partOrder !== nowPartOrder) {
        _this.isShowGuide(true)
      }
      _this.preDisable = true
      _this.nextDisable = false
      _this.answer.answerItems[_this.order].startTime = _this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      // 如果是最后一个并且已经完成了,就提交
      if (num === _this.answer.answerItems.length - 1 && _this.isForFinish(num)) {
        this.submitForm()
      }
    },
    nextQuestionOrder: function (nowPartOrder) {
      if (this.order < this.problemLength) {
        this.nextQuestion(nowPartOrder)
      } else {
        this.submitDisable = false
      }
    },
    isNextShowGuide: function (nowPartOrder) {
      let _this = this
      // 本段的每一题都打完要指导语
      let showGuideFlag = true
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (_this.form.titleItems[0].questionItems[i].partOrder === nowPartOrder && !_this.isContentNotEmpty(i)) {
          showGuideFlag = false
        }
      }
      return showGuideFlag
    },
    catThaiSubmit: function (answerSubmit) {
      answerSubmit.isCallTain = 'CallThai'
    },
    getEmptySubjct: function (order) {
      let _this = this
      _this.itemOrder = _this.form.titleItems[0].questionItems[order].itemOrder
      _this.questionItem = _this.form.titleItems[0].questionItems[order]
      _this.partOrder = _this.questionItem.partOrder
      _this.preDisable = false
      _this.answer.answerItems[order].readOnly = false
      // _this.answer.answerItems[order].content = ''
      // _this.answer.answerItems[order].completed = false
      _this.order = order
    },
    isNotEmpty (obj) {
      return obj !== null && obj !== undefined && obj !== ''
    },
    isEmpty (obj) {
      return !this.isNotEmpty(obj)
    },
    nextSubject: function () {
      let _this = this
      _this.isShowGuide(false)
      let nowPartOrder = _this.partOrder
      // 设置上一题的结束时间
      this.answer.answerItems[_this.order].endTime = _this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      // 循环没有答的题目,这里同时决定了问答题打完可以进入下一题
      for (let i = 0; i < _this.order; i++) {
        if (!this.isContentNotEmpty(i)) {
          // 返回之前没做的那道题
          this.getEmptySubjct(i)
          return
        }
        // 如果循环到最后一题就提交
        if (_this.isForFinish(i)) {
          _this.submitForm()
          return
        }
      }
      // 全部答完了就可以提交了,但是如果是最后一题就自己提交
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (!_this.isContentNotEmpty(i)) {
          break
        }
        if (i === _this.answer.answerItems.length - 1 && _this.order !== _this.answer.answerItems.length - 1) {
          _this.submitForm()
          return
        }
      }
      this.answer.answerItems[_this.order].readOnly = true
      let answerSubmit = _this.getSubmitAnswerValue(nowPartOrder)
      // 已经答到了最后一题了,不用自动跳到下一题
      if (this.order + 1 > this.problemLength) {
        this.submitDisable = false
        return
      }
      // 如果是最后一题就上面提交了,不会走到这里
      let loading = null
      if (this.isNextShowGuide(nowPartOrder)) {
        this.catThaiSubmit(answerSubmit)
        loading = _this.$loading({
          lock: false,
          text: _this.$t('nextPartInfo'),
          spinner: 'el-icon-loading'
        })
      }
      examPaperAnswerApi.answerSubmit(answerSubmit, _this.memberToken).then(re => {
        if (_this.isNotEmpty(loading)) {
          loading.close()
        }
        if (re.code !== 1) {
          _this.$message.error(re.message)
          throw (re.message)
        }
        // 展示指导语
        if (this.isNextShowGuide(nowPartOrder)) {
          // 展示指导语
          _this.showNextGuide(re.response.signalObject, true)
        } else {
          this.nextQuestionOrder(nowPartOrder)
        }
      }).catch(e => {
        if (_this.isNotEmpty(loading)) {
          loading.close()
        }
        _this.reloadPage(e)
      })
    },
    getSubmitAnswerValue (nowPartOrder) {
      let _this = this
      let answerSubmit = JSON.parse(JSON.stringify(_this.answer))
      answerSubmit.answerItems.forEach(answerItem => {
        // 题目报表,如果没有结束时间,开始时间也不需要
        if (answerItem.endTime == null || answerItem.endTime === '') {
          answerItem.startTime = ''
        }
        answerItem.questionReport = { questionOrder: answerItem.questionId, questionTitle: answerItem.title, permanentId: answerItem.permanentId, startTime: answerItem.startTime, endTime: answerItem.endTime, langType: _this.langType, partOrder: answerItem.partOrder, answer: answerItem.content }
      })
      answerSubmit.langType = _this.langType
      answerSubmit.isCallTain = _this.isCallTain
      answerSubmit.partOrder = nowPartOrder
      answerSubmit.itemOrder = _this.itemOrder
      return answerSubmit
    },
    preSubject () {
      let _this = this
      _this.isShowGuide(false)
      if (_this.order > 0) {
        let num = --_this.order
        _this.itemOrder = _this.form.titleItems[0].questionItems[num].itemOrder
        _this.questionItem = _this.form.titleItems[0].questionItems[num]
        _this.partOrder = _this.questionItem.partOrder
        _this.preDisable = false
        _this.nextDisable = true
        _this.answer.answerItems[_this.order].readOnly = false
      }
    },
    submitForm () {
      let _this = this
      _this.submitDisable = true
      window.clearInterval(_this.timer)
      _this.formLoading = true
      let unFinish = false
      for (let i = 0; i < _this.answer.answerItems.length; i++) {
        if (_this.answer.answerItems[i].content === null) {
          unFinish = true
          _this.answer.finish = 0
        }
      }
      if (unFinish) {
        // 直接停留在没有打完的题目
        for (let i = 0; i < _this.answer.answerItems.length; i++) {
          if (!_this.isContentNotEmpty(i)) {
            _this.questionItem = _this.form.titleItems[0].questionItems[i]
            _this.partOrder = _this.questionItem.partOrder
            _this.itemOrder = _this.form.titleItems[0].questionItems[i].itemOrder
            _this.order = i
            _this.preDisable = true
            break
          }
          _this.formLoading = false
          _this.submitDisable = false
        }
      } else {
        _this.answer.verifyStatus = 1
        _this.answer.finish = 1
        _this.submitAnswer(_this.$t('finishSubmit'))
      }
    },
    startDo: function () {
      let _this = this
      _this.guide = false
      _this.isCallTain = ''
    },
    submitAnswer: function (mess) {
      let _this = this
      this.answer.answerItems[_this.order].endTime = this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')
      // 提交之前提交最后一段
      let answerSubmit = _this.getSubmitAnswerValue(_this.partOrder)
      _this.catThaiSubmit(answerSubmit)
      let loading = _this.$loading({
        lock: false,
        text: _this.$t('uploadInfo'),
        spinner: 'el-icon-loading'
      })
      examPaperAnswerApi.answerSubmit(answerSubmit, _this.memberToken).then(re => {
        loading.close()
        if (re.code === 1) {
          _this.$router.push({ path: '/exam/finish', query: { name: _this.form.name, content: mess } })
        } else {
          _this.$message.error(re.message + '/exam/finish')
        }
        _this.formLoading = false
      }).catch(e => {
        _this.reloadPage(e)
        loading.close()
      })
    },
    showNextGuide: function (signalObject, nextQuestion) {
      let _this = this
      _this.guideDescirption = signalObject.signalDescription
      if (_this.guideDescirption !== null && _this.guideDescirption !== undefined && _this.guideDescirption !== '') {
        _this.isShowGuide(true)
        _this.preDisable = false
      } else {
        throw (signalObject.message)
      }
      if (Object.keys(_this.guideDescirption).length === 0) {
        _this.isShowGuide(false)
        _this.preDisable = false
      }
      _this.startDoDisable = false
      if (!nextQuestion) {
        return
      }
      _this.nextQuestionOrder()
    },
    isNull: function (obj) {
      return obj === null || obj === undefined
    },
    isShowGuide (flag) {
      let _this = this
      _this.guide = flag
      // _this.isCallTain = flag ? 'callTain' : ''
    },
    reloadPage (e) {
      let _this = this
      let mes = ''
      if (e.message !== null && e.message !== undefined && e.message.indexOf('timeout') !== -1) {
        mes = _this.$t('submitTimeout') + ',' + 'order:' + _this.order + ',' + e.message// 提交后台超时
      } else {
        mes = JSON.stringify(e) + ',' + 'order:' + _this.order + ',' + e.message
      }
      // 调试用
      // _this.$router.push({ path: '/exam/exception', query: { name: _this.form.name, content: _this.$t('submitExceptionInfo') + '<br>' + mes } })
      _this.$router.push({ path: '/exam/exception', query: { name: _this.form.name, content: _this.$t('submitExceptionInfo') } })
      _this.formLoading = false
      _this.submitDisable = false
    },
    isForFinish (i) {
      return i === this.answer.answerItems.length - 1 && (this.answer.answerItems[i].content !== null && this.answer.answerItems[i].content !== '')
    },
    isDiffPartOrder: function () {
      let _this = this
      return _this.form.titleItems[0].questionItems[_this.order].partOrder !== _this.form.titleItems[0].questionItems[_this.order - 1].partOrder
    },
    ...mapMutations('user', ['setLangType']),
    ...mapMutations('user', ['setLangFlag'])
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', { doCompletedTag: state => state.exam.question.answer.doCompletedTag }),
    ...mapState('user', { memberToken: state => state.memberToken }),
    ...mapState('user', { langType: state => state.langType }),
    ...mapState('user', { langFlag: state => state.langFlag }),
    ...mapState('exam', { doUrl: state => state.doUrl })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center;
  }
  .exam-question-item {
    padding: 10px;
    font-size: large;
    .el-form-item__label {
      font-size: large !important;
    }
  }
  .question-title-padding {
    padding-left: 25px;
    padding-right: 25px;
  }
  .footer {
    position: absolute;
    position: fixed;
    z-index: 1000;
    bottom: 0px;
  }
</style>
src/views/exam/paper/read.vue
New file
@@ -0,0 +1,111 @@
<template>
<div>
  <el-row  class="do-exam-title" style="background-color: #F5F5DC">
    <el-col :span="24">
        <span :key="item.itemOrder"  v-for="item in answer.answerItems">
             <el-tag :type="questionDoRightTag(item.doRight)" class="do-exam-title-tag" @click="goAnchor('#question-'+item.itemOrder)">{{item.itemOrder}}</el-tag>
        </span>
    </el-col>
  </el-row>
  <el-row  class="do-exam-title-hidden">
    <el-col :span="24">
        <span :key="item.itemOrder"  v-for="item in answer.answerItems">
             <el-tag  class="do-exam-title-tag" >{{item.itemOrder}}</el-tag>
        </span>
    </el-col>
  </el-row>
  <el-container  class="app-item-contain">
    <el-header class="align-center">
      <h1>{{form.name}}</h1>
      <div>
        <span class="question-title-padding">试卷得分:{{answer.score}}</span>
        <span class="question-title-padding">试卷耗时:{{formatSeconds(answer.doTime)}}</span>
      </div>
    </el-header>
    <el-main>
      <el-form :model="form" ref="form" v-loading="formLoading" label-width="100px">
        <el-row :key="index"  v-for="(titleItem,index) in form.titleItems">
          <h3>{{titleItem.name}}</h3>
          <el-card class="exampaper-item-box" v-if="titleItem.questionItems.length!==0">
            <el-form-item :key="questionItem.itemOrder" :label="questionItem.itemOrder+'.'"
                          v-for="questionItem in titleItem.questionItems"
                          class="exam-question-item"  label-width="50px" :id="'question-'+ questionItem.itemOrder">
              <QuestionAnswerShow :qType="questionItem.questionType" :question="questionItem"  :answer="answer.answerItems[questionItem.itemOrder-1]"/>
            </el-form-item>
          </el-card>
        </el-row>
      </el-form>
    </el-main>
  </el-container>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import { formatSeconds } from '@/utils'
import QuestionAnswerShow from '../components/QuestionAnswerShow'
import examPaperAnswerApi from '@/api/examPaperAnswer'
export default {
  components: { QuestionAnswerShow },
  data () {
    return {
      form: {},
      formLoading: false,
      answer: {
        id: null,
        score: 0,
        doTime: 0,
        answerItems: [],
        doRight: false
      }
    }
  },
  created () {
    let id = this.$route.query.id
    let _this = this
    if (id && parseInt(id) !== 0) {
      _this.formLoading = true
      examPaperAnswerApi.read(id).then(re => {
        _this.form = re.response.paper
        _this.answer = re.response.answer
        _this.formLoading = false
      })
    }
  },
  methods: {
    formatSeconds (theTime) {
      return formatSeconds(theTime)
    },
    questionDoRightTag (status) {
      return this.enumFormat(this.doRightTag, status)
    },
    goAnchor (selector) {
      this.$el.querySelector(selector).scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
    }
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', {
      doRightTag: state => state.exam.question.answer.doRightTag
    })
  }
}
</script>
<style lang="scss" scoped>
  .align-center {
    text-align: center
  }
  .exam-question-item{
    padding: 10px;
    .el-form-item__label{
      font-size: 15px !important;
    }
  }
  .question-title-padding{
    padding-left: 25px;
    padding-right: 25px;
  }
</style>
src/views/paper/index.vue
New file
@@ -0,0 +1,90 @@
<template>
  <div style="margin-top: 10px" class="app-contain">
    <el-tabs tab-position="left"  v-model="tabId"  @tab-click="subjectChange" >
      <el-tab-pane :label="item.name"  :key="item.id" :name="item.id" v-for="item in subjectList" style="margin-left: 20px;" >
        <el-row  style="float: right">
          <el-radio-group v-model="queryParam.paperType" size="mini" @change="paperTypeChange" >
            <el-radio v-for="item in paperTypeEnum" size="mini" :key="item.key" :label="item.key">{{item.value}}</el-radio>
          </el-radio-group>
        </el-row>
        <el-table v-loading="listLoading" :data="tableData" fit highlight-current-row style="width: 100%">
          <el-table-column prop="id" label="序号" width="90px"/>
          <el-table-column prop="name" label="名称"  />
          <el-table-column align="right">
            <template slot-scope="{row}">
              <router-link target="_blank" :to="{path:'/exam/do',query:{id:row.id}}">
                <el-button  type="text" size="small">开始答题</el-button>
              </router-link>
            </template>
          </el-table-column>
        </el-table>
        <pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex" :limit.sync="queryParam.pageSize"
                    @pagination="search" style="margin-top: 20px"/>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import { mapState } from 'vuex'
import Pagination from '@/components/Pagination'
import examPaperApi from '@/api/examPaper'
import subjectApi from '@/api/subject'
export default {
  components: { Pagination },
  data () {
    return {
      queryParam: {
        paperType: 1,
        subjectId: 0,
        pageIndex: 1,
        pageSize: 10
      },
      tabId: '',
      listLoading: true,
      subjectList: [],
      tableData: [],
      total: 0
    }
  },
  created () {
    this.initSubject()
  },
  methods: {
    initSubject () {
      let _this = this
      subjectApi.list().then(re => {
        _this.subjectList = re.response
        let subjectId = _this.subjectList[0].id
        _this.queryParam.subjectId = subjectId
        _this.tabId = subjectId.toString()
        _this.search()
      })
    },
    search () {
      this.listLoading = true
      examPaperApi.pageList(this.queryParam).then(data => {
        const re = data.response
        this.tableData = re.list
        this.total = re.total
        this.queryParam.pageIndex = re.pageNum
        this.listLoading = false
      })
    },
    paperTypeChange (val) {
      this.search()
    },
    subjectChange (tab, event) {
      this.queryParam.subjectId = Number(this.tabId)
      this.search()
    }
  },
  computed: {
    ...mapState('enumItem', {
      paperTypeEnum: state => state.exam.examPaper.paperTypeEnum
    })
  }
}
</script>
src/views/question-error/index.vue
New file
@@ -0,0 +1,99 @@
<template>
  <div style="margin-top: 10px" class="app-contain">
    <el-row :gutter="50">
      <el-col :span="14">
        <el-table v-loading="listLoading" :data="tableData" fit highlight-current-row style="width: 100%" @row-click="itemSelect">
          <el-table-column prop="shortTitle" label="题干"  show-overflow-tooltip />
          <el-table-column prop="questionType" label="题型"  :formatter="questionTypeFormatter" width="70" />
          <el-table-column prop="subjectName" label="学科"  width="50" />
          <el-table-column prop="createTime" label="做题时间"  width="170" />
        </el-table>
        <pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex" :limit.sync="queryParam.pageSize"
                    @pagination="search" style="margin-top: 20px"/>
      </el-col>
      <el-col  :span="10" >
        <el-card  class="record-answer-info">
          <el-form>
            <el-form-item>
              <QuestionAnswerShow :qType="selectItem.questionType" :qLoading="qAnswerLoading" :question="selectItem.questionItem"  :answer="selectItem.answerItem"/>
            </el-form-item>
          </el-form>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import Pagination from '@/components/Pagination'
import questionAnswerApi from '@/api/questionAnswer'
import QuestionAnswerShow from '../exam/components/QuestionAnswerShow'
export default {
  components: { Pagination, QuestionAnswerShow },
  data () {
    return {
      queryParam: {
        pageIndex: 1,
        pageSize: 10
      },
      listLoading: false,
      tableData: [],
      total: 0,
      qAnswerLoading: false,
      selectItem: {
        questionType: 0,
        questionItem: null,
        answerItem: null
      }
    }
  },
  created () {
    this.search()
  },
  methods: {
    search () {
      this.listLoading = true
      let _this = this
      questionAnswerApi.pageList(this.queryParam).then(data => {
        const re = data.response
        _this.tableData = re.list
        _this.total = re.total
        _this.queryParam.pageIndex = re.pageNum
        _this.listLoading = false
        if (re.list.length !== 0) {
          _this.qAnswerShow(re.list[0].id)
        }
      })
    },
    itemSelect (row, column, event) {
      this.qAnswerShow(row.id)
    },
    qAnswerShow (id) {
      let _this = this
      this.qAnswerLoading = true
      questionAnswerApi.select(id).then(re => {
        let response = re.response
        _this.selectItem.questionType = response.questionVM.questionType
        _this.selectItem.questionItem = response.questionVM
        _this.selectItem.answerItem = response.questionAnswerVM
        _this.qAnswerLoading = false
      })
    },
    questionTypeFormatter (row, column, cellValue, index) {
      return this.enumFormat(this.questionTypeEnum, cellValue)
    }
  },
  computed: {
    ...mapGetters('enumItem', ['enumFormat']),
    ...mapState('enumItem', {
      questionTypeEnum: state => state.exam.question.typeEnum
    })
  }
}
</script>
<style lang="scss" scoped>
</style>
src/views/record/index.vue
New file
@@ -0,0 +1,125 @@
<template>
  <div style="margin-top: 10px" class="app-contain">
     <el-row :gutter="50">
       <el-col :span="18">
         <el-table v-loading="listLoading" :data="tableData" fit highlight-current-row style="width: 100%" @row-click="itemSelect">
           <el-table-column prop="id" label="序号" width="90px"/>
           <el-table-column prop="paperName" label="名称"  />
           <el-table-column prop="subjectName" label="学科"  width="70" />
           <el-table-column label="状态" prop="status" width="100px">
             <template slot-scope="{row}">
               <el-tag :type="statusTagFormatter(row.status)">
                 {{ statusTextFormatter(row.status) }}
               </el-tag>
             </template>
           </el-table-column>
           <el-table-column prop="createTime" label="做题时间"  width="170" />
           <el-table-column  align="right" width="70">
             <template slot-scope="{row}">
               <router-link target="_blank" :to="{path:'/edit',query:{id:row.id}}" v-if="row.status === 1 ">
                 <el-button  type="text" size="small">批改</el-button>
               </router-link>
               <router-link target="_blank" :to="{path:'/read',query:{id:row.id}}" v-if="row.status === 2 ">
                 <el-button  type="text" size="small">查看试卷</el-button>
               </router-link>
             </template>
           </el-table-column>
         </el-table>
         <pagination v-show="total>0" :total="total" :background="false" :page.sync="queryParam.pageIndex" :limit.sync="queryParam.pageSize"
                     @pagination="search" style="margin-top: 20px"/>
       </el-col>
       <el-col  :span="6" >
         <el-card  class="record-answer-info">
            <el-form label-width="50%" >
              <el-form-item label="系统判分:">
                <span>{{selectItem.systemScore}}</span>
              </el-form-item>
              <el-form-item label="最终得分:">
                <span>{{selectItem.userScore}}</span>
              </el-form-item>
              <el-form-item label="试卷总分:">
                <span>{{selectItem.paperScore}}</span>
              </el-form-item>
              <el-form-item label="正确题数:">
                <span>{{selectItem.questionCorrect}}</span>
              </el-form-item>
              <el-form-item label="总题数:">
                <span>{{selectItem.questionCount}}</span>
              </el-form-item>
              <el-form-item label="用时:">
                <span>{{selectItem.doTime}}</span>
              </el-form-item>
            </el-form>
         </el-card>
       </el-col>
     </el-row>
  </div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import Pagination from '@/components/Pagination'
import examPaperAnswerApi from '@/api/examPaperAnswer'
import { scrollTo } from '@/utils/scroll-to'
export default {
  components: { Pagination },
  data () {
    return {
      queryParam: {
        pageIndex: 1,
        pageSize: 10
      },
      listLoading: false,
      tableData: [],
      total: 0,
      selectItem: {
        systemScore: '0',
        userScore: '0',
        doTime: '0',
        paperScore: '0',
        questionCorrect: 0,
        questionCount: 0
      }
    }
  },
  created () {
    this.search()
    scrollTo(0, 800)
  },
  methods: {
    search () {
      this.listLoading = true
      let _this = this
      examPaperAnswerApi.pageList(this.queryParam).then(data => {
        const re = data.response
        _this.tableData = re.list
        _this.total = re.total
        _this.queryParam.pageIndex = re.pageNum
        _this.listLoading = false
      })
    },
    itemSelect (row, column, event) {
      this.selectItem = row
    },
    statusTagFormatter (status) {
      return this.enumFormat(this.statusTag, status)
    },
    statusTextFormatter (status) {
      return this.enumFormat(this.statusEnum, status)
    }
  },
  computed: {
    ...mapGetters('enumItem', [
      'enumFormat'
    ]),
    ...mapState('enumItem', {
      statusEnum: state => state.exam.examPaperAnswer.statusEnum,
      statusTag: state => state.exam.examPaperAnswer.statusTag
    })
  }
}
</script>
<style lang="scss" scoped>
</style>
vue.config.js
New file
@@ -0,0 +1,55 @@
'use strict'
const path = require('path')
function resolve (dir) {
  return path.join(__dirname, dir)
}
module.exports = {
  publicPath: './',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: true,
  productionSourceMap: false,
  devServer: {
    open: true,
    host: 'localhost',
    port: 82,
    https: false,
    hotOnly: false,
    proxy: {
      '/': {
        target: 'http://127.0.0.1:8090',
        changeOrigin: true
      }
    }
  },
  configureWebpack: {
    devtool: 'source-map'
  },
  pages: {
    index: {
      entry: 'src/main.js',
      template: 'public/index.html',
      filename: 'index.html'
    }
  },
  chainWebpack (config) {
    // set svg-sprite-loader
    config.module
      .rule('svg')
      .exclude.add(resolve('src/icons'))
      .end()
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
  }
}