xiatian %!s(int64=4) %!d(string=hai) anos
pai
achega
08933c882b
Modificáronse 71 ficheiros con 21099 adicións e 0 borrados
  1. 19 0
      src/App.vue
  2. 57 0
      src/components/LevelTypeSelect.vue
  3. 28 0
      src/components/LinkTitlesCustom.vue
  4. 108 0
      src/components/ckeditor.vue
  5. 38 0
      src/components/registerComponents.js
  6. 114 0
      src/constants/constants.js
  7. 1 0
      src/directives/directives.js
  8. 12 0
      src/filters/filters.js
  9. 122 0
      src/global.d.ts
  10. 30 0
      src/main.js
  11. BIN=BIN
      src/modules/portal/assets/images/bg.jpg
  12. 0 0
      src/modules/portal/constants/constants.js
  13. 29 0
      src/modules/portal/routes/routes.js
  14. 14 0
      src/modules/portal/store/currentPaths.js
  15. 12 0
      src/modules/portal/store/menuList.js
  16. 26 0
      src/modules/portal/store/user.js
  17. 3 0
      src/modules/portal/views/Component404.vue
  18. 305 0
      src/modules/portal/views/Login.vue
  19. 436 0
      src/modules/portal/views/home/Home.vue
  20. 175 0
      src/modules/portal/views/home/HomeSide.vue
  21. 28 0
      src/modules/portal/views/home/LinkTitles.vue
  22. BIN=BIN
      src/modules/portal/views/home/icon.png
  23. 203 0
      src/modules/portal/views/home/main/HomeMain.vue
  24. 298 0
      src/modules/portal/views/tips/Tips.vue
  25. BIN=BIN
      src/modules/portal/views/tips/tips.png
  26. 116 0
      src/modules/questions/component/ckeditor.vue
  27. 59 0
      src/modules/questions/component/reduplicate_mark.vue
  28. 36 0
      src/modules/questions/constants/constants.js
  29. 30 0
      src/modules/questions/directives/directives.js
  30. 32 0
      src/modules/questions/filters/filters.js
  31. 155 0
      src/modules/questions/routes/routes.js
  32. 109 0
      src/modules/questions/styles/Common.css
  33. 155 0
      src/modules/questions/styles/EditPaper.css
  34. 390 0
      src/modules/questions/views/BluePaperStructure.vue
  35. 560 0
      src/modules/questions/views/CourseProperty.vue
  36. 537 0
      src/modules/questions/views/EditOtherQuestion.vue
  37. 2062 0
      src/modules/questions/views/EditPaper.vue
  38. 649 0
      src/modules/questions/views/EditSelectQuestion.vue
  39. 809 0
      src/modules/questions/views/ExportStructure.vue
  40. 666 0
      src/modules/questions/views/ExportTemplate.vue
  41. 903 0
      src/modules/questions/views/ExtractPaperInfo.vue
  42. 711 0
      src/modules/questions/views/ExtractPaperRule.vue
  43. 997 0
      src/modules/questions/views/GenPaper.vue
  44. 1327 0
      src/modules/questions/views/GenPaperDetail.vue
  45. 874 0
      src/modules/questions/views/ImportPaper.vue
  46. 315 0
      src/modules/questions/views/ImportPaperInfo.vue
  47. 657 0
      src/modules/questions/views/InsertBluePaperStructure.vue
  48. 829 0
      src/modules/questions/views/InsertBluePaperStructureInfo.vue
  49. 566 0
      src/modules/questions/views/InsertPaperStructure.vue
  50. 1247 0
      src/modules/questions/views/InsertPaperStructureInfo.vue
  51. 375 0
      src/modules/questions/views/InsertPaperTitle.vue
  52. 409 0
      src/modules/questions/views/PaperStructure.vue
  53. 321 0
      src/modules/questions/views/PreviewPaper.vue
  54. 456 0
      src/modules/questions/views/PropertyInfo.vue
  55. 772 0
      src/modules/questions/views/Question.vue
  56. 533 0
      src/modules/questions/views/School.vue
  57. 421 0
      src/modules/questions/views/SelectQuestion.vue
  58. 472 0
      src/plugins/axios.js
  59. 5 0
      src/plugins/element.js
  60. 7 0
      src/plugins/errorMsgConfig.js
  61. 13 0
      src/plugins/helpers.js
  62. 40 0
      src/plugins/networkInformationHint.js
  63. 13 0
      src/plugins/vueAwesome.js
  64. 139 0
      src/plugins/vueLifecylceLogs.js
  65. 55 0
      src/router/index.js
  66. 18 0
      src/store/index.js
  67. 42 0
      src/styles/bootstrap.scss
  68. 18 0
      src/styles/elementuiCustomize.css
  69. 124 0
      src/styles/global.css
  70. 12 0
      src/styles/nprogress.css
  71. 5 0
      src/utils/utils.js

+ 19 - 0
src/App.vue

@@ -0,0 +1,19 @@
+<template>
+  <div id="app"><router-view /></div>
+</template>
+
+<script>
+export default {
+  name: "App",
+};
+</script>
+
+<style scoped>
+#app {
+  font-family: "Avenir", Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  color: #2c3e50;
+  background-color: #ecf0f5;
+}
+</style>

+ 57 - 0
src/components/LevelTypeSelect.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-select
+    v-model="selected"
+    class="size-select"
+    placeholder="请选择"
+    :style="styles"
+    clearable
+    @change="select"
+  >
+    <el-option
+      v-for="item in optionList"
+      :key="item.code"
+      :label="item.name"
+      :value="item.code"
+    >
+      <span>{{ item.name }}</span>
+    </el-option>
+  </el-select>
+</template>
+
+<script>
+import { LEVEL_TYPE_SELECT } from "@/constants/constants";
+export default {
+  name: "LevelTypeSelect",
+  props: {
+    value: {
+      type: String,
+      default: "",
+    },
+    options: { type: Array, default: () => null },
+    styles: { type: String, default: "" },
+  },
+  data() {
+    return {
+      optionList: this.options || LEVEL_TYPE_SELECT,
+      selected: "",
+    };
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler(val) {
+        this.selected = val;
+      },
+    },
+  },
+  async created() {},
+  methods: {
+    select() {
+      this.$emit("input", this.selected);
+      this.$emit("change", this.selected);
+    },
+  },
+};
+</script>
+
+<style></style>

+ 28 - 0
src/components/LinkTitlesCustom.vue

@@ -0,0 +1,28 @@
+<template>
+  <el-breadcrumb
+    v-if="currentPaths.length > 0"
+    separator-class="el-icon-arrow-right"
+    style="
+      border-bottom: 1px solid #ddd;
+      padding-bottom: 5px;
+      margin-top: -20px;
+      margin-bottom: 20px;
+    "
+  >
+    <el-breadcrumb-item :to="{ path: '/home/overview' }">
+      首页
+    </el-breadcrumb-item>
+    <el-breadcrumb-item v-for="item in currentPaths" :key="item">
+      {{ item }}
+    </el-breadcrumb-item>
+  </el-breadcrumb>
+</template>
+
+<script>
+export default {
+  name: "LinkTitlesCustom",
+  props: {
+    currentPaths: { type: Array, default: () => [] },
+  },
+};
+</script>

+ 108 - 0
src/components/ckeditor.vue

@@ -0,0 +1,108 @@
+<template>
+  <div class="ckeditor" style="margin-bottom: 10px">
+    <textarea :id="id" :value="value"></textarea>
+  </div>
+</template>
+
+<script>
+let inc = 0;
+export default {
+  props: {
+    value: {
+      type: String,
+      default: "",
+    },
+    id: {
+      type: String,
+      default: () => `editor-${++inc}`,
+    },
+    height: {
+      type: String,
+      default: "200px",
+    },
+    width: {
+      type: String,
+      default: "980px",
+    },
+    toolbar: {
+      type: [String, Array],
+      default: null,
+    },
+    language: {
+      type: String,
+      default: "zh-cn",
+    },
+    extraplugins: {
+      type: String,
+      default: "",
+    },
+    extrabuttons: {
+      type: String,
+      default: "",
+    },
+    readonly: {
+      type: Boolean,
+    },
+  },
+  computed: {
+    instance() {
+      return window.CKEDITOR.instances[this.id];
+    },
+  },
+  beforeUpdate() {
+    if (this.value !== this.instance.getData()) {
+      this.instance.setData(this.value);
+    }
+  },
+  mounted() {
+    var removePluginStr = "bidi,image";
+    var removeButtonStr =
+      "Styles,Format,ShowBlocks,Iframe,PageBreak,Smiley,Flash,Language,JustifyBlock,JustifyRight,JustifyCenter,JustifyLeft,CreateDiv,CopyFormatting,ImageButton,Button,HiddenField,Select,Textarea,TextField,Radio,Checkbox,Form,BGColor,SelectAll,Replace,Find,Templates,Print,Preview,NewPage,Save,Underline,Subscript,Superscript,HorizontalRule,Unlink,Link,Scayt,Cut,Copy,Paste,PasteText,PasteFromWord,Maximize,Italic,Bold,NumberedList,BulletedList,Indent,Outdent,Blockquote,About,RemoveFormat,Strike";
+    if (!this.extrabuttons || this.extrabuttons.indexOf("Font") == -1) {
+      removeButtonStr = removeButtonStr + ",Font";
+    }
+    if (!this.extrabuttons || this.extrabuttons.indexOf("FontSize") == -1) {
+      removeButtonStr = removeButtonStr + ",FontSize";
+    }
+    if (!this.extrabuttons || this.extrabuttons.indexOf("TextColor") == -1) {
+      removeButtonStr = removeButtonStr + ",TextColor";
+    }
+
+    let config = {
+      toolbar: this.toolbar,
+      language: this.language,
+      height: this.height,
+      width: this.width,
+      extraPlugins: this.extraplugins + ",image2,base64image,pastebase64",
+      removeButtons: removeButtonStr,
+      removePlugins: removePluginStr,
+    };
+    window.CKEDITOR.replace(this.id, config);
+    this.instance.on("instanceReady", (ev) => {
+      ev.editor.setReadOnly(this.readonly);
+    });
+    this.instance.on("change", () => {
+      let html = this.instance.getData();
+      if (html !== this.value) {
+        this.$emit("input", html);
+      }
+    });
+  },
+  beforeDestroy() {
+    if (this.instance) {
+      this.instance.focusManager.blur(true);
+      this.instance.destroy();
+    }
+  },
+};
+</script>
+<style>
+.ckeditor::after {
+  content: "";
+  display: table;
+  clear: both;
+}
+.cke_button__image {
+  display: none !important;
+}
+</style>

+ 38 - 0
src/components/registerComponents.js

@@ -0,0 +1,38 @@
+import Vue from "vue";
+// import upperFirst from "lodash/upperFirst";
+// import camelCase from "lodash/camelCase";
+
+const requireComponent = require.context(
+  // The relative path of the components folder
+  "./",
+  // Whether or not to look in subfolders
+  false,
+  // The regular expression used to match base component filenames
+  /[A-Z]\w+\.(vue|js)$/
+);
+
+requireComponent.keys().forEach((fileName) => {
+  // Get component config
+  const componentConfig = requireComponent(fileName);
+
+  // Get PascalCase name of component
+  const componentName =
+    // upperFirst(
+    //   camelCase(
+    // Gets the file name regardless of folder depth
+    fileName
+      .split("/")
+      .pop()
+      .replace(/\.\w+$/, "");
+  //   )
+  // );
+
+  // Register component globally
+  Vue.component(
+    componentName,
+    // Look for the component options on `.default`, which will
+    // exist if the component was exported with `export default`,
+    // otherwise fall back to module's root.
+    componentConfig.default || componentConfig
+  );
+});

+ 114 - 0
src/constants/constants.js

@@ -0,0 +1,114 @@
+export const CORE_API = "/api/ecs_core"; //基础信息API
+export const EXAM_WORK_API = "/api/ecs_exam_work"; //考务信息API
+export const MARKING_API = "/api/ecs_marking"; //阅卷API
+export const DATA_PROCESS_API = "/api/ecs_data_process"; //数据中间层API
+export const OE_API = "/api/ecs_oe"; //网考API
+export const QUESTION_API = "/api/uq_basic"; //题库API
+export const EXCHANGE_API = "/api/ecs_outlet"; //接口机
+export const PRINT_API = "/api/ecs_prt";
+export const TASK_API = "/api/ctr/task";
+export const REPORTS_API = "/api/ecs_reports";
+
+//考试类型
+export const EXAM_TYPE = [
+  { label: "传统", value: "TRADITION" },
+  { label: "网考", value: "ONLINE" },
+  { label: "练习", value: "PRACTICE" },
+  { label: "离线", value: "OFFLINE" },
+  { label: "在线作业", value: "ONLINE_HOMEWORK" },
+  { label: "印刷", value: "PRINT_EXAM" },
+];
+
+// for exam type select
+export const EXAM_TYPE_SELECT = [
+  { code: "TRADITION", name: "传统" },
+  { code: "ONLINE", name: "网考" },
+  { code: "PRACTICE", name: "练习" },
+  { code: "OFFLINE", name: "离线" },
+  { code: "ONLINE_HOMEWORK", name: "在线作业" },
+  { code: "PRINT_EXAM", name: "印刷" },
+];
+
+// for level type select
+export const LEVEL_TYPE_SELECT = [
+  { code: "ZSB", name: "专升本" },
+  { code: "GQZ", name: "高起专" },
+  { code: "GQB", name: "高起本" },
+  { code: "ALL", name: "不限" },
+];
+
+//试卷类型
+export const PAPER_TYPE = [
+  "A",
+  "B",
+  "C",
+  "D",
+  "E",
+  "F",
+  "G",
+  "H",
+  "I",
+  "J",
+  "K",
+  "L",
+  "M",
+  "N",
+  "O",
+  "P",
+  "Q",
+  "R",
+  "S",
+  "T",
+  "U",
+  "V",
+  "W",
+  "X",
+  "Y",
+  "Z",
+];
+
+//数据类型
+export const BASIC_DATA_TYPE = [
+  { code: "STRING", name: "STRING" },
+  { code: "INTEGER", name: "INTEGER" },
+  { code: "BOOLEAN", name: "BOOLEAN" },
+  { code: "LONG", name: "LONG" },
+  { code: "DATE", name: "DATE" },
+];
+//发布状态
+export const PUBLISH_STATUS = [
+  { code: "DRAFT", name: "未发布" },
+  { code: "TO_BE_PUBLISHED", name: "待发布" },
+  { code: "PUBLISHING", name: "发布中" },
+  { code: "PUBLISHED", name: "已发布" },
+];
+//公告接受规则类型
+export const NOTICE_RECEIVER_RULE_TYPE = [
+  { code: "STUDENTS_OF_EXAM", name: "学生-按考试选择" },
+  { code: "ALL_STUDENTS_OF_ROOT_ORG", name: "学生-学校中所有学生" },
+  { code: "TEACHER_OF_MARK_WORK", name: "老师-按评卷老师选择" },
+  { code: "COMMON_USERS_OF_ROLE", name: "老师-学习中心老师" },
+];
+//登录帐号类型
+export const LOGIN_TYPE = [
+  { code: "STUDENT_CODE", name: "学号登录" },
+  { code: "IDENTITY_NUMBER", name: "身份证号登录" },
+];
+//防作弊配置
+export const PREVENT_CHEATING_CONFIG = [
+  { code: "DISABLE_REMOTE_ASSISTANCE", name: "禁用远程协助" },
+  { code: "DISABLE_VIRTUAL_CAMERA", name: "禁用虚拟摄像头" },
+  // { code: "DISABLE_VIRTUAL_MACHINE", name: "禁用虚拟机" },
+  { code: "FULL_SCREEN_TOP", name: "强制全屏置顶" },
+  { code: "DISABLE_MULTISCREEN", name: "禁用双屏" },
+];
+//学生端版本
+export const STUDENT_CLIENT_VERSION = [
+  { code: "1.0.0", name: "1.0.0" },
+  { code: "2.0.0", name: "2.0.0" },
+];
+//考生端支持的登录方
+export const LOGIN_SUPPORT = [
+  { code: "NATIVE", name: "考生端登录" },
+  { code: "BROWSER", name: "浏览器登录" },
+];

+ 1 - 0
src/directives/directives.js

@@ -0,0 +1 @@
+import "../modules/questions/directives/directives";

+ 12 - 0
src/filters/filters.js

@@ -0,0 +1,12 @@
+import "../modules/questions/filters/filters";
+import Vue from "vue";
+import { LEVEL_TYPE_SELECT } from "@/constants/constants";
+
+// 课程层次过滤器
+Vue.filter("levelTypeFilter", function (val) {
+  for (let examType of LEVEL_TYPE_SELECT) {
+    if (examType.code === val) {
+      return examType.name;
+    }
+  }
+});

+ 122 - 0
src/global.d.ts

@@ -0,0 +1,122 @@
+// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
+// Project: [~THE PROJECT NAME~]
+// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
+
+// import EditorJS from "@editorjs/editorjs";
+
+/*~ If this library is callable (e.g. can be invoked as myLib(3)),
+ *~ include those call signatures here.
+ *~ Otherwise, delete this section.
+ */
+// declare function myLib(a: string): string;
+// declare function myLib(a: number): number;
+
+// /*~ If you want the name of this library to be a valid type name,
+//  *~ you can do so here.
+//  *~
+//  *~ For example, this allows us to write 'var x: myLib';
+//  *~ Be sure this actually makes sense! If it doesn't, just
+//  *~ delete this declaration and add types inside the namespace below.
+//  */
+// interface myLib {
+//   name: string;
+//   length: number;
+//   extras?: string[];
+// }
+
+// interface Window {
+//   EditorJS: EditorJS;
+// }
+
+// TS有用。JS没用。
+// interface Window {
+//   test: string;
+//  }
+
+//  declare var ee: EditorJS;
+
+ 
+ // JS可用。 window.gee
+//  declare global {
+//    var gee: EditorJS;
+
+//    // 或者
+//    interface Window {
+//     gtest: string;
+//    }
+//  }
+
+// /*~ If your library has properties exposed on a global variable,
+//  *~ place them here.
+//  *~ You should also place types (interfaces and type alias) here.
+//  */
+// declare namespace myLib {
+//   //~ We can write 'myLib.timeout = 50;'
+//   let timeout: number;
+
+//   //~ We can access 'myLib.version', but not change it
+//   const version: string;
+
+//   //~ There's some class we can create via 'let c = new myLib.Cat(42)'
+//   //~ Or reference e.g. 'function f(c: myLib.Cat) { ... }
+//   class Cat {
+//     constructor(n: number);
+
+//     //~ We can read 'c.age' from a 'Cat' instance
+//     readonly age: number;
+
+//     //~ We can invoke 'c.purr()' from a 'Cat' instance
+//     purr(): void;
+//   }
+
+//   //~ We can declare a variable as
+//   //~   'var s: myLib.CatSettings = { weight: 5, name: "Maru" };'
+//   interface CatSettings {
+//     weight: number;
+//     name: string;
+//     tailLength?: number;
+//   }
+
+//   //~ We can write 'const v: myLib.VetID = 42;'
+//   //~  or 'const v: myLib.VetID = "bob";'
+//   type VetID = string | number;
+
+//   //~ We can invoke 'myLib.checkCat(c)' or 'myLib.checkCat(c, v);'
+//   function checkCat(c: Cat, s?: VetID);
+// }
+
+import Vue from "vue";
+import { AxiosInstance, AxiosRequestConfig } from "axios";
+
+// 可惜内部代码不能通过此类型推导
+//import * as api from "./api";
+declare module '*.vue' {
+  import Vue from 'vue'
+  export default Vue
+}
+declare module "vue/types/vue" {
+  interface Vue {
+    $http: AxiosInstance;
+    // $api: api;
+    /** @param cause "?cause=正常退出" */
+    logout(cause?: string): Promise;
+     /**
+     * 会在 beforeDestory 中自动清除
+     * @param {function} fn 要执行的函数
+     * @param {number} interval 执行间隔ms
+     */
+    addInterval(fn: Function, interval: number): void;
+     /**
+     * 会在 beforeDestory 中自动清除
+     * @param {function} fn 要执行的函数
+     * @param {number} interval 执行间隔ms
+     */
+    addTimeout(fn: Function, interval: number): void;
+  }
+}
+
+declare module "axios/index" {
+  interface AxiosRequestConfig {
+    noErrorMessage?: boolean | false;
+  }
+}

+ 30 - 0
src/main.js

@@ -0,0 +1,30 @@
+import Vue from "vue";
+import App from "./App.vue";
+import router from "./router";
+import store from "./store";
+// import "./registerServiceWorker";
+import "./plugins/element.js";
+import "./plugins/axios";
+import "./plugins/vueAwesome";
+import "./directives/directives.js";
+import "./filters/filters.js";
+import "./styles/bootstrap.scss";
+import "./styles/global.css";
+
+Vue.config.productionTip = process.env.NODE_ENV !== "production";
+
+if (
+  process.env.NODE_ENV === "development" &&
+  process.env.VUE_APP_ENABLE_VUE_RENDER_LOGS === "true"
+) {
+  const vueLifecylceLogs = require("./plugins/vueLifecylceLogs").default;
+  vueLifecylceLogs({});
+}
+
+Vue.prototype.$eventHub = new Vue();
+
+new Vue({
+  router,
+  store,
+  render: (h) => h(App),
+}).$mount("#app");

BIN=BIN
src/modules/portal/assets/images/bg.jpg


+ 0 - 0
src/modules/portal/constants/constants.js


+ 29 - 0
src/modules/portal/routes/routes.js

@@ -0,0 +1,29 @@
+import Login from "../views/Login.vue";
+import Component404 from "../views/Component404.vue";
+export default [
+  {
+    path: "/",
+    name: "Root",
+    meta: { pageName: "首页" },
+    component: Login,
+  },
+  {
+    path: "/login",
+    name: "Login",
+    component: Login,
+  },
+  {
+    // will match everything
+    path: "*",
+    component: Component404,
+  },
+  // {
+  //   path: "/about",
+  //   name: "about",
+  //   // route level code-splitting
+  //   // this generates a separate chunk (about.[hash].js) for this route
+  //   // which is lazy-loaded when the route is visited.
+  //   component: () =>
+  //     import(/* webpackChunkName: "about" */ "./views/About.vue")
+  // }
+];

+ 14 - 0
src/modules/portal/store/currentPaths.js

@@ -0,0 +1,14 @@
+export const UPDATE_CURRENT_PATHS = "UPDATE_CURRENT_PATHS";
+
+export default {
+  state: [],
+  mutations: {
+    [UPDATE_CURRENT_PATHS](state, paths) {
+      // console.log(state, paths);
+      // state = paths;
+      // Object.assign(state, paths);
+      state.length = 0;
+      state.push(...paths);
+    },
+  },
+};

+ 12 - 0
src/modules/portal/store/menuList.js

@@ -0,0 +1,12 @@
+export const UPDATE_MENU_LIST = "UPDATE_MENU_LIST";
+
+export default {
+  state: [],
+  mutations: {
+    [UPDATE_MENU_LIST](state, menuList) {
+      // console.log(state, menuList);
+      state.length = 0;
+      state.push(...menuList);
+    },
+  },
+};

+ 26 - 0
src/modules/portal/store/user.js

@@ -0,0 +1,26 @@
+import Vue from "vue";
+
+export const USER_SIGNIN = "USER_SIGNIN"; //登录成功
+export const USER_SIGNOUT = "USER_SIGNOUT"; //退出登录
+
+export default {
+  state: JSON.parse(sessionStorage.getItem("user")) || {},
+  mutations: {
+    [USER_SIGNIN](state, user) {
+      sessionStorage.setItem("user", JSON.stringify(user));
+      Object.assign(state, user);
+    },
+    [USER_SIGNOUT](state) {
+      sessionStorage.removeItem("user");
+      Object.keys(state).forEach((k) => Vue.delete(state, k));
+    },
+  },
+  actions: {
+    [USER_SIGNIN]({ commit }, user) {
+      commit(USER_SIGNIN, user);
+    },
+    [USER_SIGNOUT]({ commit }) {
+      commit(USER_SIGNOUT);
+    },
+  },
+};

+ 3 - 0
src/modules/portal/views/Component404.vue

@@ -0,0 +1,3 @@
+<template>
+  <div>找不到该页面!请联系管理员。</div>
+</template>

+ 305 - 0
src/modules/portal/views/Login.vue

@@ -0,0 +1,305 @@
+<template>
+  <div class="bg">
+    <!-- <header class="login-header">
+          <img v-if="!jwptCustomize" src="../assets/images/login_footer_logo.jpg" />
+          <img v-else src="../assets/images/new_login_logo.png" />
+          <span class="qm-logo-text" v-if="!jwptCustomize">考试云平台</span>
+          <span class="qm-logo-text" v-else>{{ title }}</span>
+        </header> -->
+
+    <main class="login-main">
+      <!-- <img class="left_tree" src="../assets/images/login_main_left_tree.png" /> -->
+
+      <div class="logo-text">题库</div>
+      <div class="right_login">
+        <h1 style="font-size: 20px; color: #666">欢迎登录</h1>
+        <div class="username">
+          <v-icon
+            name="user"
+            scale="1.4"
+            style="padding: 5px 0px; z-index: 3"
+          />
+          <input
+            id="accountValue"
+            v-model="loginInfo.accountValue"
+            type="text"
+            placeholder="请输入账号"
+            @keyup.enter="login"
+            @change="nameChange"
+          />
+        </div>
+        <div class="password">
+          <v-icon
+            name="lock"
+            scale="1.4"
+            style="padding: 5px 0px; z-index: 3"
+          />
+          <input
+            id="password"
+            v-model="loginInfo.password"
+            type="password"
+            placeholder="请输入密码"
+            @keyup.enter="login"
+          />
+        </div>
+        <div class="smscode">
+          <v-icon
+            v-if="dialogVisible"
+            name="lock"
+            scale="1.4"
+            style="padding: 5px 0px; z-index: 3"
+          />
+          <input
+            v-if="dialogVisible"
+            id="smsCode"
+            v-model="loginInfo.smsCode"
+            type="text"
+            placeholder="请输入短信验证码"
+            @keyup.enter="login"
+          />
+        </div>
+        <button class="login-btn" @click="login">登 录</button>
+      </div>
+      <footer class="login-footer">
+        Copyright &copy; 2021
+        <a href="https://www.qmth.com.cn">武汉启明软件</a>.
+      </footer>
+    </main>
+  </div>
+</template>
+
+<script>
+import { mapActions } from "vuex";
+import { USER_SIGNIN } from "../store/user";
+import { QUESTION_API } from "@/constants/constants";
+
+export default {
+  data() {
+    return {
+      errorInfo: "",
+      title: "考试云平台",
+      jwptCustomize: false,
+      dialogVisible: false,
+      loginInfo: {
+        rootOrgId: "",
+        domain: "",
+        accountType: "COMMON_LOGIN_NAME",
+        accountValue: "",
+        password: "",
+        smsCode: null,
+      },
+    };
+  },
+  watch: {
+    $route(to) {
+      this.loginInfo.rootOrgId = to.query.orgId;
+    },
+  },
+  created() {
+    if (this.$route.hash && this.$route.hash.startsWith("#/access?")) {
+      this.$router.push(this.$route.hash.slice(1));
+      return;
+    }
+    this.loginInfo.domain =
+      window.location.hostname.split(".")[0] + ".ecs.qmth.com.cn";
+    sessionStorage.clear();
+    var params = this.$route.query;
+
+    this.loginInfo.rootOrgId = params.orgId;
+  },
+  methods: {
+    ...mapActions([USER_SIGNIN]),
+    nameChange() {
+      // this.dialogVisible = false;
+    },
+    checkAccountValue() {
+      this.errorInfo = "";
+      if (!this.loginInfo.accountValue) {
+        this.errorInfo += "账号不能为空!\n";
+      }
+      if (this.errorInfo) {
+        this.$notify({
+          showClose: true,
+          message: this.errorInfo,
+          type: "error",
+        });
+        return false;
+      }
+      return true;
+    },
+
+    checkPassword() {
+      this.errorInfo = "";
+      if (!this.loginInfo.password) {
+        this.errorInfo += "密码不能为空!\n";
+      }
+      if (this.errorInfo) {
+        this.$notify({
+          showClose: true,
+          message: this.errorInfo,
+          type: "error",
+        });
+        return false;
+      }
+      return true;
+    },
+
+    login() {
+      if (!this.checkAccountValue()) {
+        return;
+      }
+      if (!this.checkPassword()) {
+        return;
+      }
+      var url = QUESTION_API + "/auth/login";
+      this.$httpWithMsg
+        .post(url, this.loginInfo)
+        .then((response) => {
+          var user = response.data;
+          this.USER_SIGNIN(user);
+          this.$router.replace({ path: "/questions/tips" });
+          this.$notify({
+            message: "登录成功",
+            type: "success",
+            duration: 1000,
+          });
+        })
+        .catch((error) => {
+          if (error.response.data.code === "UQ-003100") {
+            this.dialogVisible = true;
+          }
+        });
+    },
+    loginWithSms() {
+      this.dialogVisible = false;
+      this.login();
+    },
+  },
+};
+</script>
+
+<style scoped>
+.bg {
+  width: 100vw;
+  height: 100vh;
+  background-image: url("../assets/images/bg.jpg");
+  background-size: cover;
+}
+
+.qm-logo-text {
+  font-size: 36px;
+  font-weight: 700;
+  font-stretch: normal;
+  line-height: 36px;
+  letter-spacing: 0px;
+  margin-left: 40px;
+}
+
+.logo-text {
+  /* font-family: "Xingkai SC", "STXingkai", "KaiTi"; */
+  width: 100%;
+  height: 40px;
+  font-size: 40px;
+  margin-bottom: 30px;
+  font-weight: bold;
+  font-stretch: normal;
+  line-height: 48px;
+  letter-spacing: 0px;
+  color: #3968d7;
+  text-shadow: 0px 7px 4px rgba(77, 124, 196, 0.3);
+  text-align: center;
+  letter-spacing: 0.2em;
+}
+
+.login-main {
+  width: 100%;
+  height: 90vh;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.right_login {
+  width: 480px;
+  height: 330px;
+  background-color: #ffffff;
+  box-shadow: 0px 7px 20px 0px rgba(77, 124, 196, 0.1);
+  border-radius: 38px;
+  opacity: 0.8;
+  padding: 0 80px;
+}
+
+.right_login h1 {
+  text-align: center;
+  height: 20px;
+  font-size: 20px;
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: 24px;
+  letter-spacing: 0px;
+  color: #666666;
+  margin: 30px 0;
+}
+
+.right_login .username {
+  display: flex;
+  align-items: center;
+  justify-items: center;
+}
+.right_login .password {
+  display: flex;
+  align-items: center;
+  justify-items: center;
+  margin-top: 10px;
+}
+.right_login .smscode {
+  height: 38px;
+  display: flex;
+  align-items: center;
+  justify-items: center;
+  margin-top: 10px;
+}
+
+.right_login input {
+  width: 100%;
+  padding: 5px 30px;
+  margin-left: -21px;
+  font-size: 16px;
+  border-radius: 10px;
+}
+
+.right_login input::placeholder {
+  font-size: 16px;
+  color: #000000;
+  opacity: 0.3;
+}
+
+.login-btn {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  width: 100%;
+  background-color: #4d7cc4;
+  font-size: 25px;
+  color: #fff;
+  cursor: pointer;
+  border-radius: 27px;
+}
+
+.login-btn:hover {
+  box-shadow: 0 16px 29px rgba(29, 170, 240, 0.3);
+}
+
+.login-footer {
+  clear: both;
+  width: 240px;
+  height: 40px;
+  line-height: 40px;
+  background-color: #fff;
+  color: #999999;
+  margin: -20px auto;
+  text-align: center;
+  border-radius: 25px;
+  z-index: 3;
+}
+</style>

+ 436 - 0
src/modules/portal/views/home/Home.vue

@@ -0,0 +1,436 @@
+<template>
+  <el-container>
+    <el-header style="padding: 0">
+      <el-menu class="el-menu" mode="horizontal">
+        <el-menu-item index="1" style="width: 200px">
+          <router-link
+            to="/questions/tips"
+            style="
+              display: inline-block;
+              font-size: 20px;
+              line-height: 48px;
+              width: 100%;
+              text-decoration-line: none;
+            "
+          >
+            <img
+              src="./icon.png"
+              style="
+                margin-bottom: 2px;
+                line-height: 48px;
+                display: inline-block;
+              "
+            />
+            题库
+          </router-link>
+        </el-menu-item>
+        <el-menu-item
+          index="4"
+          style="float: right"
+          title="退出系统"
+          @click="logout"
+        >
+          <v-icon name="sign-out-alt" />
+          <span style="cursor: pointer"> 退出 </span>
+        </el-menu-item>
+        <el-menu-item index="3" style="float: right" title="个人信息管理">
+          <v-icon name="user" />
+          <span style="cursor: pointer" @click="openUserDialog">
+            {{ user.displayName }}
+          </span>
+        </el-menu-item>
+        <el-menu-item
+          index="1"
+          class="navbar-group-item"
+          style="float: right"
+          title="机构名称"
+        >
+          <v-icon name="users" /> {{ user.rootOrgName }}
+        </el-menu-item>
+      </el-menu>
+    </el-header>
+
+    <el-container>
+      <HomeSide v-if="ifShowHomeSide" :key="sideKey" />
+      <el-container class="main-body">
+        <LinkTitles v-if="ifShowHomeSide" :key="Math.random()" />
+        <router-view class="main-content"></router-view>
+        <el-footer class="footer">&copy; 启明泰和 2019</el-footer>
+      </el-container>
+    </el-container>
+    <!-- 添加用户信息弹出框 -->
+    <el-dialog
+      title="个人信息"
+      width="410px"
+      :visible.sync="userDialog"
+      @close="() => $refs.passForm.clearValidate()"
+    >
+      <el-tabs value="first">
+        <el-tab-pane label="用户权限" name="first">
+          <el-form :inline="true" label-position="right" label-width="90px">
+            <el-row :gutter="10">
+              <el-col>
+                <el-tag
+                  v-for="role in user.roleList"
+                  :key="role.roleId"
+                  type="primary"
+                  style="margin-left: 10px; margin-top: 10px"
+                >
+                  {{ role.roleName }}
+                </el-tag>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-tab-pane>
+        <el-tab-pane label="修改密码" name="second">
+          <el-form
+            ref="passForm"
+            :inline="true"
+            inline-message
+            :model="passForm"
+            :rules="passRules"
+            label-position="right"
+            label-width="80px"
+          >
+            <el-row>
+              <el-form-item label="密码" prop="pass">
+                <el-input
+                  v-model="passForm.pass"
+                  type="password"
+                  style="width: 150px"
+                  auto-complete="off"
+                  placeholder="请输入密码"
+                />
+              </el-form-item>
+            </el-row>
+            <el-row>
+              <el-form-item label="确认密码" prop="checkPass">
+                <el-input
+                  v-model="passForm.checkPass"
+                  type="password"
+                  style="width: 150px"
+                  auto-complete="off"
+                  placeholder="请输入确认密码"
+                />
+              </el-form-item>
+            </el-row>
+            <el-row style="margin-left: 100px">
+              <el-button type="primary" @click="submitForm">保 存</el-button>
+              <el-button @click="userDialog = false">取 消</el-button>
+            </el-row>
+          </el-form>
+        </el-tab-pane>
+      </el-tabs>
+    </el-dialog>
+    <el-dialog
+      title="修改密码"
+      width="406px"
+      :lock-scroll="true"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :show-close="false"
+      :visible.sync="passWeakDialog"
+      @close="() => $refs.passWeakForm.clearValidate()"
+    >
+      <el-form
+        ref="passWeakForm"
+        :inline="true"
+        inline-message
+        :model="passWeakForm"
+        :rules="passWeakRules"
+        label-position="right"
+        label-width="80px"
+      >
+        <el-row>
+          <el-form-item label="密码" prop="pass">
+            <el-input
+              v-model="passWeakForm.pass"
+              type="password"
+              style="width: 150px"
+              auto-complete="off"
+              placeholder="输入新密码"
+            />
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="确认密码" prop="checkPass">
+            <el-input
+              v-model="passWeakForm.checkPass"
+              type="password"
+              style="width: 150px"
+              auto-complete="off"
+              placeholder="再次输入新密码"
+            />
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item
+            ><span class="passWeakInfo"
+              >*为保护您的账户安全,请重新录入新密码,以防泄露
+            </span></el-form-item
+          >
+        </el-row>
+        <el-row style="margin-left: 80px">
+          <el-button type="primary" @click="submitPassWeakForm"
+            >确 认</el-button
+          >
+          <el-button @click="logout">取 消</el-button>
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </el-container>
+</template>
+
+<script>
+import { mapActions, mapState } from "vuex";
+import { USER_SIGNOUT, USER_SIGNIN } from "../../store/user";
+import { QUESTION_API } from "@/constants/constants";
+import HomeSide from "./HomeSide.vue";
+import LinkTitles from "./LinkTitles.vue";
+export default {
+  name: "Home",
+  components: { HomeSide, LinkTitles },
+  data() {
+    var validatePass = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("请输入密码"));
+      } else if (value.length < 6) {
+        callback(new Error("密码长度至少6位"));
+      } else {
+        if (this.passForm.checkPass !== "") {
+          this.$refs.passForm.validateField("checkPass");
+        }
+        callback();
+      }
+    };
+    var validatePass2 = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("请输入确认密码"));
+      } else if (value !== this.passForm.pass) {
+        callback(new Error("两次输入密码不一致!"));
+      } else {
+        callback();
+      }
+    };
+    var validatePassWeakPass = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("请输入密码"));
+      } else if (value.length < 6) {
+        callback(new Error("密码长度至少6位"));
+      } else {
+        if (this.passWeakForm.checkPass !== "") {
+          this.$refs.passWeakForm.validateField("checkPass");
+        }
+        callback();
+      }
+    };
+    var validatePassWeakPass2 = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("请输入确认密码"));
+      } else if (value !== this.passWeakForm.pass) {
+        callback(new Error("两次输入密码不一致!"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      unreadMessageCount: 0,
+      userDialog: false,
+      passWeakDialog: false,
+      passForm: { pass: "", checkPass: "" },
+      passWeakForm: { pass: "", checkPass: "" },
+      passRules: {
+        pass: [{ validator: validatePass, trigger: "blur" }],
+        checkPass: [{ validator: validatePass2, trigger: "blur" }],
+      },
+      passWeakRules: {
+        pass: [{ validator: validatePassWeakPass, trigger: "blur" }],
+        checkPass: [{ validator: validatePassWeakPass2, trigger: "blur" }],
+      },
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    ifShowHomeSide() {
+      return this.$route.fullPath.startsWith("/home") === false;
+    },
+    sideKey() {
+      const module = this.$route.fullPath.split("/")[1];
+      return module;
+    },
+  },
+  created() {
+    if (this.user.passwordWeak) {
+      this.passWeakDialog = true;
+    }
+  },
+  methods: {
+    ...mapActions([USER_SIGNOUT, USER_SIGNIN]),
+    openMessageHome() {
+      this.$router.push({ path: "/home/site-message" });
+    },
+    changeUnreadMessageCount(count) {
+      this.unreadMessageCount = count;
+    },
+    openUserDialog() {
+      this.passForm = { pass: "", checkPass: "" };
+      this.userDialog = true;
+    },
+    //保存密码
+    submitForm() {
+      this.$refs.passForm.validate((valid) => {
+        if (valid) {
+          var password = encodeURIComponent(this.passForm.pass);
+          var url = QUESTION_API + "/user/password?password=" + password;
+          this.$httpWithMsg.put(url).then(() => {
+            this.$notify({
+              type: "success",
+              message: "修改密码成功!",
+            });
+            this.resetForm();
+            this.userDialog = false;
+          });
+        } else {
+          console.log("error submit!");
+          return false;
+        }
+      });
+    },
+    submitPassWeakForm() {
+      this.$refs.passWeakForm.validate((valid) => {
+        if (valid) {
+          var password = encodeURIComponent(this.passWeakForm.pass);
+          var url = QUESTION_API + "/user/password?password=" + password;
+          this.$httpWithMsg.put(url).then(() => {
+            this.$notify({
+              type: "success",
+              message: "修改密码成功!",
+            });
+            this.user.passwordWeak = false;
+            this.USER_SIGNIN(this.user);
+            this.$refs.passWeakForm.resetFields();
+            this.passWeakDialog = false;
+          });
+        } else {
+          console.log("error submit!");
+          return false;
+        }
+      });
+    },
+    //重置
+    resetForm() {
+      this.$refs.passForm.resetFields();
+    },
+    logout() {
+      const orgId = this.user.rootOrgId;
+      const getRootOrgId = () => {
+        if (location.hostname.includes("qmth.com.cn")) {
+          return "";
+        } else {
+          return "?orgId=" + orgId;
+        }
+      };
+
+      this.$http
+        .post(QUESTION_API + "/auth/logout")
+        .then(() => {
+          this.USER_SIGNOUT();
+          window.name = "";
+          this.$router.replace({
+            path: "/login" + getRootOrgId(),
+          });
+        })
+        .catch((response) => {
+          if (response.status == 500) {
+            this.$notify({
+              showClose: true,
+              message: response.data.desc,
+              type: "error",
+            });
+          }
+          this.USER_SIGNOUT();
+          window.name = "";
+          this.$router.replace({
+            path: "/login" + getRootOrgId(),
+          });
+        });
+    },
+  },
+};
+</script>
+
+<style scoped>
+.el-menu,
+.el-footer {
+  background-color: #3c8dbd;
+  color: #ffffff;
+  text-align: center;
+  line-height: 60px;
+}
+.el-footer {
+  color: #878e93;
+  background-color: #ecf0f5;
+  line-height: 40px;
+  height: 40px !important;
+}
+
+.el-menu.el-menu--horizontal {
+  border-bottom: none;
+}
+
+.el-menu >>> .el-menu-item {
+  color: white !important;
+}
+
+.el-menu >>> .el-menu-item:hover,
+.el-menu >>> .el-menu-item:focus {
+  color: white !important;
+  background-color: rgba(40, 121, 169) !important;
+}
+
+.el-menu >>> .is-active.el-menu-item:focus {
+  color: white !important;
+}
+
+.el-menu >>> .navbar-group-item.is-active.el-menu-item:focus {
+  color: none !important;
+}
+
+.el-menu >>> .navbar-group-item.el-menu-item:hover,
+.el-menu >>> .navbar-group-item.el-menu-item:focus {
+  color: white !important;
+  background-color: transparent !important;
+  cursor: unset;
+  border-bottom: none;
+}
+
+body > .el-container {
+  margin-bottom: 40px;
+}
+
+.main-body {
+  min-height: calc(100vh - 100px);
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  margin-top: 20px;
+  margin-left: 20px;
+}
+
+.main-content {
+  min-height: calc(100vh - 60px - 60px - 40px);
+  margin-top: 20px;
+  margin-right: 20px;
+}
+
+.footer {
+  justify-self: flex-end;
+  margin-left: -20px;
+}
+.passWeakInfo {
+  color: blue !important;
+}
+.passWeakErr {
+  color: red !important;
+}
+</style>

+ 175 - 0
src/modules/portal/views/home/HomeSide.vue

@@ -0,0 +1,175 @@
+<template>
+  <el-aside v-if="menuList.length > 0" width="">
+    <el-menu
+      class="el-menu-vertical-demo"
+      background-color="#222c32"
+      text-color="#fff"
+      active-text-color="#409eff"
+      router
+      :default-active="$route.path"
+      :collapse="isCollapse"
+    >
+      <el-submenu
+        v-for="menu1 in menuList1"
+        :key="menu1.id"
+        :index="menu1.nodeCode"
+      >
+        <template slot="title">
+          <i class="el-icon-menu"></i> <span>{{ menu1.name }}</span>
+        </template>
+
+        <el-menu-item
+          v-for="menu2 in menuList2(menu1)"
+          :key="menu2.id"
+          :index="menu2.ext5"
+          :route="{ path: menu2.ext5 }"
+        >
+          <span style="margin-left: 9px">{{ menu2.name }}</span>
+        </el-menu-item>
+      </el-submenu>
+    </el-menu>
+  </el-aside>
+</template>
+
+<script>
+import { mapState } from "vuex";
+import { QUESTION_API } from "@/constants/constants";
+
+const routesToMenu = [
+  {
+    path: "/basic",
+    name: "基础信息",
+    groupCode: "BASIC_MENUS",
+  },
+  {
+    path: "/examwork",
+    name: "考务管理",
+    groupCode: "EXAM_WORK_MENUS",
+  },
+  {
+    path: "/questions",
+    name: "题库",
+    groupCode: "QUESTIONS_WORK_MENUS",
+  },
+  {
+    path: "/oe",
+    name: "网考",
+    groupCode: "NETEXAM_WORK_MENUS",
+  },
+  {
+    path: "/marking",
+    name: "阅卷",
+    groupCode: "MARK_WORK_MENUS",
+  },
+  {
+    path: "/portal",
+    groupCode: "PORTAL_MENUS",
+  },
+  {
+    path: "/print",
+    name: "印刷管理",
+    groupCode: "PRINT_MENUS",
+  },
+  {
+    path: "/reports",
+    name: "报表",
+    groupCode: "REPORTS_MENUS",
+  },
+];
+
+import { mapMutations } from "vuex";
+import { UPDATE_CURRENT_PATHS } from "../../store/currentPaths";
+import { UPDATE_MENU_LIST } from "../../store/menuList";
+
+export default {
+  name: "HomeSide",
+  data() {
+    return {
+      group: null,
+      menuList: [],
+      isCollapse: false,
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    menuList1() {
+      return this.menuList.filter(
+        (m) => m.parentId === null && m.ext1 === "menu"
+      );
+    },
+  },
+  watch: {
+    $route() {
+      this.updatePath();
+    },
+  },
+  async created() {
+    this.group = routesToMenu.find((v) => this.$route.path.startsWith(v.path));
+    const groupCode = this.group && this.group.groupCode;
+    if (groupCode) {
+      this.menuList = await this.getUserPrivileges(groupCode);
+      this.UPDATE_MENU_LIST(this.menuList);
+      this.updatePath();
+    }
+  },
+  methods: {
+    ...mapMutations([UPDATE_CURRENT_PATHS, UPDATE_MENU_LIST]),
+    toggoleSidebar() {
+      this.isCollapse = !this.isCollapse;
+    },
+    async getUserPrivileges(groupCode) {
+      var url = QUESTION_API + "/rolePrivilege/getUserPrivileges";
+      const params = new URLSearchParams();
+      params.append("groupCode", groupCode);
+      params.append("full", false);
+      const res = await this.$httpWithMsg.post(url, params, {
+        headers: { "content-type": "application/x-www-form-urlencoded" },
+      });
+
+      return res.data;
+    },
+    menuList2(menu1) {
+      return this.menuList.filter(
+        (m) => m.parentId === menu1.id && m.ext1 === "menu"
+      );
+    },
+    updatePath() {
+      let currentPaths = [];
+      let part = this.menuList.find((v) => v.ext5 === this.$route.path);
+      if (!part) {
+        this.UPDATE_CURRENT_PATHS([]);
+        return;
+      }
+      currentPaths.unshift(part.name);
+      while (this.menuList.find((v) => v.id === part.parentId)) {
+        part = this.menuList.find((v) => v.id === part.parentId);
+        currentPaths.unshift(part.name);
+      }
+      // console.log(currentPaths);
+      this.UPDATE_CURRENT_PATHS(currentPaths);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.el-menu-vertical-demo {
+  height: calc(100vh - 60px - 70px);
+  border-right: none;
+}
+
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  min-height: 400px;
+}
+
+.el-aside {
+  background: rgba(34, 44, 50, 1);
+  color: #fff;
+}
+
+.router-link-active {
+  color: #409eff !important;
+  font-weight: bold;
+}
+</style>

+ 28 - 0
src/modules/portal/views/home/LinkTitles.vue

@@ -0,0 +1,28 @@
+<template>
+  <el-breadcrumb
+    v-if="currentPaths.length > 0"
+    separator-class="el-icon-arrow-right"
+    style="border-bottom: 1px solid #ddd; padding-bottom: 5px"
+  >
+    <el-breadcrumb-item :to="{ path: '/home/overview' }"
+      >首页</el-breadcrumb-item
+    >
+    <el-breadcrumb-item
+      v-for="(item, index) in currentPaths"
+      :key="item + index"
+    >
+      {{ item }}
+    </el-breadcrumb-item>
+  </el-breadcrumb>
+</template>
+
+<script>
+import { mapState } from "vuex";
+
+export default {
+  name: "LinkTitles",
+  computed: {
+    ...mapState(["currentPaths"]),
+  },
+};
+</script>

BIN=BIN
src/modules/portal/views/home/icon.png


+ 203 - 0
src/modules/portal/views/home/main/HomeMain.vue

@@ -0,0 +1,203 @@
+<template>
+  <el-main style="display: flex; align-items: center; margin-bottom: 20px">
+    <el-row width="100vw">
+      <el-col
+        v-for="(menu, index) in menuList.filter((m) => m.parentId === null)"
+        :key="menu.id"
+        :span="11"
+        :offset="1"
+      >
+        <div
+          :class="['module-card', index % 2 ? 'float-left' : 'float-right']"
+          @click="() => $router.push('/' + menu.ext4 + '/tips')"
+        >
+          <div style="position: relative">
+            <img :class="['module-image', menu.ext4 + '-main-module']" />
+            <div class="cover"></div>
+          </div>
+          <div
+            class="align-self-left d-flex d-flex flex-column align-items-start module-desc"
+          >
+            <div class="h4">{{ menu.name }}</div>
+            <div style="width: 400px; font-size: 14px; text-align: left">
+              {{ moduleDesc[menu.ext4] && moduleDesc[menu.ext4].detail }}
+            </div>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </el-main>
+</template>
+
+<script>
+const moduleDesc = {
+  basic: {
+    detail: "包括学习中心查询、课程查询、用户查询",
+  },
+  examwork: {
+    detail: "包括安排考试、考试课程、学生档案",
+  },
+  questions: {
+    detail: "包括试卷导入、卷库组成、考试用卷绑定",
+  },
+  oe: {
+    detail: "包括考试过程监考、考试明细查询、考试完成进度查询",
+  },
+  marking: {
+    detail: "包括评卷进度查询、评卷员工作量核算、评卷结果检查",
+  },
+  print: {
+    detail: "记录线下纸笔考试数码印刷准确数量与实施过程",
+  },
+  reports: {
+    detail: "报表",
+  },
+};
+
+import { mapState } from "vuex";
+import { QUESTION_API } from "@/constants/constants";
+
+export default {
+  name: "HomeMain",
+  components: {},
+  data() {
+    return {
+      moduleDesc: moduleDesc,
+      menuList: [],
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+  },
+  async created() {
+    this.menuList = await this.getUserPrivileges("PORTAL_MENUS");
+  },
+  methods: {
+    async getUserPrivileges(groupCode) {
+      var url = QUESTION_API + "/rolePrivilege/getUserPrivileges";
+      const params = new URLSearchParams();
+      params.append("groupCode", groupCode);
+      params.append("full", false);
+      const res = await this.$httpWithMsg.post(url, params, {
+        headers: { "content-type": "application/x-www-form-urlencoded" },
+      });
+
+      return res.data;
+    },
+  },
+};
+</script>
+
+<style scoped>
+.el-main {
+  background-color: #e9eef3;
+  color: #333;
+  text-align: center;
+  line-height: 60px;
+}
+
+.main-content {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: center;
+  align-items: center;
+  justify-items: center;
+}
+
+.module-card {
+  height: 120px;
+  margin-bottom: 50px;
+  margin-right: 20px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+
+  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+</style>
+
+<style scoped>
+.module-card:hover .h4 {
+  color: #4d7cc4 !important;
+}
+.module-card:hover div {
+  color: #65738b !important;
+}
+.module-image {
+  width: 90px;
+  height: 90px;
+  background-position: center;
+  background-repeat: no-repeat;
+  background-color: #4d7cc4;
+  border-top-left-radius: 20px 20px;
+  border-bottom-left-radius: 20px 20px;
+}
+.module-card:hover .module-image {
+  background-color: #55bfff;
+  box-shadow: 0px 5px 20px 0px #55bfff;
+  color: #4d7cc4 !important;
+}
+.module-card .cover {
+  width: 100px;
+  height: 116px;
+  position: absolute;
+  top: -13px;
+  left: 0px;
+  transition: all 1s ease-in-out;
+  border-top-left-radius: 10px;
+  border-bottom-left-radius: 10px;
+  background-color: rgba(255, 255, 255, 0.8);
+}
+.module-card:hover .cover {
+  /* background-color: transparent !important; */
+  /* left: -100px; */
+  width: 0px !important;
+  left: -80px !important;
+  /* transition: all 2s ease-in-out; */
+}
+.module-desc {
+  background-color: white;
+  padding: 10px;
+}
+.module-card:hover .module-desc {
+  box-shadow: 7px 7px 10px 0px rgba(0, 0, 0, 0.1);
+}
+.basic-main-module {
+  background-image: url("./images/basic.png");
+}
+.module-card:hover .basic-main-module {
+  background-image: url("./images/basic-hover.png");
+}
+.examwork-main-module {
+  background-image: url("./images/examwork.png");
+}
+.module-card:hover .examwork-main-module {
+  background-image: url("./images/examwork-hover.png");
+}
+.questions-main-module {
+  background-image: url("./images/questions.png");
+}
+.module-card:hover .questions-main-module {
+  background-image: url("./images/questions-hover.png");
+}
+.oe-main-module {
+  background-image: url("./images/oe.png");
+}
+.module-card:hover .oe-main-module {
+  background-image: url("./images/oe-hover.png");
+}
+.marking-main-module {
+  background-image: url("./images/marking.png");
+}
+.module-card:hover .marking-main-module {
+  background-image: url("./images/marking-hover.png");
+}
+.print-main-module {
+  background-image: url("./images/print.png");
+}
+.module-card:hover .print-main-module {
+  background-image: url("./images/print-hover.png");
+}
+</style>

+ 298 - 0
src/modules/portal/views/tips/Tips.vue

@@ -0,0 +1,298 @@
+<template>
+  <section class="content">
+    <div><img src="./tips.png" /></div>
+    <div class="text">
+      <div v-for="(menu1, index) in instructions" :key="index" class="menu1">
+        <h4 v-if="menu1.menu2.filter((m) => m.canAccess).length > 0">
+          {{ menu1.menu1Name }}
+        </h4>
+        <div v-for="(menu2, index2) in menu1.menu2" :key="index2" class="menu1">
+          <span v-if="menu2.canAccess">
+            <router-link :to="menu2.link" class="link">
+              {{ menu2.name }}
+            </router-link>
+            {{ menu2.detail }}
+          </span>
+        </div>
+      </div>
+    </div>
+  </section>
+</template>
+
+<script>
+const ALL_INSTRUCTIONS = {
+  "/basic/tips": [
+    {
+      menu1Name: "学习中心查询",
+      menu2: [
+        {
+          name: "机构管理——中心列表",
+          link: "/basic/campus",
+          detail:
+            "查询学校下属所有学习中心编码及名称,可按学习中心设置学习中心平台权限,即A学习中心只能查询A中心的相关考试数据。",
+        },
+      ],
+    },
+    {
+      menu1Name: "课程查询",
+      menu2: [
+        {
+          name: "课程管理——课程列表",
+          link: "/basic/course",
+          detail:
+            "查看所有课程名称及代码,如一门课程即有专科又有本科,课程对应在的教材、试卷不一样时,需要建立专科课程及本科课程。",
+        },
+      ],
+    },
+    {
+      menu1Name: "用户查询",
+      menu2: [
+        {
+          name: "用户管理——普通用户管理",
+          link: "/basic/user",
+          detail:
+            "添加、查询、禁用管理平台用户,并可以针对不同用户设置不同的平台模块权限。",
+        },
+      ],
+    },
+  ],
+  "/examwork/tips": [
+    {
+      menu1Name: "安排考试",
+      menu2: [
+        {
+          name: "考试管理——考试信息",
+          link: "/examwork/examInfo",
+          detail: "安排一场在线考试,设置考试开启、结束时间。",
+        },
+      ],
+    },
+    {
+      menu1Name: "考试课程",
+      menu2: [
+        {
+          name: "考试管理——考生信息",
+          link: "/examwork/examStudent",
+          detail: "新增或导入考生报考的相关课程。",
+        },
+      ],
+    },
+    {
+      menu1Name: "学生档案",
+      menu2: [
+        {
+          name: "学生管理——学生信息",
+          link: "/examwork/student",
+          detail: "更新学生的登录名、密码、相片。",
+        },
+      ],
+    },
+  ],
+  "/questions/tips": [
+    {
+      menu1Name: "导入试卷",
+      menu2: [
+        {
+          name: "题库管理——导入试卷管理",
+          link: "/questions/import_paper/0",
+          detail: "可以将word版试卷导入题库,由题库按课程、题型形成题库。",
+        },
+      ],
+    },
+    {
+      menu1Name: "卷库组卷",
+      menu2: [
+        {
+          name: "卷库管理——考试试卷管理",
+          link: "/questions/gen_paper/0",
+          detail:
+            "可按需求将XX课程的所有试题,按分数、题量、题型结构、章节等多种方式进行随机组合,生成若干套试卷。",
+        },
+      ],
+    },
+    {
+      menu1Name: "考试用卷绑定",
+      menu2: [
+        {
+          name: "考试管理——调卷规则",
+          link: "/questions/extract_paper_rule/0",
+          detail: "来指定在本次考试中使用卷库中的哪一套试卷,及抽卷的比例。",
+        },
+      ],
+    },
+  ],
+  "/oe/tips": [
+    {
+      menu1Name: "考试过程监考",
+      menu2: [
+        {
+          name: "网考管理——监考待审",
+          link: "/oe/awaitingAudit",
+          detail: "列出所有考生考试过程中的数据,并提示出违纪数据审核。",
+        },
+      ],
+    },
+    {
+      menu1Name: "考试明细查询",
+      menu2: [
+        {
+          name: "统计报表——考试明细",
+          link: "/oe/examDetail",
+          detail:
+            "需要查询学生所有作答的试卷,可以在考试明细中输入学生信息进行查询考试时间、违纪情况、客观题得分、答卷等。",
+        },
+      ],
+    },
+    {
+      menu1Name: "考试完成进度查询",
+      menu2: [
+        {
+          name: "考试进度——考试概览",
+          link: "/oe/examSummary",
+          detail: "可以查看按考试、学习中心、课程数来查看考试完成的进度。",
+        },
+      ],
+    },
+    {
+      menu1Name: "考试完成进度查询",
+      menu2: [
+        {
+          name: "考试进度——考试进度详情",
+          link: "/oe/examScheduling",
+          detail: "可以查看按考试的人数、学习中心、查看考生的完成情况。",
+        },
+      ],
+    },
+  ],
+  "/print/tips": [
+    {
+      menu1Name: "项目管理",
+      menu2: [
+        {
+          name: "项目管理——项目列表",
+          link: "/print/project/list",
+          detail:
+            "查询学校下传统考试的统计概况信息,如考生人数,试卷数、试卷印刷数量等相关数据。",
+        },
+      ],
+    },
+  ],
+  "/marking/tips": [
+    {
+      menu1Name: "评卷进度查询",
+      menu2: [
+        {
+          name: "评卷总览——评卷进度",
+          link: "/marking/mark_work_overview",
+          detail: "本次评卷完成进度,可以按卷量、课程进行查询。",
+        },
+      ],
+    },
+    {
+      menu1Name: "评卷员工作量核算",
+      menu2: [
+        {
+          name: "评卷总览——评卷员一览",
+          link: "/marking/marker",
+          detail: "可以按评卷员统计出阅卷量、最高分、最低分、方差等。",
+        },
+      ],
+    },
+    {
+      menu1Name: "评卷结果检查",
+      menu2: [
+        {
+          name: "试卷检查",
+          link: "/marking/mark_paper_check",
+          detail:
+            "可以查看所有已评卷的结果,并对有疑问的试卷进行打回,让评卷员进行重新给分。",
+        },
+      ],
+    },
+    {
+      menu1Name: "试卷评分",
+      menu2: [
+        {
+          name: "试卷评阅-阅卷",
+          link: "/marking/mark_setting_work/marking",
+          detail: "评卷员可以对已分配的试卷进行打分。",
+        },
+      ],
+    },
+  ],
+  "/reports/tips": [
+    {
+      menu1Name: "分析项目",
+      menu2: [
+        {
+          name: "分析项目——项目列表",
+          link: "/reports/project",
+          detail: "分析项目——项目列表",
+        },
+      ],
+    },
+  ],
+};
+
+import { mapState } from "vuex";
+
+export default {
+  name: "Tips",
+  data() {
+    return {
+      instructions: [],
+    };
+  },
+  computed: { ...mapState({ menuList: (state) => state.menuList }) },
+  watch: {
+    menuList() {
+      const tipsLink = this.menuList.map((m) => m.ext5);
+      this.instructions.map((menu) => {
+        menu.menu2 = menu.menu2.map((m) => {
+          if (tipsLink.includes(m.link)) {
+            m.canAccess = true;
+          } else {
+            m.canAccess = false;
+          }
+          return m;
+        });
+        return menu;
+      });
+    },
+  },
+  created() {
+    this.instructions = ALL_INSTRUCTIONS[this.$route.path];
+  },
+  methods: {},
+};
+</script>
+
+<style scoped>
+.content {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  height: 100%;
+  width: 100%;
+
+  /* margin: 100px; */
+}
+
+.text {
+  max-width: 600px;
+  margin: 0 20px;
+}
+
+.menu1 {
+  margin-bottom: 20px;
+}
+
+.link {
+  padding: 0em 1em;
+  background-color: #dbe7ef;
+  border-radius: 12px;
+
+  color: #3c8dbd;
+}
+</style>

BIN=BIN
src/modules/portal/views/tips/tips.png


+ 116 - 0
src/modules/questions/component/ckeditor.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="ckeditor">
+    <textarea
+      :id="id"
+      :display="display"
+      :value="value"
+      class="el-textarea__inner"
+    ></textarea>
+  </div>
+</template>
+
+<script>
+let inc = 0;
+export default {
+  props: {
+    display: {
+      type: String,
+      default: "inline",
+    },
+    value: {
+      type: String,
+      default: "",
+    },
+    id: {
+      type: String,
+      default: () => `editor-${++inc}`,
+    },
+    height: {
+      type: String,
+      default: "300px",
+    },
+    width: {
+      type: String,
+      default: "500px",
+    },
+    language: {
+      type: String,
+      default: "zh-cn",
+    },
+    extraplugins: {
+      type: String,
+      default: "",
+    },
+  },
+  computed: {
+    instance() {
+      return window.CKEDITOR.instances[this.id];
+    },
+  },
+  beforeUpdate() {
+    if (this.value !== this.instance.getData()) {
+      this.instance.setData(this.value);
+    }
+  },
+  mounted() {
+    let config = {
+      language: this.language,
+      height: this.height,
+      width: this.width,
+      extraPlugins: this.extraplugins + ",base64image,pastebase64,image2",
+      toolbarGroups: [
+        { name: "clipboard", groups: ["clipboard", "undo"] },
+        {
+          name: "editing",
+          groups: ["find", "selection", "spellchecker", "editing"],
+        },
+        { name: "links", groups: ["links"] },
+        { name: "insert", groups: ["insert"] },
+        { name: "forms", groups: ["forms"] },
+        { name: "tools", groups: ["tools"] },
+        { name: "document", groups: ["mode", "document", "doctools"] },
+        { name: "others", groups: ["others"] },
+        "/",
+        { name: "basicstyles", groups: ["basicstyles", "cleanup"] },
+        {
+          name: "paragraph",
+          groups: ["list", "indent", "blocks", "align", "bidi", "paragraph"],
+        },
+        { name: "styles", groups: ["styles"] },
+        { name: "colors", groups: ["colors"] },
+        { name: "about", groups: ["about"] },
+      ],
+      removePlugins: "bidi,colorbutton,image",
+      removeButtons:
+        "Font,FontSize,Styles,Format,ShowBlocks,Iframe,PageBreak,Smiley,Flash,Language,JustifyBlock,JustifyRight,JustifyCenter,JustifyLeft,CreateDiv,CopyFormatting,ImageButton,Button,HiddenField,Select,Textarea,TextField,Radio,Checkbox,Form,BGColor,SelectAll,Replace,Find,Templates,Print,Preview,NewPage,Save,Subscript,Superscript,HorizontalRule,Unlink,Link,Scayt,Cut,Copy,Paste,PasteText,PasteFromWord,Maximize,NumberedList,BulletedList,Indent,Outdent,Blockquote,About,RemoveFormat,Strike",
+    };
+    if (this.display !== "inline") {
+      window.CKEDITOR.replace(this.id, config);
+    } else {
+      window.CKEDITOR.inline(this.id, config);
+    }
+    this.instance.on("change", () => {
+      let html = this.instance.getData();
+      if (html !== this.value) {
+        this.$emit("input", html);
+      }
+    });
+  },
+  beforeDestroy() {
+    if (this.instance) {
+      this.instance.focusManager.blur(true);
+      this.instance.destroy();
+    }
+  },
+};
+</script>
+<style>
+.ckeditor::after {
+  content: "";
+  display: table;
+  clear: both;
+}
+.cke_button__image {
+  display: none !important;
+}
+</style>

+ 59 - 0
src/modules/questions/component/reduplicate_mark.vue

@@ -0,0 +1,59 @@
+<template>
+  <svg
+    v-if="show"
+    class="icon reduplicate"
+    :style="{ fill: fillColor }"
+    width="50px"
+    height="26px"
+    viewBox="0 0 1984 1024"
+    version="1.1"
+    xmlns="http://www.w3.org/2000/svg"
+    @click="markCheck"
+  >
+    <path
+      d="M0 1024 0 0l1984 0 0 1024L0 1024zM57.6 966.4l1868.8 0L1926.4 57.6 57.6 57.6 57.6 966.4z"
+    />
+    <path
+      d="M329.6 796.8l0-32 275.2 0 0-54.4-233.6 0 0-35.2 233.6 0 0-57.6-211.2 0 0-208 211.2 0 0-54.4-272 0 0-32 272 0L604.8 268.8l-230.4 0L374.4 233.6l320 0c6.4 0 12.8 0 19.2 0 60.8 0 124.8-6.4 188.8-22.4l0 38.4c-51.2 12.8-108.8 19.2-169.6 19.2-6.4 0-16 0-22.4 0l-41.6 0 0 51.2 278.4 0 0 32L672 352l0 54.4 214.4 0L886.4 544c0 25.6-3.2 44.8-16 57.6-9.6 12.8-28.8 16-48 16-3.2 0-6.4 0-9.6 0l-137.6 0 0 57.6 236.8 0 0 35.2L672 710.4l0 54.4 278.4 0 0 32L329.6 796.8zM672 585.6l115.2 0c0 0 3.2 0 6.4 0 12.8 0 19.2-3.2 25.6-9.6 6.4-6.4 9.6-16 6.4-28.8l0-19.2L672 528 672 585.6zM454.4 585.6l153.6 0 0-57.6-153.6 0L454.4 585.6zM672 499.2l153.6 0 0-57.6L672 441.6 672 499.2zM454.4 499.2l153.6 0 0-57.6-153.6 0L454.4 499.2z"
+    />
+    <path
+      d="M1049.6 790.4c92.8-16 166.4-28.8 227.2-41.6l9.6-3.2L1280 739.2c-41.6-19.2-83.2-44.8-124.8-76.8l0 0-3.2 0c-28.8 12.8-57.6 22.4-92.8 28.8l0-32c76.8-28.8 134.4-67.2 166.4-115.2l3.2-3.2-105.6 0 0-201.6L1120 339.2c-28.8 12.8-54.4 22.4-76.8 28.8l0-35.2c67.2-32 112-76.8 131.2-134.4l67.2 0c-6.4 16-9.6 28.8-16 38.4l-3.2 3.2 425.6 0 0 38.4L1193.6 278.4l0 0c-12.8 19.2-32 32-54.4 44.8l-9.6 6.4 464 0 0 134.4c0 25.6-6.4 44.8-16 57.6-12.8 12.8-32 19.2-57.6 19.2-3.2 0-3.2 0-6.4 0l-217.6 0 0 3.2c-3.2 9.6-9.6 19.2-22.4 32l-12.8 12.8 323.2 0 0 35.2c-54.4 51.2-108.8 92.8-166.4 118.4l-6.4 3.2 6.4 3.2c32 9.6 89.6 19.2 169.6 28.8 25.6 6.4 44.8 9.6 60.8 9.6l0 32c-131.2-16-230.4-32-300.8-57.6l0 0 0 0c-67.2 22.4-166.4 41.6-300.8 60.8L1046.4 790.4zM1225.6 652.8c48 28.8 89.6 51.2 124.8 67.2l0 0 0 0c54.4-22.4 102.4-54.4 144-92.8l6.4-6.4-278.4 0 0 0c-9.6 9.6-22.4 19.2-35.2 22.4l0 6.4L1225.6 649.6zM1184 505.6l310.4 0c0 0 3.2 0 3.2 0 0 0 0 0 0 0 9.6 0 19.2-3.2 22.4-9.6 6.4-6.4 9.6-16 6.4-28.8l0-16L1184 451.2 1184 505.6zM1184 422.4l345.6 0 0-54.4L1184 368 1184 422.4z"
+    />
+  </svg>
+</template>
+<script>
+export default {
+  props: {
+    id: {
+      type: String,
+      default: "",
+    },
+    show: {
+      type: Boolean,
+      default: false,
+    },
+    fillColor: {
+      type: String,
+      default: "blue",
+    },
+    checked: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  methods: {
+    markCheck() {
+      this.$emit("reduplicate_mark_check", this.id, this.checked);
+    },
+  },
+};
+</script>
+
+<style lang="css">
+.icon.reduplicate {
+  position: absolute;
+  left: -30px;
+  top: 20px;
+  transform: rotate(-45deg);
+}
+</style>

+ 36 - 0
src/modules/questions/constants/constants.js

@@ -0,0 +1,36 @@
+export const LEVEL_TYPE = [
+  { label: "专升本", value: "ZSB" },
+  { label: "高起专", value: "GQZ" },
+  { label: "高起本", value: "GQB" },
+  { label: "不限", value: "ALL" },
+];
+export const QUESTION_TYPES = [
+  { value: "SINGLE_ANSWER_QUESTION", label: "单选" },
+  { value: "MULTIPLE_ANSWER_QUESTION", label: "多选" },
+  { value: "BOOL_ANSWER_QUESTION", label: "判断" },
+  { value: "FILL_BLANK_QUESTION", label: "填空" },
+  { value: "TEXT_ANSWER_QUESTION", label: "问答" },
+  { value: "NESTED_ANSWER_QUESTION", label: "套题" },
+];
+export const PUBLICITY_LIST = [
+  { label: "公开", value: true },
+  { label: "非公开", value: false },
+];
+export const EXAM_TYPES = [
+  { value: "TRADITION", label: "传统考试" },
+  { value: "ONLINE", label: "网络考试" },
+  { value: "PRACTICE", label: "在线练习" },
+  { value: "ONLINE_HOMEWORK", label: "在线作业" },
+];
+export const EXPORT_TYPES = [
+  { value: "ONLINE", label: "机考" },
+  { value: "NORMAL", label: "普通" },
+];
+
+/* 是否为空字符串(包含数字0时则返回true) */
+export function isEmptyStr(str) {
+  if (str == undefined || String(str).length < 1) {
+    return true;
+  }
+  return false;
+}

+ 30 - 0
src/modules/questions/directives/directives.js

@@ -0,0 +1,30 @@
+import Vue from "vue";
+import { QUESTION_API } from "@/constants/constants";
+Vue.directive("questionAudio", {
+  bind: function (el, binding, vnode) {
+    addAudio(el, binding, vnode);
+  },
+});
+function addAudio(el, binding, vnode) {
+  // console.log(el, binding, vnode);
+  var list = el.getElementsByTagName("a");
+  for (let obj of list) {
+    var questionAudioId = obj.getAttribute("id");
+    if (questionAudioId) {
+      console.log("come in");
+      vnode.context.$httpWithoutAuth
+        .get(QUESTION_API + "/questionAudio/" + questionAudioId)
+        .then((response) => {
+          if (response && response.data) {
+            let audioFlag =
+              '<audio controls><source src="' +
+              response.data.fileUrl +
+              '" type="audio/mpeg"></audio>';
+            var b = document.createElement("span");
+            b.innerHTML = audioFlag;
+            obj.append(b);
+          }
+        });
+    }
+  }
+}

+ 32 - 0
src/modules/questions/filters/filters.js

@@ -0,0 +1,32 @@
+import Vue from "vue";
+import {
+  QUESTION_TYPES,
+  EXAM_TYPES,
+  EXPORT_TYPES,
+} from "../constants/constants";
+//option字母顺序过滤器
+Vue.filter("optionOrderWordFilter", function (value) {
+  return String.fromCharCode(65 + value);
+});
+//题型过滤器
+Vue.filter("questionType", function (value) {
+  for (let questionType of QUESTION_TYPES) {
+    if (questionType.value === value) {
+      return questionType.label;
+    }
+  }
+});
+Vue.filter("examTypesFilter", function (value) {
+  for (let item of EXAM_TYPES) {
+    if (item.value === value) {
+      return item.label;
+    }
+  }
+});
+Vue.filter("exportTypesFilter", function (value) {
+  for (let item of EXPORT_TYPES) {
+    if (item.value === value) {
+      return item.label;
+    }
+  }
+});

+ 155 - 0
src/modules/questions/routes/routes.js

@@ -0,0 +1,155 @@
+import Home from "../../portal/views/home/Home.vue";
+import PaperStructure from "../views/PaperStructure.vue";
+import InsertPaperStructure from "../views/InsertPaperStructure.vue";
+import InsertPaperStructureInfo from "../views/InsertPaperStructureInfo.vue";
+import BluePaperStructure from "../views/BluePaperStructure.vue";
+import InsertBluePaperStructure from "../views/InsertBluePaperStructure.vue";
+import InsertBluePaperStructureInfo from "../views/InsertBluePaperStructureInfo.vue";
+import CourseProperty from "../views/CourseProperty.vue";
+import PropertyInfo from "../views/PropertyInfo.vue";
+import ImportPaper from "../views/ImportPaper.vue";
+import GenPaper from "../views/GenPaper.vue";
+import ImportPaperInfo from "../views/ImportPaperInfo.vue";
+import GenPaperDetail from "../views/GenPaperDetail.vue";
+import ExtractPaperRule from "../views/ExtractPaperRule.vue";
+import ExtractPaperInfo from "../views/ExtractPaperInfo.vue";
+import ExportStructure from "../views/ExportStructure.vue";
+import Question from "../views/Question.vue";
+import EditSelectQuestion from "../views/EditSelectQuestion.vue";
+import EditOtherQuestion from "../views/EditOtherQuestion.vue";
+import InsertPaperTitle from "../views/InsertPaperTitle.vue";
+import EditPaper from "../views/EditPaper.vue";
+import PreviewPaper from "../views/PreviewPaper.vue";
+import SelectQuestion from "../views/SelectQuestion.vue";
+import Tips from "../../portal/views/tips/Tips.vue";
+import ExportTemplate from "../views/ExportTemplate.vue";
+import School from "../views/School.vue";
+
+export default [
+  {
+    path: "/questions", //首页
+    meta: { auth: false },
+    component: Home,
+    children: [
+      {
+        path: "tips", //模块说明
+        component: Tips,
+      },
+      {
+        path: "school", //学校管理
+        meta: { privilegeCodes: "index_school" },
+        component: School,
+      },
+      {
+        path: "paper_structure/:isClear", //精确试卷结构列表
+        component: PaperStructure,
+      },
+      {
+        path: "insert_paper_structure/:id", //精确试卷结构(新增/修改)
+        component: InsertPaperStructure,
+      },
+      {
+        path: "insert_paper_structure_info/:id/:name/:detailId/:courseNo", //精确试卷结构添加题型结构
+        component: InsertPaperStructureInfo,
+      },
+      {
+        path: "blue_paper_structure/:isClear", //蓝图试卷结构列表
+        component: BluePaperStructure,
+      },
+      {
+        path: "insert_blue_paper_structure/:id", //蓝图试卷结构(新增/修改)
+        component: InsertBluePaperStructure,
+      },
+      {
+        path: "insert_blue_paper_structure_info/:id/:paperStructId", //蓝图试卷结构添加题型结构
+        component: InsertBluePaperStructureInfo,
+      },
+      {
+        path: "course_property/:isClear", //课程属性列表
+        component: CourseProperty,
+      },
+      {
+        path: "property_info/:id", //课程属性
+        component: PropertyInfo,
+      },
+      {
+        path: "import_paper/:isClear", //题库试卷列表
+        component: ImportPaper,
+      },
+      {
+        path: "gen_paper/:isClear", //卷库试卷列表
+        component: GenPaper,
+      },
+
+      {
+        path: "import_paper_info", //导入试卷页面
+        component: ImportPaperInfo,
+      },
+      {
+        path: "gen_paper_detail/:courseNo", //组卷页面
+        component: GenPaperDetail,
+      },
+      {
+        path: "extract_paper_rule/:isClear", //调卷规则列表
+        component: ExtractPaperRule,
+      },
+      {
+        path: "extract_paper_info_add", //调卷规则——新增
+        name: "extract_paper_info_add",
+        component: ExtractPaperInfo,
+      },
+      {
+        path: "extract_paper_info_edit/:extractConfigId", //调卷规则——修改
+        name: "extract_paper_info_edit",
+        component: ExtractPaperInfo,
+      },
+      {
+        path: "export_structure", //导出结构列表
+        component: ExportStructure,
+      },
+      {
+        path: "export_template", //导出模板设定
+        component: ExportTemplate,
+      },
+      {
+        path: "question_list/:isClear", //试题列表
+        component: Question,
+      },
+      {
+        path: "edit_select_question/:id", //编辑选择题(单选、多选)
+        component: EditSelectQuestion,
+      },
+      {
+        path: "edit_other_question/:id", //编辑其他题(填空、问答)
+        component: EditOtherQuestion,
+      },
+      {
+        path: "insert_paper_title", //添加试卷
+        component: InsertPaperTitle,
+      },
+      {
+        path:
+          "edit_other_question/:paperId/:paperDetailId/:questionType/:courseNo/:courseName",
+        component: EditOtherQuestion,
+      },
+      {
+        path:
+          "edit_select_question/:paperId/:paperDetailId/:questionType/:courseNo/:courseName",
+        component: EditSelectQuestion,
+      },
+    ],
+  },
+  {
+    path: "/edit_paper/:id/:parentView", //试卷编辑
+    component: EditPaper,
+  },
+  {
+    path:
+      "/select_question/:id/:courseNo/:courseName/:paperDetailId/:parentView", //试卷选题
+    component: SelectQuestion,
+  },
+  {
+    path: "/preview_paper/:paperId", //预览试卷
+    component: PreviewPaper,
+  },
+];

+ 109 - 0
src/modules/questions/styles/Common.css

@@ -0,0 +1,109 @@
+/* 检索框的长度 */
+.search_width {
+  width: 150px;
+}
+
+.search_width_80px {
+  width: 80px;
+}
+
+.form_width {
+  width: 200px;
+}
+
+/* 查询按钮与检索框对齐 */
+.search_down {
+  margin-top: 3.3px;
+}
+
+/* 操作按钮左移*/
+.operate_left {
+  margin-left: 2.6px;
+}
+
+.button_left {
+  margin-left: 10px;
+}
+
+.paper_title {
+  background-color: bisque;
+  width: 400px;
+  border-top-right-radius: 15px;
+  border-bottom-right-radius: 15px;
+}
+
+.row_header_word {
+  font-size: 14px;
+  font-weight: bold;
+}
+
+.row_quesBody {
+  cursor: pointer;
+  white-space: nowrap;
+  overflow: hidden;
+}
+
+.row_quesBody p {
+  font-size: 13px;
+}
+
+.dialog_input_width {
+  width: 200px;
+}
+
+.margin_top_10 {
+  margin-top: 10px;
+}
+
+.margin_left_120 {
+  margin-left: 120px;
+}
+
+.margin_left_10 {
+  margin-left: 10px;
+}
+
+.error_message {
+  color: #f56c6c;
+  font-size: 12px;
+  line-height: 1;
+  padding-top: 4px;
+}
+
+.error_other {
+  position: relative;
+  top: auto;
+  left: auto;
+  display: inline-block;
+  margin-left: 10px;
+}
+
+.check_remark {
+  content: "*";
+  color: #f56c6c;
+  margin-right: 4px;
+  box-sizing: border-box;
+}
+
+.form_font_size {
+  margin-left: 15px;
+  padding-top: 7px;
+}
+
+.margin_left_30 {
+  margin-left: 30%;
+}
+
+.margin_left_40 {
+  margin-left: 40%;
+}
+
+.font_length {
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+
+.message_style {
+  color: #f56c6c;
+  margin-bottom: -20px;
+}

+ 155 - 0
src/modules/questions/styles/EditPaper.css

@@ -0,0 +1,155 @@
+.red {
+  color: red;
+}
+.center {
+  margin: 0 auto 0 auto;
+  text-align: center;
+}
+h1 {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  font-weight: bold;
+}
+
+h3 {
+  font-size: 33px;
+  font-family: "微软雅黑", serif;
+  font-weight: bold;
+}
+h4 {
+  font-size: 21px;
+  font-family: "微软雅黑", serif;
+}
+span > p {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  text-align: left;
+  display: block;
+}
+.ques-title {
+  margin-right: 10px;
+}
+.mainQues {
+  margin-left: 3.5%;
+  text-align: left;
+}
+.mainQuesTitle {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  font-weight: bold;
+  margin-bottom: 20px;
+}
+.ques {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  text-align: left;
+  padding-left: 10px;
+  position: relative;
+}
+.subQues {
+  position: relative;
+  padding-left: 10px;
+}
+.quesBody {
+  display: flex;
+}
+.quesOption {
+  margin-top: 10px;
+  display: flex;
+}
+.quesSelect {
+  border: solid 1px #fff;
+}
+.quesSelect:hover {
+  border: solid 1px #99a9bf;
+}
+.text-left {
+  margin-left: 3.5%;
+  text-align: left;
+}
+
+.paper-title {
+  margin-right: 10px;
+  margin-bottom: 10px;
+}
+
+small {
+  font-size: 15px;
+  font-weight: bold;
+  margin-left: 10px;
+  margin-top: 20px;
+}
+.titlefont {
+  font-size: 14px;
+}
+.paper {
+  margin: 0 auto;
+  background-color: white;
+  padding-left: 10%;
+  padding-right: 10%;
+}
+.btnDiv {
+  height: 30px;
+  text-align: right;
+  position: relative;
+  right: 2px;
+  top: 1px;
+  visibility: hidden;
+}
+textarea {
+  width: 200px;
+}
+.paperName {
+  margin-top: 30px;
+  text-align: center;
+}
+.showContentBtn {
+  display: none;
+}
+.score-span {
+  width: 220px;
+}
+.edit-paper-top {
+  background: #eff2f7;
+  color: #000;
+  position: fixed;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  height: 60px;
+  line-height: 60px;
+  z-index: 100;
+  text-align: center;
+}
+.edit-paper-top-inline {
+  margin-left: 6%;
+}
+.paper-top-div {
+  float: left;
+  margin-left: 5px;
+  padding-left: 5px;
+}
+.paper-top-title {
+  font-size: 16px;
+  font-weight: bold;
+}
+.paper-top-value {
+  font-size: 16px;
+  padding: 3px 10px;
+}
+.paperName-input {
+  background-color: #eff2f7;
+  border: 1px solid #000;
+  border-top: 0;
+  border-left: 0;
+  border-right: 0;
+  box-sizing: border-box;
+  color: #1f2d3d;
+  font-size: inherit;
+  height: 20px;
+  padding: 2px 10px;
+  transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
+  outline: none;
+  width: 110px;
+}

+ 390 - 0
src/modules/questions/views/BluePaperStructure.vue

@@ -0,0 +1,390 @@
+<template>
+  <section class="content">
+    <div v-show="isClear == 1">
+      <LinkTitlesCustom :current-paths="['基础信息', '蓝图试卷结构']" />
+    </div>
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="结构名称">
+              <el-input
+                v-model="formSearch.name"
+                class="search_width"
+                placeholder="请输入结构名称"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="制定课程">
+              <el-select
+                v-model="formSearch.courseNo"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                placeholder="请选择"
+                size="small"
+                @focus="(e) => getCourses(e.target.value)"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseNo"
+                  :label="item.courseInfo"
+                  :value="item.courseNo"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" type="primary" @click="insertStruct"
+                ><i class="el-icon-plus"></i> 新增</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+        <div
+          style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+        ></div>
+        <el-row>
+          <el-form-item>
+            <span>批量操作:</span>
+            <el-button
+              size="small"
+              type="danger"
+              :disabled="noBatchSelected"
+              @click="deleteStructs"
+            >
+              <i class="el-icon-delete"></i> 删除
+            </el-button>
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div style="width: 100%; margin-bottom: 10px"></div>
+      <!-- 页面列表 -->
+      <el-table
+        v-loading="loading"
+        :data="tableData"
+        element-loading-text="拼命加载中"
+        border
+        style="width: 100%; text-align: center"
+        @selection-change="selectionChange"
+      >
+        <el-table-column type="selection" width="40"></el-table-column>
+        <el-table-column label="蓝图试卷结构名称">
+          <template slot-scope="scope">
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="关联课程" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程代码" width="90">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseNo }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="类型" width="80">
+          <template slot-scope="scope">
+            <span>{{ getType(scope.row.genPaperType) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="大题数" width="88" sortable prop="detailCount">
+        </el-table-column>
+        <el-table-column label="难度" width="95">
+          <template slot-scope="scope">
+            <span>{{ getDifficulty(scope.row.difficulty) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="总分" width="78" sortable prop="totalScore">
+        </el-table-column>
+        <el-table-column label="操作" width="175">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="editStruct(scope.row)"
+                ><i class="el-icon-edit"></i>编辑</el-button
+              >
+              <el-button
+                size="mini"
+                type="danger"
+                @click="deleteStruct(scope.row)"
+                ><i class="el-icon-delete"></i> 删除</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </section>
+</template>
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      formSearch: {
+        name: "",
+        courseNo: "",
+        type: "BLUEPRINT",
+        courseName: "",
+      },
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      courseList: [],
+      loading: false,
+      tableData: [],
+      selectedList: [],
+      isClear: 0,
+    };
+  },
+  computed: {
+    selectedIds() {
+      var selectedIdsStr = "";
+      for (let id of this.selectedList) {
+        if (!selectedIdsStr) {
+          selectedIdsStr += id;
+        } else {
+          selectedIdsStr += "," + id;
+        }
+      }
+      return selectedIdsStr;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+    noBatchSelected() {
+      return this.selectedList.length === 0;
+    },
+  },
+  watch: {
+    $route: "initVue",
+  },
+  created() {
+    this.initVue();
+    this.removeItem();
+  },
+  methods: {
+    //新增
+    insertStruct() {
+      sessionStorage.setItem(
+        "blue_paper_stucture",
+        JSON.stringify(this.formSearch)
+      );
+      sessionStorage.setItem(
+        "blue_paper_stucture_currentPage",
+        this.currentPage
+      );
+      this.$router.push({
+        path: "/questions/insert_blue_paper_structure/add",
+      });
+    },
+    //分页
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchAll();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchAll();
+    },
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchAll();
+    },
+    //查询所有
+    searchAll() {
+      var pageNo = Number(this.currentPage);
+      this.currentPage = 1;
+      this.loading = true;
+      var url = QUESTION_API + "/paperStruct/" + pageNo + "/" + this.pageSize;
+      this.$http.get(url, { params: this.formSearch }).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.currentPage = pageNo;
+      });
+      this.loading = false;
+    },
+    //删除多个试卷结构
+    deleteStructs() {
+      this.$confirm("是否删除试卷结构?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "error",
+      }).then(() => {
+        this.loading = true;
+        var url = QUESTION_API + "/paperStruct/" + this.selectedIds;
+        this.$http
+          .delete(url)
+          .then(() => {
+            this.$notify({
+              type: "success",
+              message: "删除成功!",
+            });
+            this.searchAll();
+            this.selectedList = [];
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "删除失败!",
+            });
+            this.selectedList = [];
+          });
+      });
+      this.loading = false;
+    },
+    //得到难度
+    getDifficulty(val) {
+      if (parseFloat(val) >= 0.8 && parseFloat(val) <= 1.0) {
+        return val + "(易)";
+      }
+      if (parseFloat(val) >= 0.4 && parseFloat(val) <= 0.7) {
+        return val + "(中)";
+      } else {
+        return val + "(难)";
+      }
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.formSearch.courseName = course.name;
+        }
+      }
+    },
+    //修改
+    editStruct(row) {
+      this.getCourseName(this.formSearch.courseNo);
+      sessionStorage.setItem(
+        "blue_paper_stucture",
+        JSON.stringify(this.formSearch)
+      );
+      sessionStorage.setItem(
+        "blue_paper_stucture_currentPage",
+        this.currentPage
+      );
+      sessionStorage.setItem("blueStruct", JSON.stringify(row));
+      this.$router.push({
+        path: "/questions/insert_blue_paper_structure/" + row.id,
+      });
+    },
+    deleteStruct(row) {
+      this.$confirm("是否删除试卷结构?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http
+          .delete(QUESTION_API + "/paperStruct/" + row.id)
+          .then(() => {
+            this.$notify({
+              message: "删除成功",
+              type: "success",
+            });
+            this.searchAll();
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "删除失败",
+            });
+          });
+      });
+      this.loading = false;
+    },
+    //清理缓存试卷结构
+    removeItem() {
+      sessionStorage.removeItem("blueStruct");
+    },
+    //全选
+    selectionChange(val) {
+      this.selectedList = [];
+      var selectedList = this.selectedList;
+      val.forEach((element) => {
+        selectedList.push(element.id);
+      });
+      this.selectedList = selectedList;
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+        });
+    },
+    getType(val) {
+      if (val == "ENSEMBLE") {
+        return "宏观结构";
+      }
+      return "微观结构";
+    },
+    initVue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        sessionStorage.removeItem("blue_paper_stucture");
+        sessionStorage.removeItem("blue_paper_stucture_currentPage");
+        this.formSearch = {
+          name: "",
+          courseNo: "",
+          type: "BLUEPRINT",
+          courseName: "",
+        };
+        this.currentPage = 1;
+      } else {
+        this.formSearch = JSON.parse(
+          sessionStorage.getItem("blue_paper_stucture")
+        );
+        this.currentPage = parseInt(
+          sessionStorage.getItem("blue_paper_stucture_currentPage")
+        );
+      }
+      if (this.formSearch.courseName) {
+        this.getCourses(this.formSearch.courseName);
+      }
+      this.searchAll();
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 560 - 0
src/modules/questions/views/CourseProperty.vue

@@ -0,0 +1,560 @@
+<template>
+  <section class="content">
+    <div v-show="isClear == 1">
+      <LinkTitlesCustom :current-paths="['基础信息', '课程属性预设']" />
+    </div>
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="属性名称">
+              <el-input
+                v-model="formSearch.name"
+                class="search_width"
+                placeholder="请输入属性名称"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="课程名称">
+              <el-select
+                v-model="formSearch.courseId"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                placeholder="请选择"
+                size="small"
+                @focus="(e) => getCourses(e.target.value)"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseId"
+                  :label="item.courseInfo"
+                  :value="item.courseId"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" type="primary" @click="addCourseProperty"
+                ><i class="el-icon-plus"></i> 新增</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+        <div
+          style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+        ></div>
+        <el-row>
+          <el-form-item class="pull-left">
+            <span>批量操作:</span>
+            <el-button
+              size="small"
+              type="success"
+              :disabled="noBatchSelected"
+              @click="openCoursePropertys"
+              ><i class="el-icon-check"></i> 启用</el-button
+            >
+            <el-button
+              size="small"
+              type="danger"
+              :disabled="noBatchSelected"
+              @click="closeCoursePropertys"
+              ><i class="el-icon-close"></i> 禁用</el-button
+            >
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div style="width: 100%; margin-bottom: 10px"></div>
+      <!-- 页面列表 -->
+      <el-table
+        v-loading="loading"
+        :data="tableData"
+        element-loading-text="拼命加载中"
+        border
+        style="width: 100%"
+        @selection-change="selectionChange"
+      >
+        <el-table-column type="selection" width="40"></el-table-column>
+        <el-table-column label="课程名称">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="属性名称">
+          <template slot-scope="scope">
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态">
+          <template slot-scope="scope">
+            <span v-if="scope.row.enable">
+              <el-tooltip
+                class="item"
+                effect="dark"
+                content="启用"
+                placement="left"
+              >
+                <i class="el-icon-success" style="color: green"></i>
+              </el-tooltip>
+            </span>
+            <span v-else>
+              <el-tooltip
+                class="item"
+                effect="dark"
+                content="禁用"
+                placement="left"
+              >
+                <i class="el-icon-error" style="color: red"></i>
+              </el-tooltip>
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="260">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="editCourseProperty(scope.row)"
+                ><i class="el-icon-zoom-in"></i> 详情
+              </el-button>
+              <el-button
+                v-if="!scope.row.enable"
+                size="mini"
+                type="primary"
+                plain
+                @click="openCourseProperty(scope.row)"
+              >
+                <i class="el-icon-check" aria-hidden="true"></i>启用
+              </el-button>
+              <el-button
+                v-if="scope.row.enable"
+                size="mini"
+                type="danger"
+                @click="closeCourseProperty(scope.row)"
+              >
+                <i class="el-icon-close" aria-hidden="true"></i>禁用
+              </el-button>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page.sync="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <el-dialog
+      title="新增课程属性名称"
+      :visible.sync="coursePropertyDialog"
+      width="500px"
+    >
+      <el-form
+        ref="coursePropertyForm"
+        :model="coursePropertyForm"
+        :rules="rules"
+        label-position="right"
+        label-width="90px"
+        inline-message
+      >
+        <el-row>
+          <el-form-item label="属性名称" label-width="120px" prop="name">
+            <el-input
+              v-model="coursePropertyForm.name"
+              class="dialog_input_width"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="课程名称" label-width="120px" prop="courseId">
+            <el-select
+              v-model="coursePropertyForm.courseId"
+              class="dialog_input_width"
+              filterable
+              :remote-method="getCourses"
+              remote
+              clearable
+              placeholder="请选择课程"
+              @focus="(e) => getCourses(e.target.value)"
+            >
+              <el-option
+                v-for="item in courseInfoSelect"
+                :key="item.courseId"
+                :label="item.courseInfo"
+                :value="item.courseId"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button type="primary" @click="submit('coursePropertyForm')"
+            >保 存</el-button
+          >
+          <el-button @click="resetForm('coursePropertyForm')"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          >
+          <el-button type="primary" @click="back('coursePropertyForm')"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </section>
+</template>
+
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      formSearch: {
+        name: "",
+        courseId: "",
+      },
+      courseList: [],
+      loading: false,
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      coursePropertyForm: {
+        name: "",
+        courseId: "",
+        courseCode: "",
+      },
+      coursePropertyDialog: false,
+      selectedList: [],
+      rules: {
+        name: [{ required: true, message: "请输入属性名称", trigger: "blur" }],
+        courseId: [
+          { required: true, message: "请选择课程名称", trigger: "change" },
+        ],
+      },
+    };
+  },
+  computed: {
+    selectedIds() {
+      var selectedIdsStr = "";
+      for (let id of this.selectedList) {
+        if (!selectedIdsStr) {
+          selectedIdsStr += id;
+        } else {
+          selectedIdsStr += "," + id;
+        }
+      }
+      return selectedIdsStr;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (var i = 0; i < this.courseList.length; i++) {
+        var courseInfo = {
+          courseInfo:
+            this.courseList[i].name + "(" + this.courseList[i].code + ")",
+          courseId: this.courseList[i].id,
+        };
+        courseList.push(courseInfo);
+      }
+      return courseList;
+    },
+    noBatchSelected() {
+      return this.selectedList.length === 0;
+    },
+  },
+  watch: {
+    $route: "initValue",
+  },
+  created() {
+    this.initValue();
+  },
+
+  methods: {
+    //查询所有课程属性
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchCourProperty();
+    },
+    searchCourProperty() {
+      var pageNo = Number(this.currentPage);
+      this.loading = true;
+      var url =
+        QUESTION_API +
+        "/courseProperty/all/" +
+        this.currentPage +
+        "/" +
+        this.pageSize;
+      this.$http.get(url, { params: this.formSearch }).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.currentPage = pageNo;
+        this.loading = false;
+      });
+    },
+    //新增
+    addCourseProperty() {
+      this.coursePropertyDialog = true;
+      this.coursePropertyForm.name = "";
+      this.coursePropertyForm.courseId = "";
+    },
+    //重置
+    resetForm(formData) {
+      this.coursePropertyForm.name = "";
+      this.coursePropertyForm.courseId = "";
+      this.$refs[formData].clearValidate();
+    },
+    //返回
+    back(formData) {
+      this.resetForm(formData);
+      this.coursePropertyDialog = false;
+    },
+    //修改
+    editCourseProperty(row) {
+      sessionStorage.setItem("course_property_name", this.formSearch.name);
+      sessionStorage.setItem(
+        "course_property_courseId",
+        this.formSearch.courseId
+      );
+      sessionStorage.setItem("course_property_currentPage", this.currentPage);
+      sessionStorage.setItem("courseProperty", JSON.stringify(row));
+      this.$router.push({
+        path: "/questions/property_info/" + row.id,
+      });
+    },
+    //开启
+    openCourseProperty(row) {
+      this.$confirm("确认开启?", "提示", {
+        type: "success",
+      }).then(() => {
+        this.loading = true;
+        this.$http
+          .put(QUESTION_API + "/courseProperty/open/" + row.id)
+          .then(() => {
+            this.$notify({
+              message: "开启成功",
+              type: "success",
+            });
+            this.searchCourProperty();
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "开启失败",
+            });
+          });
+      });
+    },
+    //批量启用
+    openCoursePropertys() {
+      this.$confirm("是否确认开启?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "error",
+      }).then(() => {
+        this.loading = true;
+        var url = QUESTION_API + "/courseProperty/opens/" + this.selectedIds;
+        this.$http
+          .put(url)
+          .then(() => {
+            this.$notify({
+              type: "success",
+              message: "开启成功!",
+            });
+            this.searchCourProperty();
+            this.selectedList = [];
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "开启失败!",
+            });
+            this.selectedList = [];
+          });
+      });
+      this.loading = false;
+    },
+    //关闭
+    closeCourseProperty(row) {
+      this.$confirm("确认禁用?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http
+          .put(QUESTION_API + "/courseProperty/close/" + row.id)
+          .then(() => {
+            this.$notify({
+              message: "禁用成功",
+              type: "success",
+            });
+            this.searchCourProperty();
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "禁用失败",
+            });
+          });
+      });
+    },
+    //批量关闭
+    closeCoursePropertys() {
+      this.$confirm("是否确认禁用?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "error",
+      }).then(() => {
+        this.loading = true;
+        var url = QUESTION_API + "/courseProperty/closes/" + this.selectedIds;
+        this.$http
+          .put(url)
+          .then(() => {
+            this.$notify({
+              type: "success",
+              message: "禁用成功!",
+            });
+            this.searchCourProperty();
+            this.selectedList = [];
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "禁用失败!",
+            });
+            this.selectedList = [];
+          });
+      });
+      this.loading = false;
+    },
+    //分页
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchCourProperty();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchCourProperty();
+    },
+    //确定
+    submit(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          for (let course of this.courseList) {
+            if (course.id == this.coursePropertyForm.courseId) {
+              this.coursePropertyForm.courseCode = course.code;
+            }
+          }
+          this.$http
+            .post(
+              QUESTION_API + "/courseProperty/save",
+              this.coursePropertyForm
+            )
+            .then(() => {
+              this.$notify({
+                message: "新增成功",
+                type: "success",
+              });
+              this.coursePropertyDialog = false;
+              this.searchCourProperty();
+            })
+            .catch((error) => {
+              this.$notify({
+                type: "error",
+                message: error.response.data.desc,
+              });
+            });
+        } else {
+          return false;
+        }
+      });
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+        });
+    },
+    removeItem() {
+      sessionStorage.removeItem("course_property_name");
+      sessionStorage.removeItem("course_property_courseId");
+      sessionStorage.removeItem("course_property_currentPage");
+    },
+    //页面回填值
+    initValue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        this.removeItem();
+        this.formSearch = {
+          name: "",
+          courseId: "",
+        };
+      } else {
+        this.formSearch.name =
+          sessionStorage.getItem("course_property_name") == "null"
+            ? ""
+            : sessionStorage.getItem("course_property_name");
+        this.formSearch.courseId =
+          sessionStorage.getItem("course_property_courseId") == ""
+            ? ""
+            : parseInt(sessionStorage.getItem("course_property_courseId"));
+        this.currentPage =
+          sessionStorage.getItem("course_property_currentPage") == null
+            ? 1
+            : parseInt(sessionStorage.getItem("course_property_currentPage"));
+      }
+      this.getOneCourse(this.formSearch.courseId);
+      this.handleCurrentChange(this.currentPage);
+    },
+    //查询单个课程
+    getOneCourse(courseId) {
+      if (courseId) {
+        this.$http.get(CORE_API + "/course/" + courseId).then((response) => {
+          this.courseList.push(response.data);
+        });
+      } else {
+        this.courseList = [];
+      }
+    },
+    //全选
+    selectionChange(val) {
+      this.selectedList = [];
+      var selectedList = this.selectedList;
+      val.forEach((element) => {
+        selectedList.push(element.id);
+      });
+      this.selectedList = selectedList;
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 537 - 0
src/modules/questions/views/EditOtherQuestion.vue

@@ -0,0 +1,537 @@
+<!-- 编辑填空,问答,判断题 -->
+<template>
+  <div id="editOtherApp">
+    <section v-loading="fullscreenLoading" class="content">
+      <h3 class="box-title">
+        <span v-if="!quesModel.id"
+          ><LinkTitlesCustom :current-paths="['试题管理', '试题新增']"
+        /></span>
+        <span v-if="quesModel.id"
+          ><LinkTitlesCustom :current-paths="['试题管理', '试题修改']"
+        /></span>
+      </h3>
+      <div class="box-body">
+        <el-form
+          ref="quesModel"
+          :model="quesModel"
+          :rules="rules"
+          label-position="right"
+          label-width="80px"
+        >
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="题型">
+                <el-select
+                  v-model="quesModel.questionType"
+                  :disabled="true"
+                  placeholder="请输入题型"
+                >
+                  <el-option
+                    v-for="item in questionTypes"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!-- created by weiwenhai -->
+          <el-form-item label="难度">
+            <el-select
+              v-model="quesModel.difficultyDegree"
+              placeholder="请输入难度"
+            >
+              <el-option
+                v-for="item in difficultyDegreeList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="公开度">
+            <el-select v-model="quesModel.publicity" placeholder="请输入公开度">
+              <el-option
+                v-for="item in publicityList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item
+            v-if="quesModel.questionType == 'TEXT_ANSWER_QUESTION'"
+            label="作答类型"
+          >
+            <el-select v-model="quesModel.answerType">
+              <el-option
+                v-for="item in answerTypes"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="属性列表">
+            <el-tooltip
+              v-for="(content, index) in quesModel.quesProperties"
+              :key="index"
+              placement="top"
+            >
+              <div slot="content">
+                <span v-if="content.firstProperty != null"
+                  >一级属性:{{ content.firstProperty.name }}</span
+                ><br />
+                <span v-if="content.secondProperty != null"
+                  >二级属性:{{ content.secondProperty.name }}</span
+                >
+              </div>
+              <el-tag
+                :key="content.id"
+                style="margin-right: 5px"
+                closable
+                type="primary"
+                @close="handleClose(content)"
+              >
+                {{ content.coursePropertyName }}
+              </el-tag>
+            </el-tooltip>
+          </el-form-item>
+          <el-row :gutter="20">
+            <el-col :xs="6" :sm="6" :md="6" :lg="6">
+              <el-form-item label="属性名">
+                <el-select
+                  v-model="coursePropertyName"
+                  placeholder="属性名"
+                  class="property_with"
+                  @change="searchFirst"
+                >
+                  <el-option
+                    v-for="item in coursePropertyList"
+                    :key="item.name"
+                    :label="item.name"
+                    :value="item.name"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="6" :sm="6" :md="6" :lg="6">
+              <el-form-item label="一级">
+                <el-select
+                  v-model="firstPropertyId"
+                  placeholder="一级"
+                  class="property_with"
+                  @change="searchSecond"
+                >
+                  <el-option
+                    v-for="item in firstPropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="6" :sm="6" :md="6" :lg="6">
+              <el-form-item label="二级">
+                <el-select
+                  v-model="secondPropertyId"
+                  placeholder="二级"
+                  class="property_with"
+                >
+                  <el-option
+                    v-for="item in secondPropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="3" :sm="3" :md="3" :lg="3">
+              <el-form-item>
+                <el-button
+                  type="primary"
+                  style="margin-left: -30px"
+                  @click="insertProperty"
+                  ><i class="el-icon-plus"></i> 新增属性</el-button
+                >
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!-- end -->
+          <el-row :gutter="10">
+            <el-col :xs="30" :sm="30" :md="30" :lg="30">
+              <el-form-item label="题干" prop="quesBody">
+                <ckeditor v-model="quesModel.quesBody"></ckeditor>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <el-row
+            v-for="quesOption in quesModel.quesOptions"
+            :key="quesOption"
+            :gutter="10"
+          >
+            <el-col :xs="30" :sm="30" :md="30" :lg="30">
+              <el-form-item :label="quesOption.number">
+                <ckeditor v-model="quesOption.quesBody"></ckeditor>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <!-- 非套题 -->
+          <div
+            v-if="
+              quesModel.questionType !== 'NESTED_ANSWER_QUESTION' &&
+              quesModel.questionType !== 'BOOL_ANSWER_QUESTION'
+            "
+          >
+            <el-form-item label="答案" prop="quesAnswer">
+              <ckeditor v-model="quesModel.quesAnswer"></ckeditor>
+            </el-form-item>
+          </div>
+          <div v-if="quesModel.questionType == 'BOOL_ANSWER_QUESTION'">
+            <el-row>
+              <el-col>
+                <el-form-item label="答案" prop="quesAnswer">
+                  <el-select
+                    v-model="quesModel.quesAnswer"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="op in options"
+                      :key="op"
+                      :label="op"
+                      :value="op"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </div>
+          <div class="paper-top">
+            <el-form-item>
+              <el-button type="primary" @click="saveQues('quesModel')"
+                >保存</el-button
+              >
+              <el-button
+                type="primary"
+                icon="caret-left"
+                @click="backToQuesList()"
+                >返回列表</el-button
+              >
+            </el-form-item>
+          </div>
+        </el-form>
+      </div>
+    </section>
+  </div>
+</template>
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { isEmptyStr, QUESTION_TYPES } from "../constants/constants";
+import ckeditor from "../component/ckeditor.vue";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+
+export default {
+  name: "EditOtherApp",
+  components: { ckeditor, LinkTitlesCustom },
+  data() {
+    return {
+      fullscreenLoading: false,
+      questionTypes: QUESTION_TYPES,
+      courseName: "",
+      courseNo: "",
+      paperDetailId: "",
+      paperId: "",
+      quesModel: {
+        courseName: "",
+        courseNo: "",
+        quesAnswer: "",
+        quesBody: "",
+        questionType: "",
+        difficultyDegree: "",
+        publicity: true,
+        answerType: "",
+        quesProperties: [],
+        score: 0,
+      },
+      options: ["正确", "错误"],
+      difficultyDegreeList: [
+        { label: 0.1, value: 0.1 },
+        { label: 0.2, value: 0.2 },
+        { label: 0.3, value: 0.3 },
+        { label: 0.4, value: 0.4 },
+        { label: 0.5, value: 0.5 },
+        { label: 0.6, value: 0.6 },
+        { label: 0.7, value: 0.7 },
+        { label: 0.8, value: 0.8 },
+        { label: 0.9, value: 0.9 },
+        { label: 1.0, value: 1.0 },
+      ],
+      publicityList: [
+        { label: "公开", value: true },
+        { label: "非公开", value: false },
+      ],
+      answerTypes: [
+        { label: "文本", value: "DIVERSIFIED_TEXT" },
+        { label: "音频", value: "SINGLE_AUDIO" },
+      ],
+      coursePropertyList: [],
+      coursePropertyName: "", //课程属性名
+      firstPropertyList: [], //一级属性集合
+      firstPropertyId: "", //一级属性id
+      secondPropertyList: [], //二级属性集合
+      secondPropertyId: "", //二级属性id
+      //验证
+      rules: {
+        // quesBody: [
+        //     {required: true, message: '请输入题干', trigger: 'blur'}
+        // ],
+        quesAnswer: [
+          { required: true, message: "请输入答案", trigger: "blur" },
+        ],
+      },
+    };
+  },
+  computed: {},
+  created() {
+    this.paperId = this.$route.params.paperId;
+    this.paperDetailId = this.$route.params.paperDetailId;
+    this.courseName = this.$route.params.courseName;
+    this.courseNo = this.$route.params.courseNo;
+    let questionType = this.$route.params.questionType;
+    if (questionType) {
+      this.quesModel.questionType = questionType;
+    }
+    this.questionId = this.$route.params.id;
+    if (this.questionId) {
+      this.quesModel["id"] = this.questionId;
+      this.getQues(this.questionId);
+    }
+    if (this.courseNo) {
+      this.$http
+        .get(QUESTION_API + "/courseProperty/enable/" + this.courseNo)
+        .then((response) => {
+          this.coursePropertyList = response.data;
+        });
+    }
+
+    if (isEmptyStr(this.quesModel.answerType)) {
+      this.quesModel.answerType = "DIVERSIFIED_TEXT";
+    }
+  },
+  mounted() {},
+  methods: {
+    saveQues(formName) {
+      if (!this.quesModel.difficultyDegree) {
+        this.$notify({
+          message: "请选择试题难度",
+          type: "error",
+        });
+        return false;
+      }
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          if (this.questionId) {
+            this.fullscreenLoading = true;
+            //修改逻辑
+            this.$http
+              .put(QUESTION_API + "/question", this.quesModel)
+              .then(() => {
+                this.$notify({
+                  message: "保存成功",
+                  type: "success",
+                });
+                this.fullscreenLoading = false;
+              });
+          } else {
+            //新增逻辑
+            this.quesModel.courseNo = this.courseNo;
+            this.quesModel.courseName = this.courseName;
+            this.fullscreenLoading = true;
+            this.$http
+              .post(
+                QUESTION_API +
+                  "/paper/addQuestion/" +
+                  this.paperId +
+                  "/" +
+                  this.paperDetailId,
+                this.quesModel
+              )
+              .then(() => {
+                this.fullscreenLoading = false;
+                this.$notify({
+                  type: "success",
+                  message: `保存成功`,
+                });
+                this.$router.push({ path: "/questions/question_list/0" });
+              })
+              .catch(() => {
+                this.fullscreenLoading = false;
+              });
+          }
+        } else {
+          return false;
+        }
+      });
+    },
+    getQues(id) {
+      this.$http.get(QUESTION_API + "/question/" + id).then((response) => {
+        this.quesModel = response.data;
+
+        if (isEmptyStr(this.quesModel.answerType)) {
+          this.quesModel.answerType = "DIVERSIFIED_TEXT";
+        }
+
+        this.initCourseProperty();
+      });
+    },
+    backToQuesList() {
+      this.$router.push({
+        path: "/questions/question_list/1",
+      });
+    },
+    //查询所有课程属性名
+    initCourseProperty() {
+      var code = this.quesModel.course.code;
+      this.$http
+        .get(QUESTION_API + "/courseProperty/code/" + code)
+        .then((response) => {
+          this.coursePropertyList = response.data;
+        });
+    },
+    //查询一级属性
+    searchFirst() {
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.secondPropertyList = [];
+      for (let courseProperty of this.coursePropertyList) {
+        if (courseProperty.name == this.coursePropertyName) {
+          this.$http
+            .get(QUESTION_API + "/property/first/" + courseProperty.id)
+            .then((response) => {
+              this.firstPropertyList = response.data;
+            });
+        }
+      }
+    },
+    //查询二级属性
+    searchSecond() {
+      this.secondPropertyId = "";
+      this.$http
+        .get(QUESTION_API + "/property/second/" + this.firstPropertyId)
+        .then((response) => {
+          this.secondPropertyList = response.data;
+        });
+    },
+    //新增属性
+    insertProperty() {
+      if (!this.checkInsertPro()) {
+        return false;
+      }
+      var quesProperty = {
+        id: "",
+        coursePropertyName: "",
+        firstProperty: {},
+        secondProperty: {},
+      };
+      if (
+        this.quesModel.quesProperties === undefined ||
+        this.quesModel.quesProperties === null ||
+        this.quesModel.quesProperties.length == 0
+      ) {
+        this.quesModel.quesProperties = [];
+      }
+      quesProperty.id =
+        this.coursePropertyName +
+        "-" +
+        this.firstPropertyId +
+        "-" +
+        this.secondPropertyId;
+      for (let quesPro of this.quesModel.quesProperties) {
+        if (quesPro.id == quesProperty.id) {
+          this.$notify({
+            message: "该属性已存在,请重新选择",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      quesProperty.coursePropertyName = this.coursePropertyName;
+      //取到一级属性对象
+      for (let property of this.firstPropertyList) {
+        if (property.id == this.firstPropertyId) {
+          quesProperty.firstProperty = property;
+        }
+      }
+      //判断是否有二级属性
+      if (
+        this.secondPropertyList != undefined &&
+        this.secondPropertyList.length > 0
+      ) {
+        if (!this.secondPropertyId) {
+          this.$notify({
+            message: "请选择二级属性",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      //取到二级属性对象
+      for (let property of this.secondPropertyList) {
+        if (property.id == this.secondPropertyId) {
+          quesProperty.secondProperty = property;
+        }
+      }
+      this.quesModel.quesProperties.push(quesProperty);
+      this.quesModel = Object.assign({}, this.quesModel);
+      //清空下拉框
+      this.coursePropertyName = "";
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.firstPropertyList = [];
+      this.secondPropertyList = [];
+    },
+    //删除属性
+    handleClose(tag) {
+      this.quesModel.quesProperties.splice(
+        this.quesModel.quesProperties.indexOf(tag),
+        1
+      );
+      this.quesModel = Object.assign({}, this.quesModel);
+    },
+    //新增属性验证
+    checkInsertPro() {
+      if (!this.coursePropertyName) {
+        this.$notify({
+          message: "请选择属性",
+          type: "error",
+        });
+        return false;
+      }
+      if (!this.firstPropertyId) {
+        this.$notify({
+          message: "请选择一级属性",
+          type: "error",
+        });
+        return false;
+      }
+      return true;
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 2062 - 0
src/modules/questions/views/EditPaper.vue

@@ -0,0 +1,2062 @@
+<template>
+  <div
+    id="editPaperApp"
+    v-loading="loading"
+    class="paper"
+    element-loading-text="拼命加载中。。。"
+  >
+    <!-- <ckeditor v-model="examRemark"></ckeditor> -->
+    <div class="edit-paper-top">
+      <div class="edit-paper-top-inline">
+        <div class="paper-top-div">
+          <span class="paper-top-title">课程代码:</span>
+          <span class="paper-top-value">{{ paper.course.code }}</span>
+        </div>
+        <div class="paper-top-div">
+          <span class="paper-top-title">课程名称:</span>
+          <span class="paper-top-value">{{ paper.course.name }}</span>
+        </div>
+        <div class="paper-top-div">
+          <span class="paper-top-title">试卷名称:</span>
+          <el-tooltip class="item" effect="dark" placement="top-start">
+            <div slot="content">{{ paper.name }}</div>
+            <input
+              v-model="paper.name"
+              class="paperName-input font_length"
+              placeholder="试卷名称"
+            />
+          </el-tooltip>
+        </div>
+        <div class="paper-top-div">
+          <span class="paper-top-title">试卷难度:</span>
+          <span class="paper-top-value">{{ paper.difficultyDegree }}</span>
+        </div>
+        <div>
+          <el-button type="primary" size="small" @click="savePaper">
+            保存
+          </el-button>
+          <el-button type="danger" size="small" @click="deletePaper(paper.id)">
+            <i class="el-icon-delete"></i> 删除
+          </el-button>
+          <el-dropdown class="button_left">
+            <el-button type="primary" size="small">
+              更多 <i class="el-icon-arrow-down el-icon--right"></i>
+            </el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item>
+                <el-button
+                  type="primary"
+                  :loading="duplicateLoading"
+                  size="small"
+                  @click="getreduplicateQuestions"
+                  ><i class="el-icon-zoom-in"></i>查重
+                </el-button>
+              </el-dropdown-item>
+              <el-dropdown-item>
+                <el-button
+                  v-show="parentView == 'import_paper'"
+                  type="primary"
+                  size="small"
+                  @click="openDialog"
+                  ><i class="el-icon-upload2"></i>上传音频
+                </el-button>
+              </el-dropdown-item>
+              <el-dropdown-item>
+                <el-button
+                  size="small"
+                  type="primary"
+                  @click="exportPaperAnswer()"
+                  ><i class="el-icon-download"></i>导出答案</el-button
+                >
+              </el-dropdown-item>
+              <el-dropdown-item>
+                <el-button
+                  v-show="parentView == 'import_paper'"
+                  type="primary"
+                  size="small"
+                  @click="openAnswerDialog"
+                  ><i class="el-icon-upload2"></i>导入答案
+                </el-button>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+
+          <el-button
+            size="small"
+            type="primary"
+            style="margin-left: 10px"
+            @click="back"
+            ><i class="el-icon-arrow-left"></i> 返回</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <div class="paperName">
+      <div>
+        <br />
+        <h3 class="text-center">{{ paper.course.name }}&nbsp;试卷</h3>
+        <h5 class="text-center">
+          <span v-show="paper.hasAudio">(含音频试卷)</span>
+        </h5>
+        <br />
+        <h4 class="text-center">(课程代码&nbsp;{{ paper.course.code }})</h4>
+        <br />
+      </div>
+      <div class="text-left">
+        <el-table :data="paper.paperDetails" border style="width: 100%">
+          <el-table-column header-align="center" label="大题名称">
+            <template slot-scope="scope">
+              <span>{{ scope.row.name }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column header-align="center" label="大题总分">
+            <template slot-scope="scope">
+              <span>{{ scope.row.score }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column header-align="center" label="小题数量">
+            <template slot-scope="scope">
+              <span>{{ scope.row.unitCount }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column header-align="center" label="公开数量">
+            <template slot-scope="scope">
+              <span>{{ scope.row.pubCount }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column header-align="center" label="非公开数量">
+            <template slot-scope="scope">
+              <span>{{ scope.row.noPubCount }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <div class="mainQues" style="margin-top: 20px; margin-left: 0px">
+          <div class="mainQuesTitle">
+            <span>考试说明:</span>
+            <span>
+              <el-button size="small" @click="openEditExamPaperRemark"
+                >编辑</el-button
+              >
+            </span>
+            <div style="width: 550px; margin-left: 20px; margin-top: 20px">
+              <span v-html="paper.examRemark"></span>
+            </div>
+          </div>
+        </div>
+        <!-- end by wwh -->
+        <div>
+          <h1>本试卷满分{{ paper.totalScore }}分。</h1>
+        </div>
+        <br />
+      </div>
+    </div>
+
+    <div>
+      <!-- 循环大题 -->
+      <div
+        v-for="(paperDetail, detailIndex) in paper.paperDetails"
+        v-show="paperDetailShow(paperDetail)"
+        :key="detailIndex"
+        class="mainQues"
+      >
+        <div
+          class="mainQuesTitle"
+          @mouseover="quesMouseOver(paperDetail.id)"
+          @mouseout="quesMouseOut(paperDetail.id)"
+        >
+          <span>{{ paperDetail.cnNum }}</span> <span>.</span>
+          <span>{{ paperDetail.name }}</span>
+          <span>
+            ({{ !paperDetail.title ? "本大题" : paperDetail.title + "," }}共{{
+              paperDetail.unitCount
+            }}小题,满分{{ paperDetail.score }}分)
+          </span>
+
+          <span :id="paperDetail.id" class="btnDiv">
+            <el-button
+              v-show="parentView == 'gen_paper'"
+              size="small"
+              @click="selectQues(paperDetail.id)"
+              >选题
+            </el-button>
+
+            <el-button size="small" @click="openEditPaperDetail(paperDetail)"
+              >编辑
+            </el-button>
+
+            <el-button
+              v-if="showUp(paperDetail)"
+              size="small"
+              @click="movePaperDetail(paperDetail, 'up')"
+              >上移
+            </el-button>
+
+            <el-button
+              v-if="showDown(paperDetail)"
+              size="small"
+              @click="movePaperDetail(paperDetail, 'down')"
+              >下移
+            </el-button>
+
+            <el-button
+              size="small"
+              type="danger"
+              @click="deletePaperDetail(paperDetail.id)"
+              >删除
+            </el-button>
+
+            <el-button
+              v-show="showButtons[detailIndex].up"
+              size="small"
+              icon="el-icon-arrow-up"
+              @click.stop="hideContent(detailIndex)"
+            ></el-button>
+
+            <el-button
+              v-show="!showButtons[detailIndex].up"
+              size="small"
+              icon="el-icon-arrow-down"
+              @click.stop="showContent(detailIndex)"
+            ></el-button>
+          </span>
+        </div>
+
+        <!-- 循环小题 -->
+        <div v-show="showQuestions[detailIndex].is_show">
+          <div
+            v-for="(paperDetailUnit, unitIndex) in paperDetail.paperDetailUnits"
+            v-show="quesShow(paperDetailUnit.id)"
+            :key="unitIndex"
+            class="ques"
+          >
+            <reduplicate_mark
+              :id="paperDetailUnit.id"
+              :show="reduplicateMarkShow(paperDetailUnit.id)"
+              :fill-color="reduplicateMarkColor(paperDetailUnit.id)"
+              :checked="reduplicateMarkCheck(paperDetailUnit.id)"
+              @reduplicate_mark_check="reduplicate_mark_check"
+            >
+            </reduplicate_mark>
+
+            <div
+              class="quesSelect"
+              @mouseover="quesMouseOver(paperDetailUnit.id)"
+              @mouseout="quesMouseOut(paperDetailUnit.id)"
+            >
+              <div :id="paperDetailUnit.id" class="btnDiv">
+                <el-button
+                  size="small"
+                  @click="editQues(paperDetailUnit, paperDetailUnit.question)"
+                  >编辑
+                </el-button>
+
+                <el-button
+                  type="danger"
+                  size="small"
+                  @click="deleteQues(paperDetailUnit)"
+                  >删除
+                </el-button>
+              </div>
+
+              <div class="quesBody">
+                <span class="ques-title">{{ paperDetailUnit.number }}.</span>
+                <span
+                  v-question-audio
+                  class="ques-body"
+                  :hasAudio="paperDetailUnit.question.hasAudio"
+                  :questionId="paperDetailUnit.question.id"
+                  v-html="paperDetailUnit.question.quesBody"
+                ></span>
+
+                <span class="score-span">
+                  ({{ paperDetailUnit.score }}分)
+                </span>
+              </div>
+
+              <div
+                v-for="(quesOption, optionIndex) in paperDetailUnit.question
+                  .quesOptions"
+                :key="optionIndex"
+                class="quesOption"
+              >
+                <span class="ques-title"
+                  >{{ optionIndex | optionOrderWordFilter }}.
+                </span>
+
+                <span
+                  v-question-audio
+                  class="ques-body"
+                  :hasAudio="paperDetailUnit.question.hasAudio"
+                  :questionId="paperDetailUnit.question.id"
+                  v-html="quesOption.optionBody"
+                ></span>
+              </div>
+            </div>
+            <br />
+
+            <div
+              v-for="(subQuestion, subIndex) in paperDetailUnit.question
+                .subQuestions"
+              v-show="quesShow(subQuestion.id)"
+              :key="subIndex"
+              class="subQues"
+            >
+              <reduplicate_mark
+                :show="reduplicateMarkShow(subQuestion.id)"
+              ></reduplicate_mark>
+
+              <div
+                class="quesSelect"
+                @mouseover="
+                  quesMouseOver(getSubQuesEditId(paperDetailUnit, subQuestion))
+                "
+                @mouseout="
+                  quesMouseOut(getSubQuesEditId(paperDetailUnit, subQuestion))
+                "
+              >
+                <div
+                  :id="getSubQuesEditId(paperDetailUnit, subQuestion)"
+                  class="btnDiv"
+                >
+                  <el-button
+                    size="small"
+                    @click="editQues(paperDetailUnit, subQuestion)"
+                    >编辑
+                  </el-button>
+                </div>
+
+                <div class="quesBody">
+                  <span class="ques-title"
+                    >{{ subQuestion.quesParams.number }}.
+                  </span>
+
+                  <span v-html="subQuestion.quesBody"></span>
+                  <span
+                    >({{ paperDetailUnit.subScoreList[subIndex] }}分)</span
+                  >
+                </div>
+
+                <div
+                  v-for="(
+                    subQuesOption, subOptIndex
+                  ) in subQuestion.quesOptions"
+                  :key="subOptIndex"
+                  class="quesOption"
+                >
+                  <span class="ques-title"
+                    >{{ subOptIndex | optionOrderWordFilter }}.
+                  </span>
+                  <span v-html="subQuesOption.optionBody"></span>
+                </div>
+                <br />
+              </div>
+            </div>
+          </div>
+          <br />
+        </div>
+      </div>
+    </div>
+
+    <div class="text-left">
+      <!-- 编辑大题弹框 -->
+      <el-dialog
+        v-loading.body="detailLoading"
+        width="360px"
+        title="大题名称编辑"
+        element-loading-text="保存中。。。"
+        :visible.sync="paperDatailDialog"
+        @close="closeQuesDialog"
+      >
+        <el-form
+          :model="editpaperDetail"
+          label-position="right"
+          label-width="80px"
+        >
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="大题名称" placeholder="大题名称">
+                <el-input
+                  v-model="editpaperDetail.name"
+                  class="dialog_input_width"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-form-item>
+              <el-button
+                type="primary"
+                @click="savePaperDatail(editpaperDetail)"
+                >保存</el-button
+              >
+              <el-button @click="closePaperDatailDialog()">取消</el-button>
+            </el-form-item>
+          </el-row>
+        </el-form>
+      </el-dialog>
+      <!-- 编辑试题弹框 -->
+      <el-dialog
+        v-loading.body="dialogLoading"
+        title="试题编辑"
+        element-loading-text="保存中。。。"
+        :visible.sync="quesDialog"
+        @close="closeQuesDialog"
+      >
+        <el-form :model="quesModel" label-position="right" label-width="80px">
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="题型">
+                <el-select
+                  v-model="quesModel.questionType"
+                  :disabled="true"
+                  placeholder="请输入题型"
+                >
+                  <el-option
+                    v-for="item in questionTypes"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="分值">
+                <el-input
+                  v-model="quesModel.score"
+                  placeholder="分值"
+                  :disabled="quesModel.questionType == 'NESTED_ANSWER_QUESTION'"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!-- create by  weiwenhai 添加难度,公开度,试题属性 -->
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="难度">
+                <el-select
+                  v-model="quesModel.difficultyDegree"
+                  placeholder="请输入难度"
+                  :disabled="
+                    quesModel.questionType == 'NESTED_ANSWER_QUESTION'
+                      ? true
+                      : updatePorperty
+                  "
+                >
+                  <el-option
+                    v-for="item in difficultyDegreeList"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="公开度">
+                <el-select
+                  v-model="quesModel.publicity"
+                  placeholder="请输入公开度"
+                  :disabled="updatePorperty"
+                >
+                  <el-option
+                    v-for="item in publicityList"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+
+            <el-col
+              v-if="quesModel.questionType == 'TEXT_ANSWER_QUESTION'"
+              :xs="10"
+              :sm="10"
+              :md="10"
+              :lg="10"
+            >
+              <el-form-item label="作答类型">
+                <el-select
+                  v-model="quesModel.answerType"
+                  :disabled="updatePorperty"
+                >
+                  <el-option
+                    v-for="item in answerTypes"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <el-row :gutter="10">
+            <el-col :xs="20" :sm="20" :md="20" :lg="20">
+              <el-form-item label="属性列表">
+                <el-tooltip
+                  v-for="(content, propIndex) in quesModel.quesProperties"
+                  :key="propIndex"
+                  placement="top"
+                >
+                  <div slot="content">
+                    <span v-if="content.firstProperty != null"
+                      >一级属性:{{ content.firstProperty.name }}</span
+                    ><br />
+                    <span v-if="content.secondProperty != null"
+                      >二级属性:{{ content.secondProperty.name }}</span
+                    >
+                  </div>
+                  <span>
+                    <el-tag
+                      :key="content.id"
+                      style="margin-right: 5px"
+                      :closable="!updatePorperty"
+                      type="primary"
+                      @close="handleClose(content)"
+                    >
+                      {{ content.coursePropertyName }}
+                    </el-tag>
+                  </span>
+                </el-tooltip>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <el-row>
+            <el-col :span="6">
+              <el-form-item label="属性名" label-width="60px">
+                <el-select
+                  v-model="coursePropertyName"
+                  placeholder="属性名"
+                  class="property_with"
+                  :disabled="updatePorperty"
+                  @change="searchFirst"
+                >
+                  <el-option label="请选择" value=""></el-option>
+                  <el-option
+                    v-for="item in coursePropertyList"
+                    :key="item.name"
+                    :label="item.name"
+                    :value="item.name"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="一级" label-width="48px">
+                <el-select
+                  v-model="firstPropertyId"
+                  placeholder="一级"
+                  class="property_with"
+                  :disabled="updatePorperty"
+                  @change="searchSecond"
+                >
+                  <el-option label="请选择" value=""></el-option>
+                  <el-option
+                    v-for="item in firstPropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="二级" label-width="48px">
+                <el-select
+                  v-model="secondPropertyId"
+                  placeholder="二级"
+                  class="property_with"
+                  :disabled="updatePorperty"
+                >
+                  <el-option label="请选择" value=""></el-option>
+                  <el-option
+                    v-for="item in secondPropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item>
+                <el-button
+                  type="primary"
+                  style="margin-left: -50px"
+                  :disabled="updatePorperty"
+                  @click="insertProperty"
+                  ><i class="el-icon-plus"></i>新增属性
+                </el-button>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!-- end by weiwenhai -->
+          <div v-if="paper.paperType != 'IMPORT'">
+            <el-row>
+              <el-col>
+                <el-form-item label="题目">
+                  <span v-html="quesModel.quesBody"></span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <el-form-item
+              v-for="(quesOption, optIndex) in quesModel.quesOptions"
+              :key="optIndex"
+              ><el-col :span="2">
+                <span>{{ optIndex | optionOrderWordFilter }}</span>
+              </el-col>
+              <el-col :span="20">
+                <span v-html="quesOption.optionBody"></span>
+              </el-col>
+            </el-form-item>
+
+            <div>
+              <el-form-item label="答案">
+                <span v-html="quesModel.quesAnswer"></span>
+              </el-form-item>
+            </div>
+            <!-- 单选或多选 -->
+          </div>
+          <div v-if="paper.paperType == 'IMPORT'">
+            <el-row>
+              <el-col>
+                <el-form-item label="题目">
+                  <ckeditor v-model="quesModel.quesBody"></ckeditor>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <el-form-item
+              v-for="(quesOption, optIndex) in quesModel.quesOptions"
+              :key="optIndex"
+            >
+              <el-col :span="2">
+                <el-radio
+                  v-if="quesModel.questionType === 'SINGLE_ANSWER_QUESTION'"
+                  v-model="singleRightAnswer"
+                  :label="optIndex | optionOrderWordFilter"
+                ></el-radio>
+                <el-checkbox
+                  v-if="quesModel.questionType === 'MULTIPLE_ANSWER_QUESTION'"
+                  v-model="multipleRightAnswer"
+                  :label="optIndex | optionOrderWordFilter"
+                ></el-checkbox>
+              </el-col>
+              <el-col :span="20">
+                <ckeditor v-model="quesOption.optionBody"></ckeditor>
+              </el-col>
+              <el-col :span="2">
+                <i
+                  class="el-icon-delete"
+                  title="删除"
+                  @click.prevent="removeQuesOption(quesOption)"
+                ></i>
+              </el-col>
+            </el-form-item>
+
+            <div
+              v-if="
+                quesModel.questionType != 'NESTED_ANSWER_QUESTION' &&
+                quesModel.questionType != 'SINGLE_ANSWER_QUESTION' &&
+                quesModel.questionType != 'MULTIPLE_ANSWER_QUESTION' &&
+                quesModel.questionType != 'BOOL_ANSWER_QUESTION'
+              "
+            >
+              <el-form-item label="答案">
+                <ckeditor v-model="quesModel.quesAnswer"></ckeditor>
+              </el-form-item>
+            </div>
+            <!-- 单选或多选 -->
+            <div
+              v-if="
+                quesModel.questionType == 'SINGLE_ANSWER_QUESTION' ||
+                quesModel.questionType == 'MULTIPLE_ANSWER_QUESTION'
+              "
+            >
+              <el-form-item label="答案">
+                <span v-html="answer"></span>
+              </el-form-item>
+            </div>
+            <div v-if="quesModel.questionType == 'BOOL_ANSWER_QUESTION'">
+              <el-row>
+                <el-col>
+                  <el-form-item label="答案" prop="quesAnswer">
+                    <el-select
+                      v-model="quesModel.quesAnswer"
+                      placeholder="请选择"
+                    >
+                      <el-option
+                        v-for="op in options"
+                        :key="op"
+                        :label="op"
+                        :value="op"
+                      >
+                      </el-option>
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+            </div>
+          </div>
+          <div
+            :class="{
+              margin_left_30:
+                paper.paperType == 'IMPORT' &&
+                (quesModel.questionType == 'SINGLE_ANSWER_QUESTION' ||
+                  quesModel.questionType == 'MULTIPLE_ANSWER_QUESTION'),
+              margin_left_40: !(
+                paper.paperType == 'IMPORT' &&
+                (quesModel.questionType == 'SINGLE_ANSWER_QUESTION' ||
+                  quesModel.questionType == 'MULTIPLE_ANSWER_QUESTION')
+              ),
+            }"
+          >
+            <el-button
+              v-if="
+                paper.paperType == 'IMPORT' &&
+                (quesModel.questionType == 'SINGLE_ANSWER_QUESTION' ||
+                  quesModel.questionType == 'MULTIPLE_ANSWER_QUESTION')
+              "
+              type="primary"
+              @click="addQuesOption"
+              ><i class="el-icon-plus"></i> 新增选项
+            </el-button>
+            <el-button type="primary" @click="savePaperDetailUnit()"
+              >保存</el-button
+            >
+            <el-button @click="closeQuesDialog">取消</el-button>
+          </div>
+        </el-form>
+      </el-dialog>
+      <!-- 考试说明弹框 -->
+      <el-dialog title="考试说明编辑" :visible.sync="paperRemarkDialog">
+        <el-form label-position="right" label-width="80px">
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="考试说明">
+                <div style="width: 550px">
+                  <ckeditor
+                    v-model="examRemark"
+                    :display="display"
+                    :width="wValue"
+                    :height="hValue"
+                  ></ckeditor>
+                </div>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <div style="margin-top: 20px; margin-left: 40%">
+            <el-button type="primary" @click="savePaperRemark">保存</el-button>
+            <el-button @click="closPaperRemark">取消</el-button>
+          </div>
+        </el-form>
+      </el-dialog>
+      <!-- 上传音频弹框 -->
+      <el-dialog
+        title="上传音频文件"
+        :visible.sync="dialogRadioFile"
+        :before-close="closeAudioDialog"
+      >
+        <form
+          id="radioForm"
+          method="post"
+          action=""
+          enctype="multipart/form-data"
+        >
+          <input
+            id="radioFile"
+            name="files"
+            type="file"
+            value="上传音频文件"
+            webkitdirectory
+          />
+          <el-button type="warning" @click="checkFile">检查文件名</el-button>
+          <el-button
+            type="info"
+            :loading="uploadAudioLoading"
+            :disabled="isUpload || uploadAudioLoading"
+            @click="uploadAudioFile"
+          >
+            <span v-show="!uploadAudioLoading">开始上传</span>
+            <span v-show="uploadAudioLoading">正在上传中...</span>
+          </el-button>
+        </form>
+        <div v-if="checkResult" style="margin-top: 20px">
+          <span>检查结果:</span><br /><br />
+          <span v-show="message == 'OK!'" style="color: #13ce66">OK!</span>
+          <span v-show="message != 'OK!'" style="color: #ff4949">{{
+            message
+          }}</span>
+        </div>
+      </el-dialog>
+      <el-dialog
+        title="上传答案文件"
+        :visible.sync="dialogAnswerFile"
+        :before-close="closeAnswerDialog"
+      >
+        <form
+          id="answerForm"
+          method="post"
+          action=""
+          enctype="multipart/form-data"
+        >
+          <input
+            id="answerFile"
+            name="answerFiles"
+            type="file"
+            value="上传答案文件"
+            accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+          />
+          <el-button type="info" @click="downAnswerTemplate">
+            <span>下载模板</span>
+          </el-button>
+          <el-button
+            type="info"
+            :loading="uploadAnswerLoading"
+            :disabled="uploadAnswerLoading"
+            @click="uploadAnswerFile"
+          >
+            <span v-show="!uploadAnswerLoading">开始上传</span>
+            <span v-show="uploadAnswerLoading">正在上传中...</span>
+          </el-button>
+        </form>
+        <div style="margin-top: 20px">
+          <span v-show="answerMessage != ''" style="color: #ff4949">{{
+            answerMessage
+          }}</span>
+        </div>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { isEmptyStr, QUESTION_TYPES } from "../constants/constants";
+import { mapState } from "vuex";
+import reduplicate_mark from "../component/reduplicate_mark.vue";
+import randomColor from "randomcolor";
+import ckeditor from "../component/ckeditor.vue";
+
+export default {
+  name: "EditPaperApp",
+  components: {
+    reduplicate_mark,
+    ckeditor,
+  },
+  data() {
+    return {
+      hValue: "100px",
+      wValue: "500px",
+      display: "block",
+      uploadAction: "",
+      fileList: [],
+      answerFileList: [],
+      paperId: "",
+      paperDetailId: "",
+      editPaperDetailUnit: "",
+      quesDialog: false,
+      paperDatailDialog: false,
+      paperRemarkDialog: false,
+      parentView: "",
+      paper: {
+        course: {
+          code: "",
+          name: "",
+        },
+        examRemark: "",
+      },
+      loading: false,
+      dialogLoading: false,
+      detailLoading: false,
+      uploadAudioLoading: false,
+      uploadAnswerLoading: false,
+      questionTypes: QUESTION_TYPES,
+      questionType: "",
+      quesModel: { quesProperties: [] },
+      editpaperDetail: {},
+      reduplicateQuestions: [],
+      reduplicateGroup: [],
+      reduplicateQuesColor: [],
+      singleRightAnswer: "", //接收单选答案
+      multipleRightAnswer: [], //接收多选答案
+      options: ["正确", "错误"],
+      duplicateLoading: false,
+      dialogRadioFile: false,
+      dialogAnswerFile: false,
+      isUpload: true,
+      isUploadAnswer: true,
+      message: "",
+      answerMessage: "",
+      checkResult: false,
+      checkResultAnswer: false,
+      fileNameList: [],
+      defaultColor: [
+        "Red",
+        "Blue",
+        "LimeGreen",
+        "GoldenRod",
+        "Black",
+        "BlueViolet",
+        "Chocolate",
+        "DarkCyan",
+        "HotPink",
+        "Orange",
+        "IndianRed",
+        "Indigo",
+        "Green",
+        "Aqua",
+        "CadetBlue",
+        "SkyBlue",
+        "SlateBlue",
+        "SlateGray",
+        "Tomato",
+        "VioletRed",
+      ],
+      difficultyDegreeList: [
+        { label: 0.1, value: 0.1 },
+        { label: 0.2, value: 0.2 },
+        { label: 0.3, value: 0.3 },
+        { label: 0.4, value: 0.4 },
+        { label: 0.5, value: 0.5 },
+        { label: 0.6, value: 0.6 },
+        { label: 0.7, value: 0.7 },
+        { label: 0.8, value: 0.8 },
+        { label: 0.9, value: 0.9 },
+        { label: 1.0, value: 1.0 },
+      ],
+      publicityList: [
+        { label: "公开", value: true },
+        { label: "非公开", value: false },
+      ],
+      answerTypes: [
+        { label: "文本", value: "DIVERSIFIED_TEXT" },
+        { label: "音频", value: "SINGLE_AUDIO" },
+      ],
+      coursePropertyList: [],
+      coursePropertyName: "", //课程属性名
+      firstPropertyList: [], //一级属性集合
+      firstPropertyId: "", //一级属性id
+      secondPropertyList: [], //二级属性集合
+      secondPropertyId: "", //二级属性id
+      examRemark: "",
+      showQuestions: [],
+      showButtons: [],
+    };
+  },
+  computed: {
+    ...mapState({
+      user: (state) => state.user,
+    }),
+    updatePorperty() {
+      if (this.parentView === "gen_paper") {
+        return true;
+      }
+      return false;
+    },
+    answer() {
+      if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+        return this.singleRightAnswer;
+      } else if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+        var obj = this.multipleRightAnswer;
+        return obj.sort().toString();
+      }
+      return this.quesModel.quesAnswer;
+    },
+  },
+  created() {
+    // $("body").attr("style", "");
+    document.getElementsByTagName("body")[0].style = "";
+    this.paperId = this.$route.params.id;
+    this.parentView = this.$route.params.parentView;
+    this.initPaper();
+    this.getreduplicateQuestions();
+    this.uploadAction = QUESTION_API + "/uploadRadio/" + this.paperId;
+    this.uploadHeaders = {
+      key: this.user.key,
+      token: this.user.token,
+    };
+  },
+  methods: {
+    movePaperDetail(detail, vector) {
+      let vectorStr = vector == "up" ? "上移" : "下移";
+      this.$alert("您确定" + vectorStr + "吗?", "提示", {
+        confirmButtonText: "确定",
+        callback: (action) => {
+          if (action == "confirm") {
+            this.loading = true;
+            this.$http
+              .put(
+                QUESTION_API +
+                  "/paperDetail/" +
+                  this.paperId +
+                  "/" +
+                  detail.id +
+                  "/" +
+                  vector
+              )
+              .then(() => {
+                this.initPaper();
+                this.loading = true;
+                this.$notify({
+                  message: vectorStr + "成功",
+                  type: "success",
+                });
+                this.loading = false;
+              });
+          }
+        },
+      });
+    },
+    showUp(detail) {
+      if (this.paper.paperDetails.length <= 1) {
+        return false;
+      }
+      if (detail.id != this.paper.paperDetails[0].id) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    showDown(detail) {
+      if (this.paper.paperDetails.length <= 1) {
+        return false;
+      }
+      if (
+        detail.id !=
+        this.paper.paperDetails[this.paper.paperDetails.length - 1].id
+      ) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    downAnswerTemplate() {
+      var key = this.user.key;
+      var token = this.user.token;
+      window.open(
+        QUESTION_API + "/paper/answer/template?$key=" + key + "&$token=" + token
+      );
+    },
+    openAnswerDialog() {
+      this.checkResultAnswer = false;
+      this.isUploadAnswer = true;
+      if (document.getElementById("answerFile")) {
+        document.getElementById("answerFile").value = "";
+      }
+      this.dialogAnswerFile = true;
+      this.answerFileList = [];
+    },
+
+    closeAnswerDialog() {
+      this.answerMessage = "";
+      this.dialogAnswerFile = this.uploadAnswerLoading;
+    },
+    uploadAnswerFile() {
+      this.answerMessage = "";
+      var fileList = document.getElementById("answerFile").files;
+      if (fileList.length == 0) {
+        this.answerMessage = "请选择文件!";
+        return;
+      }
+      let param = new FormData();
+      //循环添加到formData中
+      for (var i = 0; i < fileList.length; i++) {
+        var file = fileList[i];
+        param.append("dataFile", file, file.name);
+      }
+      let config = {
+        headers: { "Content-Type": "multipart/form-data" },
+      };
+      this.uploadAnswerLoading = true;
+      this.$http
+        .post(
+          QUESTION_API + "/paper/answer/import/" + this.paperId,
+          param,
+          config
+        )
+        .then(() => {
+          this.dialogAnswerFile = false;
+          this.uploadAnswerLoading = false;
+          this.checkResultAnswer = false;
+          this.isUploadAnswer = true;
+          document.getElementById("answerFile").value = "";
+          this.initPaper();
+        })
+        .catch((error) => {
+          this.answerMessage = error.response.data.desc;
+          document.getElementById("answerFile").value = "";
+          this.uploadAnswerLoading = false;
+        });
+    },
+    //隐藏大题下的所有小题
+    hideContent(index) {
+      console.log("up");
+      this.showQuestions[index].is_show = false;
+      this.showButtons[index].up = false;
+    },
+    //展开大题下所有小题
+    showContent(index) {
+      console.log("down");
+      this.showQuestions[index].is_show = true;
+      this.showButtons[index].up = true;
+    },
+    quesMouseOver(index) {
+      document.getElementById(index).style.visibility = "visible";
+    },
+    quesMouseOut(index) {
+      document.getElementById(index).style.visibility = "hidden";
+    },
+    selectQues(id) {
+      this.paperDetailId = id;
+      var courseCode = this.paper.course.code;
+      var courseName = this.paper.course.name;
+      this.$router.push({
+        path:
+          "/select_question/" +
+          this.paper.id +
+          "/" +
+          courseCode +
+          "/" +
+          encodeURIComponent(courseName) +
+          "/" +
+          this.paperDetailId +
+          "/" +
+          this.parentView,
+      });
+    },
+    //打开编辑大题题目弹窗
+    openEditPaperDetail(paperDetail) {
+      this.paperDatailDialog = true;
+      this.editpaperDetail = Object.assign({}, paperDetail); //浅拷贝
+    },
+    //关闭编辑大题题目弹窗
+    closePaperDatailDialog() {
+      this.paperDatailDialog = false;
+      this.editpaperDetail = {};
+    },
+    //保存大题题目信息
+    savePaperDatail(editpaperDetail) {
+      this.detailLoading = true;
+      var paperId = this.paper.id;
+      this.$http
+        .post(QUESTION_API + "/updatePaperDetail/" + paperId, editpaperDetail)
+        .then(() => {
+          this.$notify({
+            message: "保存成功",
+            type: "success",
+          });
+          this.detailLoading = false;
+          this.closePaperDatailDialog();
+          this.initPaper();
+        });
+    },
+    //初始化试卷
+    initPaper() {
+      const scrollPosition =
+        document.documentElement.scrollTop || document.body.scrollTop;
+      this.loading = true;
+      this.paper = {
+        course: {
+          code: "",
+          name: "",
+        },
+      };
+      this.$http
+        .get(QUESTION_API + "/paper/" + this.paperId)
+        .then((response) => {
+          this.paper = response.data;
+          //查询所有课程属性名
+          this.initCourseProperty(this.paper.course.code);
+          //将所有小题分为公开和非公开
+          if (this.paper.paperDetails && this.paper.paperDetails.length > 0) {
+            for (let paperDetil of this.paper.paperDetails) {
+              this.showQuestions.push({ is_show: true });
+              this.showButtons.push({ up: true });
+              paperDetil.pubCount = 0;
+              paperDetil.noPubCount = 0;
+              if (
+                paperDetil.paperDetailUnits &&
+                paperDetil.paperDetailUnits.length > 0
+              ) {
+                for (let paperDetilUt of paperDetil.paperDetailUnits) {
+                  if (
+                    paperDetilUt.question.questionType !=
+                    "NESTED_ANSWER_QUESTION"
+                  ) {
+                    //非套题
+                    if (paperDetilUt.question.publicity) {
+                      paperDetil.pubCount = paperDetil.pubCount + 1;
+                    } else {
+                      paperDetil.noPubCount = paperDetil.noPubCount + 1;
+                    }
+                  } else {
+                    //循环所有子题
+                    for (let ques of paperDetilUt.question.subQuestions) {
+                      if (ques.publicity) {
+                        paperDetil.pubCount = paperDetil.pubCount + 1;
+                      } else {
+                        paperDetil.noPubCount = paperDetil.noPubCount + 1;
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+          setTimeout(() => {
+            document.documentElement.scrollTop = document.body.scrollTop = scrollPosition;
+            console.log(scrollPosition);
+          }, 1000);
+          this.loading = false;
+        });
+    },
+    //查询所有课程属性名
+    initCourseProperty(courseCode) {
+      this.$http
+        .get(QUESTION_API + "/courseProperty/enable/" + courseCode)
+        .then((response) => {
+          this.coursePropertyList = response.data;
+        });
+    },
+    //删除大题
+    deletePaperDetail(paperDetailsId) {
+      //先判断大题下面是否还有小题
+      var count = 0;
+      for (var i = 0, imax = this.paper.paperDetails.length; i < imax; i++) {
+        if (paperDetailsId == this.paper.paperDetails[i].id) {
+          if (this.paper.paperDetails[i].paperDetailUnits) {
+            count += this.paper.paperDetails[i].paperDetailUnits.length;
+            break;
+          }
+        }
+      }
+      if (count == 0) {
+        this.$alert("您确定删除吗?", "提示", {
+          confirmButtonText: "确定",
+          callback: (action) => {
+            if (action == "confirm") {
+              this.loading = true;
+              this.$http
+                .delete(
+                  QUESTION_API +
+                    "/paperDetail/" +
+                    this.paperId +
+                    "/" +
+                    paperDetailsId
+                )
+                .then(() => {
+                  this.initPaper();
+                  this.loading = true;
+                  this.$notify({
+                    message: "删除成功",
+                    type: "success",
+                  });
+                  this.loading = false;
+                });
+            }
+          },
+        });
+      } else {
+        this.$alert("大题下还有小题,不可删除!", "提示", {
+          confirmButtonText: "确定",
+          callback: () => {},
+        });
+      }
+    },
+    quesShow(id) {
+      if (this.reduplicateGroup.length < 1) {
+        return true;
+      }
+      for (var i = 0, imax = this.reduplicateGroup.length; i < imax; i++) {
+        if (id == this.reduplicateGroup[i]) {
+          return true;
+        }
+      }
+      return false;
+    },
+    reduplicateMarkShow(id) {
+      var found = false;
+      for (var i = 0, imax = this.reduplicateQuestions.length; i < imax; i++) {
+        for (
+          var j = 0, jmax = this.reduplicateQuestions[i].length;
+          j < jmax;
+          j++
+        ) {
+          if (this.reduplicateQuestions[i][j] == id) {
+            found = true;
+            break;
+          }
+        }
+        if (found) {
+          break;
+        }
+      }
+      return found;
+    },
+    reduplicateMarkColor(id) {
+      for (var i = 0, imax = this.reduplicateQuestions.length; i < imax; i++) {
+        for (
+          var j = 0, jmax = this.reduplicateQuestions[i].length;
+          j < jmax;
+          j++
+        ) {
+          if (this.reduplicateQuestions[i][j] == id) {
+            return this.reduplicateQuesColor[i];
+          }
+        }
+      }
+    },
+    reduplicateMarkCheck(id) {
+      for (var i = 0, imax = this.reduplicateGroup.length; i < imax; i++) {
+        if (id == this.reduplicateGroup[i]) {
+          return true;
+        }
+      }
+      return false;
+    },
+    reduplicate_mark_check(id, checked) {
+      console.log(checked);
+      console.log(this.reduplicateQuestions);
+      console.log(id);
+      if (!checked) {
+        for (
+          var i = 0, imax = this.reduplicateQuestions.length;
+          i < imax;
+          i++
+        ) {
+          for (
+            var j = 0, jmax = this.reduplicateQuestions[i].length;
+            j < jmax;
+            j++
+          ) {
+            if (this.reduplicateQuestions[i][j] == id) {
+              this.reduplicateGroup = [];
+              for (
+                var k = 0, kmax = this.reduplicateQuestions[i].length;
+                k < kmax;
+                k++
+              ) {
+                this.reduplicateGroup.push(this.reduplicateQuestions[i][k]);
+              }
+              return;
+            }
+          }
+        }
+      } else {
+        this.reduplicateGroup = [];
+      }
+    },
+    //编辑题目
+    editQues(paperDetailUnit, question) {
+      console.log("question:", question);
+      this.coursePropertyName = "";
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.editPaperDetailUnit = paperDetailUnit;
+      this.quesModel = JSON.parse(JSON.stringify(question)); //深拷贝
+      this.quesModel.score = paperDetailUnit.score;
+      //如果是套题下面的小题编辑 ( paperDetailUnit的类型是套题,question的类型不是套题)
+      if (
+        paperDetailUnit.questionType == "NESTED_ANSWER_QUESTION" &&
+        question.questionType != "NESTED_ANSWER_QUESTION"
+      ) {
+        for (var i = 0; i < paperDetailUnit.question.subQuestions.length; i++) {
+          if (
+            paperDetailUnit.question.subQuestions[i].id == this.quesModel.id
+          ) {
+            this.quesModel.score = paperDetailUnit.subScoreList[i];
+            break;
+          }
+        }
+      }
+
+      if (isEmptyStr(this.quesModel.answerType)) {
+        this.quesModel.answerType = "DIVERSIFIED_TEXT";
+      }
+
+      if (this.quesModel.questionType == "FILL_BLANK_QUESTION") {
+        this.quesModel.quesBody = this.quesModel.quesBody.replace(
+          /______/g,
+          "###"
+        );
+      }
+
+      this.assignAnswers(); //给singleRightAnswer或multipleRightAnswer赋值
+      this.openQuesDialog();
+    },
+    //给singleRightAnswer和multipleRightAnswer赋值
+    assignAnswers() {
+      if (this.quesModel.quesOptions && this.quesModel.quesOptions.length > 0) {
+        this.singleRightAnswer = "";
+        this.multipleRightAnswer = [];
+        for (let i = 0; i < this.quesModel.quesOptions.length; i++) {
+          let option = this.quesModel.quesOptions[i];
+          if (
+            this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" &&
+            option.isCorrect == 1
+          ) {
+            this.singleRightAnswer = String.fromCharCode(65 + i);
+          }
+          if (
+            this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION" &&
+            option.isCorrect == 1
+          ) {
+            this.multipleRightAnswer.push(String.fromCharCode(65 + i));
+          }
+        }
+      }
+    },
+    //打开修改试题编辑框
+    openQuesDialog() {
+      this.quesDialog = true;
+    },
+    //关闭试题编辑框
+    closeQuesDialog() {
+      this.quesDialog = false;
+      this.quesModel = {};
+    },
+    //删除属性
+    handleClose(tag) {
+      this.quesModel.quesProperties.splice(
+        this.quesModel.quesProperties.indexOf(tag),
+        1
+      );
+    },
+    //查询一级属性
+    searchFirst() {
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.secondPropertyList = [];
+      if (this.coursePropertyName) {
+        for (let courseProperty of this.coursePropertyList) {
+          if (courseProperty.name == this.coursePropertyName) {
+            this.$http
+              .get(QUESTION_API + "/property/first/" + courseProperty.id)
+              .then((response) => {
+                this.firstPropertyList = response.data;
+              });
+          }
+        }
+      }
+    },
+    //查询二级属性
+    searchSecond() {
+      this.secondPropertyId = "";
+      if (this.firstPropertyId) {
+        this.$http
+          .get(QUESTION_API + "/property/second/" + this.firstPropertyId)
+          .then((response) => {
+            this.secondPropertyList = response.data;
+          });
+      }
+    },
+    //新增属性
+    insertProperty() {
+      if (!this.checkInsertPro()) {
+        return false;
+      }
+      var quesProperty = {
+        id: "",
+        coursePropertyName: "",
+        firstProperty: {},
+        secondProperty: {},
+      };
+      if (
+        this.quesModel.quesProperties == null ||
+        this.quesModel.quesProperties.length == 0
+      ) {
+        this.quesModel.quesProperties = [];
+      }
+      if (this.secondPropertyId) {
+        quesProperty.id =
+          this.coursePropertyName +
+          "-" +
+          this.firstPropertyId +
+          "-" +
+          this.secondPropertyId;
+      } else {
+        quesProperty.id = this.coursePropertyName + "-" + this.firstPropertyId;
+      }
+      for (let quesPro of this.quesModel.quesProperties) {
+        if (quesPro.id == quesProperty.id) {
+          this.$notify({
+            message: "该属性已存在,请重新选择",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      quesProperty.coursePropertyName = this.coursePropertyName;
+      //取到一级属性对象
+      for (let property of this.firstPropertyList) {
+        if (property.id == this.firstPropertyId) {
+          quesProperty.firstProperty = property;
+        }
+      }
+      //判断是否有二级属性
+      if (
+        this.secondPropertyList != undefined &&
+        this.secondPropertyList.length > 0
+      ) {
+        if (!this.secondPropertyId) {
+          this.$notify({
+            message: "请选择二级属性",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      //取到二级属性对象
+      for (let property of this.secondPropertyList) {
+        if (property.id == this.secondPropertyId) {
+          quesProperty.secondProperty = property;
+        }
+      }
+      this.quesModel.quesProperties.push(quesProperty);
+      this.quesModel = Object.assign({}, this.quesModel);
+      //清空下拉框
+      this.coursePropertyName = "";
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.firstPropertyList = [];
+      this.secondPropertyList = [];
+    },
+    //新增属性验证
+    checkInsertPro() {
+      if (!this.coursePropertyName) {
+        this.$notify({
+          message: "请选择属性",
+          type: "error",
+        });
+        return false;
+      }
+      if (!this.firstPropertyId) {
+        this.$notify({
+          message: "请选择一级属性",
+          type: "error",
+        });
+        return false;
+      }
+      return true;
+    },
+    //删除选项
+    removeQuesOption(option) {
+      this.singleRightAnswer = "";
+      this.multipleRightAnswer = [];
+      let index = this.quesModel.quesOptions.indexOf(option);
+      if (index !== -1) {
+        this.quesModel.quesOptions.splice(index, 1);
+      }
+      if (this.quesModel.quesOptions.length > 0) {
+        for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+          var quesOption = this.quesModel.quesOptions[i];
+          quesOption["number"] = i + 1;
+          if (quesOption.isCorrect == 1) {
+            var answerOrderNum = String.fromCharCode(65 + i);
+            if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+              this.singleRightAnswer = answerOrderNum;
+            }
+            if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+              this.multipleRightAnswer.push(answerOrderNum);
+            }
+          }
+        }
+      }
+    },
+    //新增选项
+    addQuesOption() {
+      this.quesModel.quesOptions.push({
+        number: "",
+        optionBody: "",
+        isCorrect: "",
+      });
+      for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+        this.quesModel.quesOptions[i]["number"] = i + 1;
+      }
+    },
+    savePaperDetailUnit() {
+      //跟新难度值
+      if (this.quesModel.difficultyDegree < 0.4) {
+        this.quesModel.difficulty = "难";
+      } else if (
+        this.quesModel.difficultyDegree > 0.3 &&
+        this.quesModel.difficultyDegree < 0.8
+      ) {
+        this.quesModel.difficulty = "中";
+      } else {
+        this.quesModel.difficulty = "易";
+      }
+      this.setRightAnswer();
+      if (/^\d+(?=\.{0,1}\d+$|$)/.test(this.quesModel.score)) {
+        console.log("正确");
+      } else {
+        this.$notify({
+          message: "分数只能为正数",
+          type: "error",
+        });
+        return;
+      }
+      let paperDetailUnitExp = {
+        id: this.editPaperDetailUnit.id,
+        question: this.quesModel,
+        score: this.quesModel.score,
+      };
+      if (
+        this.quesModel.quesOptions &&
+        this.quesModel.quesOptions.length == 0
+      ) {
+        this.$confirm("无选项将删除该试题, 是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          this.dialogLoading = true;
+          this.$http
+            .delete(
+              QUESTION_API +
+                "/paper/deleteQuestion/" +
+                this.editPaperDetailUnit.id +
+                "/" +
+                this.quesModel.id
+            )
+            .then((response) => {
+              if (response.data.length > 0) {
+                var deleteInfo =
+                  "该试题被试卷:" +
+                  response.data.join(" , ") +
+                  "使用,不能删除";
+                this.$notify({
+                  message: deleteInfo,
+                  type: "error",
+                });
+              } else {
+                this.$notify({
+                  message: "保存成功",
+                  type: "success",
+                });
+              }
+              this.dialogLoading = false;
+            });
+        });
+      } else {
+        this.dialogLoading = true;
+        //校验音频重复
+        let audiomap = new Map();
+        let regex = new RegExp(
+          '<a id="[^<>]+" name="([^<>]+\\.mp3)"></a>',
+          "ig"
+        );
+        let ret = "";
+        let quesBodyStr = paperDetailUnitExp.question.quesBody;
+        if (quesBodyStr) {
+          while ((ret = regex.exec(quesBodyStr))) {
+            if (audiomap.get(ret[1])) {
+              this.dialogLoading = false;
+              this.$notify({
+                type: "error",
+                message: "题干中存在相同的音频文件",
+              });
+              return;
+            } else {
+              audiomap.set(ret[1], ret[1]);
+            }
+          }
+        }
+        let quesAnswerStr = paperDetailUnitExp.question.quesAnswer;
+        if (quesAnswerStr) {
+          while ((ret = regex.exec(quesAnswerStr))) {
+            if (audiomap.get(ret[1])) {
+              this.dialogLoading = false;
+              this.$notify({
+                type: "error",
+                message: "答案中存在相同的音频文件",
+              });
+              return;
+            } else {
+              audiomap.set(ret[1], ret[1]);
+            }
+          }
+        }
+        let quesOptions = paperDetailUnitExp.question.quesOptions;
+        if (quesOptions) {
+          for (let i = 0; i < quesOptions.length; i++) {
+            let quesOptionStr = quesOptions[i].optionBody;
+            while ((ret = regex.exec(quesOptionStr))) {
+              if (audiomap.get(ret[1])) {
+                this.dialogLoading = false;
+                this.$notify({
+                  type: "error",
+                  message: "选项中存在相同的音频文件",
+                });
+                return;
+              } else {
+                audiomap.set(ret[1], ret[1]);
+              }
+            }
+          }
+        }
+        paperDetailUnitExp.question.quesAnswer = this.answer;
+        this.$http
+          .put(QUESTION_API + "/paperDetailUnit", paperDetailUnitExp)
+          .then(() => {
+            this.$notify({
+              message: "保存成功",
+              type: "success",
+            });
+            this.dialogLoading = false;
+            this.closeQuesDialog();
+            this.initPaper();
+          })
+          .catch((err) => {
+            this.dialogLoading = false;
+            this.$notify({
+              type: "error",
+              message: err.response.data.desc,
+            });
+          });
+      }
+    },
+    //在正确的option上设置isCorrect=1
+    setRightAnswer() {
+      if (
+        !this.quesModel.quesOptions ||
+        this.quesModel.quesOptions.length == 0
+      ) {
+        return false;
+      }
+      for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+        var option = this.quesModel.quesOptions[i];
+        var answerOrderNum = String.fromCharCode(65 + i);
+        if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+          option["isCorrect"] =
+            answerOrderNum == this.singleRightAnswer ? 1 : 0;
+        }
+        if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+          option["isCorrect"] =
+            this.multipleRightAnswer.indexOf(answerOrderNum) > -1 ? 1 : 0;
+        }
+      }
+    },
+    //删除试题
+    deleteQues(paperDetailUnit) {
+      let paperDetailUnitId = paperDetailUnit.id;
+      if (this.paper.paperType == "GENERATE") {
+        this.deleteQues01(paperDetailUnitId);
+      } else {
+        let questionId = paperDetailUnit.question.id;
+        this.deleteQues02(questionId, paperDetailUnitId);
+      }
+    },
+    deleteQues01(paperDetailUnitId) {
+      this.$alert("您确定删除吗?", "提示", {
+        confirmButtonText: "确定",
+        callback: (action) => {
+          if (action == "confirm") {
+            this.loading = true;
+            this.$http
+              .delete(QUESTION_API + "/paperDetailUnit/" + paperDetailUnitId)
+              .then(() => {
+                this.initPaper();
+                this.getreduplicateQuestions();
+                this.reduplicateGroup = [];
+                this.loading = true;
+                this.$notify({
+                  message: "删除成功",
+                  type: "success",
+                });
+                this.loading = false;
+              });
+          }
+        },
+      });
+    },
+    deleteQues02(questionId, paperDetailUnitId) {
+      this.$alert("您确定删除吗?", "提示", {
+        confirmButtonText: "确定",
+        callback: (action) => {
+          if (action == "confirm") {
+            this.loading = true;
+            this.$http
+              .delete(
+                QUESTION_API +
+                  "/paper/deleteQuestion/" +
+                  paperDetailUnitId +
+                  "/" +
+                  questionId
+              )
+              .then((response) => {
+                if (response.data.length > 0) {
+                  var deleteInfo =
+                    "该试题被试卷:" +
+                    response.data.join(" , ") +
+                    "使用,不能删除";
+                  this.$notify({
+                    message: deleteInfo,
+                    type: "error",
+                  });
+                } else {
+                  this.initPaper();
+                  this.getreduplicateQuestions();
+                  this.reduplicateGroup = [];
+                  this.loading = true;
+                  this.$notify({
+                    message: "保存成功",
+                    type: "success",
+                  });
+                }
+                this.loading = false;
+              });
+          }
+        },
+      });
+    },
+    //获取重复试题
+    getreduplicateQuestions() {
+      this.duplicateLoading = true;
+      this.$http
+        .get(QUESTION_API + "/paper/" + this.paperId + "/reduplicate-questions")
+        .then((response) => {
+          this.reduplicateQuestions = response.data;
+          this.duplicateLoading = false;
+          this.initReduplicateQuesColor();
+          // var ques = document.getElementsByClassName("ques")[0];
+          // ques.style.display = "inline";
+        });
+    },
+    exportPaperAnswer() {
+      var key = this.user.key;
+      var token = this.user.token;
+      window.open(
+        QUESTION_API +
+          "/paper/answer/export/" +
+          this.paperId +
+          "?$key=" +
+          key +
+          "&$token=" +
+          token
+      );
+    },
+    initReduplicateQuesColor() {
+      var colorCount = this.reduplicateQuestions.length;
+      if (colorCount > 20) {
+        this.reduplicateQuesColor = randomColor({
+          luminosity: "bright",
+          count: colorCount,
+        });
+      } else {
+        this.reduplicateQuesColor = this.defaultColor;
+      }
+    },
+    getSubQuesEditId(paperDetailUnit, subQuestion) {
+      return paperDetailUnit.question.id + "_" + subQuestion.quesParams.number;
+    },
+    //打开考试说明编辑框
+    openEditExamPaperRemark() {
+      if (this.paper.examRemark) {
+        this.examRemark = this.paper.examRemark;
+      } else {
+        this.examRemark = "";
+      }
+      this.paperRemarkDialog = true;
+    },
+    //保存考试说明
+    savePaperRemark() {
+      this.paper.examRemark = this.examRemark;
+      this.savePaper();
+      this.paperRemarkDialog = false;
+    },
+    //关闭考试说明编辑框
+    closPaperRemark() {
+      this.examRemark = "";
+      this.paperRemarkDialog = false;
+    },
+    //保存试卷
+    savePaper() {
+      this.loading = true;
+      this.$http
+        .put(QUESTION_API + "/paper", this.paper)
+        .then(() => {
+          this.$notify({
+            message: "保存成功",
+            type: "success",
+          });
+          this.loading = false;
+          this.initPaper();
+        })
+        .catch((error) => {
+          this.loading = false;
+          this.$notify({
+            type: "error",
+            message: error.response.data.desc,
+          });
+        });
+    },
+    //删除试卷
+    deletePaper(id) {
+      this.$confirm("确认删除试卷吗?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http.delete(QUESTION_API + "/paper/" + id).then(
+          () => {
+            this.$notify({
+              message: "删除成功",
+              type: "success",
+            });
+            this.back();
+          },
+          (error) => {
+            this.$notify({
+              message: error.response.data.desc,
+              type: "error",
+              title: "错误",
+            });
+            this.loading = false;
+          }
+        );
+      });
+    },
+    //打开上传音频弹框
+    openDialog() {
+      this.checkResult = false;
+      this.isUpload = true;
+      if (document.getElementById("radioFile")) {
+        document.getElementById("radioFile").value = "";
+      }
+      this.dialogRadioFile = true;
+      this.fileList = [];
+    },
+    //关闭音频弹框
+    closeAudioDialog() {
+      this.dialogRadioFile = this.uploadAudioLoading;
+    },
+    //返回
+    back() {
+      if (sessionStorage.getItem("question_back") == "true") {
+        this.$router.push({
+          path: "/questions/" + this.parentView + "/0",
+        });
+      } else {
+        this.$router.push({
+          path: "/questions/" + this.parentView + "/1",
+        });
+      }
+    },
+    paperDetailShow(paperDetail) {
+      if (this.reduplicateGroup.length == 0) {
+        return true;
+      }
+      let paperDetailUnits = paperDetail.paperDetailUnits;
+      for (let i = 0, imax = paperDetailUnits.length; i < imax; i++) {
+        for (var j = 0, jmax = this.reduplicateGroup.length; j < jmax; j++) {
+          if (paperDetailUnits[i].id == this.reduplicateGroup[j]) {
+            return true;
+          }
+        }
+      }
+      return false;
+    },
+    //上传文件检查
+    checkFile() {
+      this.fileNameList = [];
+      //读取选取的文件夹里面的文件
+      this.checkResult = true;
+      var files = document.getElementById("radioFile").files;
+      if (files.length == 0) {
+        this.message = "请选择音频文件夹!";
+        return;
+      }
+      var size = 0;
+      var isGo = false;
+      //取到所有文件的文件名
+      for (var i = 0; i < files.length; i++) {
+        this.fileNameList.push(files[i].name);
+        if (files[i].size > 5 * 1024 * 1024) {
+          isGo = true;
+          break;
+        }
+        size = files[i].size + size;
+      }
+      if (isGo) {
+        this.message = "上传单个文件不能超过5M";
+        this.isUpload = true;
+      }
+      if (size > 50 * 1024 * 1024) {
+        this.message = "上传文件总和不能超过50M";
+        this.isUpload = true;
+        return;
+      }
+      this.$http
+        .post(
+          QUESTION_API + "/checkRadioFile/" + this.paperId,
+          this.fileNameList
+        )
+        .then((response) => {
+          console.log("response:", response);
+          this.message = response.data.errorMsg;
+          if (this.message == "OK") {
+            this.message = "OK!";
+            this.isUpload = false;
+          } else {
+            this.isUpload = true;
+          }
+        })
+        .catch((error) => {
+          console.log(error);
+        });
+    },
+    //读取文件
+    uploadAudioFile() {
+      let param = new FormData();
+      var fileList = document.getElementById("radioFile").files;
+      //循环添加到formData中
+      for (var i = 0; i < fileList.length; i++) {
+        var file = fileList[i];
+        param.append("files", file, file.name);
+      }
+      let config = {
+        headers: { "Content-Type": "multipart/form-data" },
+      };
+      this.$http
+        .post(QUESTION_API + "/uploadRadio/" + this.paperId, param, config)
+        .then(() => {
+          this.dialogRadioFile = false;
+          this.uploadAudioLoading = false;
+          this.checkResult = false;
+          this.isUpload = true;
+          document.getElementById("radioFile").value = "";
+          this.initPaper();
+        })
+        .catch((error) => {
+          this.message = error.response.data.desc;
+          this.uploadAudioLoading = false;
+        });
+    },
+  },
+};
+</script>
+<style scoped src="../styles/EditPaper.css">
+.property_with {
+  width: 100px;
+}
+.ck-toolbar {
+  z-index: 9999;
+}
+#app {
+  background-color: white !important;
+}
+</style>
+<style scoped src="../styles/Common.css"></style>

+ 649 - 0
src/modules/questions/views/EditSelectQuestion.vue

@@ -0,0 +1,649 @@
+<template>
+  <div id="editSelectApp" v-loading="fullscreenLoading">
+    <section class="content">
+      <!-- 头信息 -->
+      <h3 class="box-title">
+        <span v-if="!quesModel.id"
+          ><LinkTitlesCustom :current-paths="['试题管理', '试题新增']"
+        /></span>
+        <span v-if="quesModel.id"
+          ><LinkTitlesCustom :current-paths="['试题管理', '试题修改']"
+        /></span>
+      </h3>
+      <!-- 正文信息 -->
+      <div class="box-body">
+        <el-form
+          ref="quesModel"
+          :model="quesModel"
+          :rules="rules"
+          label-width="100px"
+          class="demo-ruleForm"
+          label-position="right"
+        >
+          <el-form-item label="题型">
+            <el-select
+              v-model="quesModel.questionType"
+              :disabled="true"
+              placeholder="请输入题型"
+            >
+              <el-option
+                v-for="item in questionTypes"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <!--
+              <el-form-item label="分数">
+                  <el-input placeholder="分数" v-model="quesModel.score" style="width:50px;"></el-input>
+              </el-form-item>
+            -->
+          <!-- created by weiwenhai -->
+          <el-form-item label="难度">
+            <el-select
+              v-model="quesModel.difficultyDegree"
+              placeholder="请输入难度"
+            >
+              <el-option
+                v-for="item in difficultyDegreeList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="公开度">
+            <el-select v-model="quesModel.publicity" placeholder="请输入公开度">
+              <el-option
+                v-for="item in publicityList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item
+            v-if="quesModel.questionType == 'TEXT_ANSWER_QUESTION'"
+            label="作答类型"
+          >
+            <el-select v-model="quesModel.answerType">
+              <el-option
+                v-for="item in answerTypes"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="属性列表">
+            <el-tooltip
+              v-for="(content, index) in quesModel.quesProperties"
+              :key="index"
+              placement="top"
+            >
+              <div slot="content">
+                <span v-if="content.firstProperty != null"
+                  >一级属性:{{ content.firstProperty.name }}</span
+                ><br />
+                <span v-if="content.secondProperty != null"
+                  >二级属性:{{ content.secondProperty.name }}</span
+                >
+              </div>
+              <el-tag
+                :key="content.id"
+                style="margin-right: 5px"
+                closable
+                type="primary"
+                @close="handleClose(content)"
+              >
+                {{ content.coursePropertyName }}
+              </el-tag>
+            </el-tooltip>
+          </el-form-item>
+          <el-row :gutter="20">
+            <el-col :xs="6" :sm="6" :md="6" :lg="6">
+              <el-form-item label="属性名">
+                <el-select
+                  v-model="coursePropertyName"
+                  placeholder="属性名"
+                  class="property_with"
+                  @change="searchFirst"
+                >
+                  <el-option
+                    v-for="item in coursePropertyList"
+                    :key="item.name"
+                    :label="item.name"
+                    :value="item.name"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="6" :sm="6" :md="6" :lg="6">
+              <el-form-item label="一级">
+                <el-select
+                  v-model="firstPropertyId"
+                  placeholder="一级"
+                  class="property_with"
+                  @change="searchSecond"
+                >
+                  <el-option
+                    v-for="item in firstPropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="6" :sm="6" :md="6" :lg="6">
+              <el-form-item label="二级">
+                <el-select
+                  v-model="secondPropertyId"
+                  placeholder="二级"
+                  class="property_with"
+                >
+                  <el-option
+                    v-for="item in secondPropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :xs="3" :sm="3" :md="3" :lg="3">
+              <el-form-item>
+                <el-button
+                  type="primary"
+                  style="margin-left: -30px"
+                  @click="insertProperty"
+                  ><i class="el-icon-plus"></i> 新增属性</el-button
+                >
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!-- end -->
+          <el-form-item label="题干" prop="quesBody">
+            <ckeditor v-model="quesModel.quesBody"></ckeditor>
+          </el-form-item>
+          <el-form-item
+            v-for="(option, index) in quesModel.quesOptions"
+            :key="option.number"
+          >
+            <el-col :span="1">
+              <el-radio
+                v-if="quesModel.questionType === 'SINGLE_ANSWER_QUESTION'"
+                v-model="singleRightAnswer"
+                :label="index | optionOrderWordFilter"
+              ></el-radio>
+              <el-checkbox
+                v-if="quesModel.questionType === 'MULTIPLE_ANSWER_QUESTION'"
+                v-model="multipleRightAnswer"
+                :label="index | optionOrderWordFilter"
+              ></el-checkbox>
+            </el-col>
+            <el-col :span="20">
+              <ckeditor v-model="option.optionBody"></ckeditor>
+            </el-col>
+            <el-col :span="2">
+              <i
+                class="el-icon-delete"
+                title="删除"
+                @click.prevent="removeQuesOption(option)"
+              ></i>
+            </el-col>
+          </el-form-item>
+          <el-form-item label="答案">
+            <el-col> <span v-html="answer"></span> </el-col>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="addQuesOption"
+              ><i class="el-icon-plus"></i> 新增选项</el-button
+            >
+            <el-button
+              type="primary"
+              icon="check"
+              :disabled="saveDisabled"
+              @click="submitForm('quesModel')"
+              >保存</el-button
+            >
+            <el-button
+              type="primary"
+              icon="caret-left"
+              @click="backToQuesList()"
+              ><i class="el-icon-arrow-left"></i> 返回列表
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </section>
+  </div>
+</template>
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { isEmptyStr, QUESTION_TYPES } from "../constants/constants";
+import ckeditor from "../component/ckeditor.vue";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+
+export default {
+  name: "EditSelectApp",
+  components: { ckeditor, LinkTitlesCustom },
+  data() {
+    return {
+      questionTypes: QUESTION_TYPES,
+      fullscreenLoading: false,
+      paperId: "",
+      paperDetailId: "",
+      questionId: "",
+      quesModel: {
+        quesBody: "",
+        quesOptions: [],
+        quesAnswer: "",
+        questionType: "",
+        courseName: "",
+        courseNo: "",
+        difficultyDegree: "",
+        publicity: true,
+        answerType: "",
+        quesProperties: [],
+        score: 1,
+      },
+      difficultyDegreeList: [
+        { label: 0.1, value: 0.1 },
+        { label: 0.2, value: 0.2 },
+        { label: 0.3, value: 0.3 },
+        { label: 0.4, value: 0.4 },
+        { label: 0.5, value: 0.5 },
+        { label: 0.6, value: 0.6 },
+        { label: 0.7, value: 0.7 },
+        { label: 0.8, value: 0.8 },
+        { label: 0.9, value: 0.9 },
+        { label: 1.0, value: 1.0 },
+      ],
+      publicityList: [
+        { label: "公开", value: true },
+        { label: "非公开", value: false },
+      ],
+      answerTypes: [
+        { label: "文本", value: "DIVERSIFIED_TEXT" },
+        { label: "音频", value: "SINGLE_AUDIO" },
+      ],
+      coursePropertyList: [],
+      coursePropertyName: "", //课程属性名
+      firstPropertyList: [], //一级属性集合
+      firstPropertyId: "", //一级属性id
+      secondPropertyList: [], //二级属性集合
+      secondPropertyId: "", //二级属性id
+      //验证
+      rules: {
+        // quesBody: [
+        //     { required: true, message: '请输入题干', trigger: 'blur'}
+        // ]
+      },
+      singleRightAnswer: "", //接收单选答案
+      multipleRightAnswer: [], //接收多选答案
+    };
+  },
+  computed: {
+    answer() {
+      if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+        return this.singleRightAnswer;
+      } else if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+        var obj = this.multipleRightAnswer;
+        return obj.sort().toString();
+      }
+      return this.quesModel.quesAnswer;
+    },
+    saveDisabled: function () {
+      if (!this.questionId && this.quesModel.quesOptions.length == 0) {
+        return true;
+      }
+      return false;
+    },
+  },
+  created() {
+    let questionId = this.$route.params.id;
+    if (questionId) {
+      this.questionId = questionId;
+      this.quesModel["id"] = questionId;
+      this.findQuestionById(questionId);
+    }
+    this.paperId = this.$route.params.paperId;
+    this.paperDetailId = this.$route.params.paperDetailId;
+    this.courseNo = this.$route.params.courseNo;
+    let questionType = this.$route.params.questionType;
+    if (questionType) {
+      this.quesModel.questionType = questionType;
+    }
+    if (this.courseNo) {
+      this.$http
+        .get(QUESTION_API + "/courseProperty/enable/" + this.courseNo)
+        .then((response) => {
+          this.coursePropertyList = response.data;
+        });
+    }
+
+    if (isEmptyStr(this.quesModel.answerType)) {
+      this.quesModel.answerType = "DIVERSIFIED_TEXT";
+    }
+  },
+  methods: {
+    findQuestionById(questionId) {
+      this.$http
+        .get(QUESTION_API + "/question/" + questionId)
+        .then((response) => {
+          this.quesModel = response.data;
+          if (this.quesModel.quesOptions) {
+            for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+              var option = this.quesModel.quesOptions[i];
+              if (
+                this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" &&
+                option.isCorrect == 1
+              ) {
+                this.singleRightAnswer = String.fromCharCode(65 + i);
+              }
+              if (
+                this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION" &&
+                option.isCorrect == 1
+              ) {
+                this.multipleRightAnswer.push(String.fromCharCode(65 + i));
+              }
+            }
+          }
+
+          if (isEmptyStr(this.quesModel.answerType)) {
+            this.quesModel.answerType = "DIVERSIFIED_TEXT";
+          }
+
+          this.initCourseProperty();
+        });
+    },
+    submitForm(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.setRightAnswer();
+          if (this.questionId) {
+            this.editQuestion();
+          } else {
+            this.saveNewQuestion();
+          }
+        } else {
+          return false;
+        }
+      });
+    },
+    //修改试题
+    editQuestion() {
+      if (this.quesModel.quesOptions.length == 0) {
+        this.$confirm("无选项将删除该试题, 是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        })
+          .then(() => {
+            this.fullscreenLoading = true;
+            this.$http
+              .delete(
+                QUESTION_API + "/paper/deleteQuestion/" + this.quesModel.id
+              )
+              .then((response) => {
+                if (response.data.length > 0) {
+                  this.deleteInfo =
+                    "该试题被试卷:" +
+                    response.data.join(" , ") +
+                    "使用,不能删除";
+                  this.deleteDialogVisible = true;
+                } else {
+                  this.$notify({
+                    message: "删除成功",
+                    type: "success",
+                  });
+                  this.$router.push({ path: "/questions/question_list/0" });
+                }
+              });
+          })
+          .catch(() => {});
+      } else {
+        this.fullscreenLoading = true;
+        this.$http.put(QUESTION_API + "/question", this.quesModel).then(() => {
+          this.$notify({
+            message: "保存成功",
+            type: "success",
+          });
+          this.fullscreenLoading = false;
+        });
+      }
+    },
+    //新增试题
+    saveNewQuestion() {
+      if (!this.quesModel.difficultyDegree) {
+        this.$notify({
+          message: "请选择试题难度",
+          type: "error",
+        });
+        return false;
+      }
+      this.fullscreenLoading = true;
+      this.$http
+        .post(
+          QUESTION_API +
+            "/paper/addQuestion/" +
+            this.paperId +
+            "/" +
+            this.paperDetailId,
+          this.quesModel
+        )
+        .then(() => {
+          this.fullscreenLoading = false;
+          this.$notify({
+            type: "success",
+            message: "保存成功",
+          });
+          this.$router.push({ path: "/questions/question_list/0" });
+        })
+        .catch(() => {
+          this.fullscreenLoading = false;
+        });
+    },
+    //新增选项
+    addQuesOption() {
+      this.quesModel.quesOptions.push({
+        number: "",
+        optionBody: "",
+        isCorrect: "",
+      });
+      for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+        this.quesModel.quesOptions[i]["number"] = i + 1;
+      }
+    },
+    //删除选项
+    removeQuesOption(option) {
+      this.singleRightAnswer = "";
+      this.multipleRightAnswer = [];
+      let index = this.quesModel.quesOptions.indexOf(option);
+      if (index !== -1) {
+        this.quesModel.quesOptions.splice(index, 1);
+      }
+      if (this.quesModel.quesOptions.length > 0) {
+        for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+          var quesOption = this.quesModel.quesOptions[i];
+          quesOption["number"] = i + 1;
+          if (quesOption.isCorrect == 1) {
+            var answerOrderNum = String.fromCharCode(65 + i);
+            if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+              this.singleRightAnswer = answerOrderNum;
+            }
+            if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+              this.multipleRightAnswer.push(answerOrderNum);
+            }
+          }
+        }
+      }
+    },
+    //在正确的option上设置isCorrect=1
+    setRightAnswer() {
+      if (this.quesModel.quesOptions.length == 0) {
+        return false;
+      }
+      for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+        var option = this.quesModel.quesOptions[i];
+        var answerOrderNum = String.fromCharCode(65 + i);
+        if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+          option["isCorrect"] =
+            answerOrderNum == this.singleRightAnswer ? 1 : 0;
+        }
+        if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+          option["isCorrect"] =
+            this.multipleRightAnswer.indexOf(answerOrderNum) > -1 ? 1 : 0;
+        }
+      }
+    },
+    backToQuesList() {
+      this.$router.push({
+        path: "/questions/question_list/1",
+      });
+    },
+    //查询所有课程属性名
+    initCourseProperty() {
+      var code = this.quesModel.course.code;
+      this.$http
+        .get(QUESTION_API + "/courseProperty/enable/" + code)
+        .then((response) => {
+          this.coursePropertyList = response.data;
+        });
+    },
+    //查询一级属性
+    searchFirst() {
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.secondPropertyList = [];
+      for (let courseProperty of this.coursePropertyList) {
+        if (courseProperty.name == this.coursePropertyName) {
+          this.$http
+            .get(QUESTION_API + "/property/first/" + courseProperty.id)
+            .then((response) => {
+              this.firstPropertyList = response.data;
+            });
+        }
+      }
+    },
+    //查询二级属性
+    searchSecond() {
+      this.secondPropertyId = "";
+      if (this.firstPropertyId) {
+        this.$http
+          .get(QUESTION_API + "/property/second/" + this.firstPropertyId)
+          .then((response) => {
+            this.secondPropertyList = response.data;
+          });
+      }
+    },
+    //新增属性
+    insertProperty() {
+      if (!this.checkInsertPro()) {
+        return false;
+      }
+      var quesProperty = {
+        id: "",
+        coursePropertyName: "",
+        firstProperty: {},
+        secondProperty: {},
+      };
+      if (
+        this.quesModel.quesProperties === undefined ||
+        this.quesModel.quesProperties === null ||
+        this.quesModel.quesProperties.length == 0
+      ) {
+        this.quesModel.quesProperties = [];
+      }
+      quesProperty.id =
+        this.coursePropertyName +
+        "-" +
+        this.firstPropertyId +
+        "-" +
+        this.secondPropertyId;
+      for (let quesPro of this.quesModel.quesProperties) {
+        if (quesPro.id == quesProperty.id) {
+          this.$notify({
+            message: "该属性已存在,请重新选择",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      quesProperty.coursePropertyName = this.coursePropertyName;
+      //取到一级属性对象
+      for (let property of this.firstPropertyList) {
+        if (property.id == this.firstPropertyId) {
+          quesProperty.firstProperty = property;
+        }
+      }
+      //判断是否有二级属性
+      if (
+        this.secondPropertyList != undefined &&
+        this.secondPropertyList.length > 0
+      ) {
+        if (!this.secondPropertyId) {
+          this.$notify({
+            message: "请选择二级属性",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      //取到二级属性对象
+      for (let property of this.secondPropertyList) {
+        if (property.id == this.secondPropertyId) {
+          quesProperty.secondProperty = property;
+        }
+      }
+      this.quesModel.quesProperties.push(quesProperty);
+      this.quesModel = Object.assign({}, this.quesModel);
+
+      //清空下拉框
+      this.coursePropertyName = "";
+      this.firstPropertyId = "";
+      this.secondPropertyId = "";
+      this.firstPropertyList = [];
+      this.secondPropertyList = [];
+    },
+    //删除属性
+    handleClose(tag) {
+      this.quesModel.quesProperties.splice(
+        this.quesModel.quesProperties.indexOf(tag),
+        1
+      );
+      this.quesModel = Object.assign({}, this.quesModel);
+    },
+    //新增属性验证
+    checkInsertPro() {
+      if (!this.coursePropertyName) {
+        this.$notify({
+          message: "请选择属性",
+          type: "error",
+        });
+        return false;
+      }
+      if (!this.firstPropertyId) {
+        this.$notify({
+          message: "请选择一级属性",
+          type: "error",
+        });
+        return false;
+      }
+      return true;
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 809 - 0
src/modules/questions/views/ExportStructure.vue

@@ -0,0 +1,809 @@
+<template>
+  <section class="content">
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="考试类型">
+              <el-select
+                v-model="formSearch.examType"
+                class="search_width"
+                filterable
+                clearable
+                placeholder="请选择"
+                size="small"
+                @change="searchExams"
+              >
+                <el-option
+                  v-for="item in EXAM_TYPES"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="考试名称">
+              <el-select
+                v-model="formSearch.examId"
+                class="search_width"
+                filterable
+                :remote-method="getExams"
+                remote
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in examList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="导出类型">
+              <el-select
+                v-model="formSearch.exportType"
+                class="search_width"
+                filterable
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in EXPORT_TYPES"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" @click="resetForm"
+                ><i class="el-icon-refresh"></i> 重 置</el-button
+              >
+              <el-button size="small" type="primary" @click="add"
+                ><i class="el-icon-plus"></i> 新增</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div
+        style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+      ></div>
+      <!-- 页面列表 -->
+      <el-table border :data="tableData">
+        <el-table-column label="考试类型">
+          <template slot-scope="scope">
+            <span>{{ scope.row.examType | examTypesFilter }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="考试名称">
+          <template slot-scope="scope">
+            <span>{{ scope.row.examName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="导出类型">
+          <template slot-scope="scope">
+            <span>{{ scope.row.exportType | exportTypesFilter }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题数">
+          <template slot-scope="scope">
+            <div
+              v-for="(item, index) in scope.row.questionTypeNums"
+              v-show="scope.row.exportType == 'NORMAL'"
+              :key="index"
+            >
+              <span>{{ item.questionType | questionType }}</span>
+              <span>{{ item.quantity }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="175">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="copy(scope.row)"
+                ><i class="el-icon-edit"></i>复用</el-button
+              >
+              <el-button size="mini" type="danger" @click="del(scope.row)"
+                ><i class="el-icon-delete"></i> 删除</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <!-- 复用窗口 -->
+    <el-dialog
+      title="试卷结构复用"
+      :visible.sync="dialogCopyModel"
+      width="500px"
+    >
+      <el-form
+        :model="exportStructureCopy"
+        label-position="right"
+        label-width="120px"
+      >
+        <el-tabs> <el-tab-pane label="原结构信息"></el-tab-pane> </el-tabs>
+        <el-form-item label="考试类型">
+          <el-select
+            v-model="exportStructureCopy.examTypeOld"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in EXAM_TYPES"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="考试名称">
+          <el-select
+            v-model="exportStructureCopy.examIdOld"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in examList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="导出类型">
+          <el-select
+            v-model="exportStructureCopy.exportTypeOld"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in EXPORT_TYPES"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-tabs> <el-tab-pane label="新结构信息"></el-tab-pane> </el-tabs>
+        <el-form-item label="考试类型">
+          <el-select
+            v-model="exportStructureCopy.examType"
+            placeholder="请选择"
+            @change="searchExamId"
+          >
+            <el-option
+              v-for="item in EXAM_TYPES"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+          <span
+            v-if="check_value(exportStructureCopy.examType)"
+            class="error_message error_other"
+            >请选择考试类型</span
+          >
+        </el-form-item>
+        <el-form-item label="考试名称">
+          <el-select v-model="exportStructureCopy.examId" placeholder="请选择">
+            <el-option
+              v-for="item in examListNew"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            ></el-option>
+          </el-select>
+          <span
+            v-if="check_value(exportStructureCopy.examId)"
+            class="error_message error_other"
+            >请选择考试名称</span
+          >
+        </el-form-item>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button type="primary" @click="saveCopy">保 存</el-button>
+          <!-- <el-button @click="resetForm3"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          > -->
+          <el-button type="primary" @click="cancleCopy"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+    <!-- 新增窗口 -->
+    <el-dialog title="新增导出结构" :visible.sync="dialogModel" width="500px">
+      <el-form
+        :model="exportStructure"
+        label-position="right"
+        label-width="78px"
+        :inline="true"
+      >
+        <el-row>
+          <div style="color: #f56c6c; margin-bottom: -30px">*</div>
+          <el-form-item label="考试类型">
+            <el-select
+              v-model="exportStructure.examType"
+              placeholder="请选择"
+              @change="searchExportStructure('examType')"
+            >
+              <el-option
+                v-for="item in EXAM_TYPES"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+            <span
+              v-if="check_value(exportStructure.examType)"
+              class="error_message error_other"
+              >请选择考试类型</span
+            >
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <div style="color: #f56c6c; margin-bottom: -30px; margin-top: 8px">
+            *
+          </div>
+          <el-form-item label="考试名称">
+            <el-select
+              v-model="exportStructure.examId"
+              filterable
+              placeholder="请选择"
+              @change="searchExportStructure('examId')"
+            >
+              <el-option
+                v-for="item in examListByExamType"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+            <span
+              v-if="check_value(exportStructure.examId)"
+              class="error_message error_other"
+              >请选择考试名称</span
+            >
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <div style="color: #f56c6c; margin-bottom: -30px; margin-top: 8px">
+            *
+          </div>
+          <el-form-item label="导出类型">
+            <el-select
+              v-model="exportStructure.exportType"
+              placeholder="请选择"
+              :disabled="button"
+            >
+              <el-option
+                v-for="item in EXPORT_TYPES"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+            <span
+              v-if="check_value(exportStructure.exportType)"
+              class="error_message error_other"
+              >请选择导出类型</span
+            >
+          </el-form-item>
+        </el-row>
+        <!-- 判断机考还是传统 -->
+        <div v-if="exportStructure.exportType == 'NORMAL'">
+          <div
+            v-for="(item, index) in exportStructure.questionTypeNums"
+            :key="index"
+          >
+            <el-row>
+              <div
+                style="
+                  color: #f56c6c;
+                  margin-bottom: -30px;
+                  margin-left: 26px;
+                  margin-top: 8px;
+                "
+              >
+                *
+              </div>
+              <el-form-item
+                :label="item.questionType | questionType"
+                label-width="78px"
+              >
+                <el-input
+                  v-model="item.quantity"
+                  style="width: 220px"
+                  :disabled="button"
+                ></el-input>
+                <span
+                  v-if="check(item.quantity)"
+                  class="error_message error_other"
+                >
+                  请输入正整数或0
+                </span>
+              </el-form-item>
+            </el-row>
+          </div>
+        </div>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button type="primary" :disabled="button" @click="save"
+            >保 存</el-button
+          >
+          <!-- <el-button @click="resetForm2"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          > -->
+          <el-button type="primary" @click="cancel"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </section>
+</template>
+<script>
+import { QUESTION_API, EXAM_WORK_API } from "@/constants/constants";
+import { EXAM_TYPES, EXPORT_TYPES } from "../constants/constants";
+import { mapState } from "vuex";
+import _ from "lodash";
+export default {
+  data() {
+    return {
+      isShow: false,
+      loading: false,
+      dialogModel: false,
+      dialogCopyModel: false,
+      formSearch: {
+        examType: null,
+        examId: null,
+        exportType: null,
+      },
+      exportStructure: {
+        examType: "",
+        examId: "",
+        examName: "",
+        exportType: "",
+        useable: "1", //默认启用
+        questionTypeNums: [
+          {
+            questionType: "SINGLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "MULTIPLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "BOOL_ANSWER_QUESTION",
+            quantity: "",
+          },
+        ],
+      },
+      exportStructureCopy: {
+        examTypeOld: "",
+        examIdOld: "",
+        exportTypeOld: "",
+        examType: "",
+        examId: "",
+        useable: "1",
+        questionTypeNums: [
+          {
+            questionType: "SINGLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "MULTIPLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "BOOL_ANSWER_QUESTION",
+            quantity: "",
+          },
+        ],
+      },
+      EXAM_TYPES: EXAM_TYPES,
+      EXPORT_TYPES: EXPORT_TYPES,
+      examList: [],
+      examListByExamType: [],
+      tableData: [],
+      pageSize: 10,
+      currentPage: 1,
+      total: 10,
+      button: false,
+      examListNew: [],
+      disMessage: false,
+      reShow: false,
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+  },
+  created() {
+    this.getExams("");
+    this.search();
+  },
+  methods: {
+    resetForm() {
+      this.formSearch = {
+        examType: null,
+        examId: null,
+        exportType: null,
+      };
+    },
+    searchFrom() {
+      this.currentPage = 1;
+      this.search();
+    },
+    search() {
+      if (this.formSearch.examType === "") {
+        this.formSearch.examType = null;
+      }
+      this.tableData = [];
+      this.loading = true;
+      var url =
+        QUESTION_API +
+        "/findPageByExportStructure/" +
+        this.currentPage +
+        "/" +
+        this.pageSize;
+      this.$http.post(url, this.formSearch).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.loading = false;
+      });
+    },
+    //新增考试结构
+    add() {
+      this.dialogModel = true;
+      this.button = false;
+      this.isShow = false;
+      this.exportStructure = {
+        examType: "",
+        examId: "",
+        examName: "",
+        exportType: "",
+        useable: "1", //默认启用
+        questionTypeNums: [
+          {
+            questionType: "SINGLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "MULTIPLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "BOOL_ANSWER_QUESTION",
+            quantity: "",
+          },
+        ],
+      };
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.search();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.search();
+    },
+    closeQuesDialog() {
+      this.dialogModel = false;
+    },
+    //表单验证
+    check(value) {
+      if (this.disMessage) {
+        return false;
+      }
+      if (this.reShow) {
+        return false;
+      }
+      var r = /^\+?[1-9][0-9]*$/;
+      if ((r.test(value) || value === "0") && this.isShow) {
+        return false;
+      }
+      return true;
+    },
+    check_value(value) {
+      if (!value && this.isShow) {
+        return true;
+      }
+      return false;
+    },
+    save() {
+      this.reShow = false;
+      this.isShow = true;
+      if (this.check_value(this.exportStructure.examType)) {
+        return false;
+      }
+      if (this.check_value(this.exportStructure.examId)) {
+        return false;
+      }
+      if (this.check_value(this.exportStructure.exportType)) {
+        return false;
+      }
+      if (this.exportStructure.exportType == "NORMAL") {
+        for (var i = 0; i < this.exportStructure.questionTypeNums.length; i++) {
+          if (this.check(this.exportStructure.questionTypeNums[i].quantity)) {
+            return false;
+          }
+        }
+      }
+      let examId = this.exportStructure.examId;
+      this.exportStructure.examName = _.filter(this.examList, function (item) {
+        return item.id == examId;
+      })[0].name;
+      this.$http
+        .post(QUESTION_API + "/saveExportStructure", this.exportStructure)
+        .then(() => {
+          this.$notify({
+            title: "提示",
+            message: "保存成功",
+            type: "success",
+          });
+          this.closeQuesDialog();
+          this.search();
+        });
+    },
+    //重置2
+    resetForm2() {
+      this.exportStructure = {
+        examType: "",
+        examId: "",
+        examName: "",
+        exportType: "",
+        useable: "1", //默认启用
+        questionTypeNums: [
+          {
+            questionType: "SINGLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "MULTIPLE_ANSWER_QUESTION",
+            quantity: "",
+          },
+          {
+            questionType: "BOOL_ANSWER_QUESTION",
+            quantity: "",
+          },
+        ],
+      };
+      this.isShow = false;
+      this.button = false;
+    },
+    //取消
+    cancel() {
+      this.isShow = false;
+      this.button = false;
+      this.dialogModel = false;
+    },
+    //取消复用
+    cancleCopy() {
+      this.isShow = false;
+      this.dialogCopyModel = false;
+    },
+    //重置3
+    resetForm3() {
+      this.exportStructureCopy.examType = "";
+      this.exportStructureCopy.examId = "";
+      this.isShow = false;
+    },
+    //复用
+    copy(row) {
+      this.dialogCopyModel = true;
+      this.exportStructureCopy.examTypeOld = row.examType;
+      this.exportStructureCopy.examIdOld = parseInt(row.examId);
+      this.exportStructureCopy.exportTypeOld = row.exportType;
+      this.exportStructureCopy.questionTypeNums = row.questionTypeNums;
+      this.exportStructureCopy.examType = "";
+      this.exportStructureCopy.examId = "";
+    },
+    //新增的时候,先查询
+    searchExportStructure(type) {
+      this.disMessage = false;
+      if (type == "examType") {
+        this.$http
+          .get(
+            EXAM_WORK_API +
+              "/exam/queryByNameLike?enable=true&name=&examTypes=" +
+              this.exportStructure.examType
+          )
+          .then((response) => {
+            this.examListByExamType = response.data;
+            this.exportStructure.examId = "";
+            this.resetExportStructure();
+          });
+      }
+      if (this.exportStructure.examType && this.exportStructure.examId) {
+        var url =
+          QUESTION_API + "/findExportStructure/" + this.exportStructure.examId;
+        this.$http
+          .get(url)
+          .then((response) => {
+            console.log(response);
+            if (!response.data.id) {
+              //如果没有数据
+              this.resetExportStructure();
+            } else {
+              this.button = true;
+              this.exportStructure.exportType = response.data.exportType;
+              this.exportStructure.useable = response.data.useable
+                ? response.data.useable.toString()
+                : "1";
+              for (
+                var i = 0;
+                i < this.exportStructure.questionTypeNums.length;
+                i++
+              ) {
+                this.exportStructure.questionTypeNums[i].quantity =
+                  response.data.questionTypeNums[i].quantity;
+              }
+              this.disMessage = true;
+              console.log("非空对象");
+            }
+          })
+          .catch(() => {});
+      }
+    },
+    resetExportStructure() {
+      this.button = false;
+      this.reShow = true;
+      this.exportStructure.exportType = "";
+      this.exportStructure.useable = "1";
+      for (var i = 0; i < this.exportStructure.questionTypeNums.length; i++) {
+        this.exportStructure.questionTypeNums[i].quantity = "";
+      }
+    },
+    //点击复用,选择
+    searchExamId() {
+      if (this.exportStructureCopy.examType) {
+        this.$http
+          .get(
+            EXAM_WORK_API +
+              "/exam/queryByNameLike?enable=true&name=&examTypes=" +
+              this.exportStructureCopy.examType
+          )
+          .then((response) => {
+            let examListFilter = response.data;
+            var url =
+              QUESTION_API +
+              "/findExportStructureByExamType/" +
+              this.exportStructureCopy.examType;
+            this.$http.get(url).then((response) => {
+              let examListOld = response.data;
+              //两个数组相减
+              this.examListNew = _.filter(examListFilter, function (exam) {
+                return examListOld.indexOf(exam.id.toString()) == -1;
+              });
+            });
+          });
+      }
+    },
+    //保存复用
+    saveCopy() {
+      this.isShow = true;
+      if (this.check_value(this.exportStructureCopy.examType) === true) {
+        return false;
+      }
+      if (this.check_value(this.exportStructureCopy.examId) === true) {
+        return false;
+      }
+      this.exportStructure.examType = this.exportStructureCopy.examType;
+      this.exportStructure.examId = this.exportStructureCopy.examId;
+      this.exportStructure.exportType = this.exportStructureCopy.exportTypeOld;
+      this.exportStructure.useable = this.exportStructureCopy.useable;
+      this.exportStructure.questionTypeNums = this.exportStructureCopy.questionTypeNums;
+      let examId = this.exportStructure.examId;
+      this.exportStructure.examName = _.filter(
+        this.examListNew,
+        function (item) {
+          return item.id == examId;
+        }
+      )[0].name;
+      this.$http
+        .post(QUESTION_API + "/saveExportStructure", this.exportStructure)
+        .then(() => {
+          this.$notify({
+            title: "提示",
+            message: "保存成功",
+            type: "success",
+          });
+          this.dialogCopyModel = false;
+          this.search();
+        });
+    },
+    //删除
+    del(row) {
+      this.$confirm("是否删除该条导出结构", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        var url = QUESTION_API + "/deleteExportStructure/" + row.id;
+        this.$http.delete(url).then(
+          () => {
+            this.$notify({
+              type: "success",
+              message: "删除成功!",
+            });
+            this.loading = false;
+            this.search();
+          },
+          () => {
+            this.$notify({
+              type: "error",
+              message: "删除失败!",
+            });
+            this.loading = false;
+          }
+        );
+      });
+      this.loading = false;
+    },
+    //查询考试
+    getExams(query) {
+      query = query.trim();
+      var examType = this.formSearch.examType;
+      if (this.formSearch.examType == null) {
+        examType = "";
+      }
+      this.$http
+        .get(
+          EXAM_WORK_API +
+            "/exam/queryByNameLike?enable=true&name=" +
+            query +
+            "&examTypes=" +
+            examType
+        )
+        .then((response) => {
+          this.examList = response.data;
+        });
+    },
+    searchExams() {
+      this.formSearch.examId = null;
+      this.getExams("");
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 666 - 0
src/modules/questions/views/ExportTemplate.vue

@@ -0,0 +1,666 @@
+<template>
+  <section class="content">
+    <div class="box-body">
+      <!-- 表单 -->
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-width="70px"
+        label-position="right"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item v-if="isSuperAdmin" label="学校">
+              <el-select
+                v-model="formSearch.rootOrgId"
+                clearable
+                filterable
+                placeholder="请选择"
+                class="search_width"
+                size="small"
+              >
+                <el-option
+                  v-for="item in rootOrgList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="模板名称">
+              <el-input
+                v-model="formSearch.fileName"
+                placeholder="请输入模板名称"
+                class="search_width"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="模板类型" prop="type" class="form-item">
+              <el-select
+                v-model="formSearch.type"
+                class="search_width"
+                :clearable="true"
+                size="small"
+              >
+                <el-option label="试卷导出" value="PAPER_EXPORT"> </el-option>
+                <el-option label="答案导出" value="ANWSER_EXPORT"> </el-option>
+                <el-option label="离线试卷" value="OUTLINE_PAPER_EXPORT">
+                </el-option>
+                <el-option label="试卷预览" value="PAPER_VIEW"> </el-option>
+                <el-option label="答案预览" value="ANWSER_VIEW"> </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button
+                size="small"
+                type="primary"
+                icon="el-icon-search"
+                @click="handleSearchBtn"
+              >
+                查询
+              </el-button>
+              <el-button
+                size="small"
+                type="primary"
+                icon="el-icon-plus"
+                @click="addFile"
+              >
+                新增
+              </el-button>
+              <el-button
+                size="small"
+                type="primary"
+                icon="el-icon-edit"
+                @click="operConfig"
+              >
+                配置
+              </el-button>
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <div class="block-seperator"></div>
+      <!-- 页面列表 -->
+      <el-table :data="tableData" border resizable stripe style="width: 100%">
+        <el-table-column prop="fileName" label="模板名称"> </el-table-column>
+        <el-table-column prop="suffix" width="100" label="文件类型">
+        </el-table-column>
+        <el-table-column width="150" prop="typeName" label="模板类型">
+        </el-table-column>
+        <el-table-column width="150" prop="creationTime" label="上传时间">
+        </el-table-column>
+        <el-table-column width="150" prop="createUser" label="上传人">
+        </el-table-column>
+        <el-table-column label="状态" width="70">
+          <template slot-scope="scope">
+            <span v-if="scope.row.enable">
+              <el-tooltip
+                class="item"
+                effect="dark"
+                content="启用"
+                placement="left"
+              >
+                <i class="el-icon-success" style="color: green"></i>
+              </el-tooltip>
+            </span>
+            <span v-else>
+              <el-tooltip
+                class="item"
+                effect="dark"
+                content="禁用"
+                placement="left"
+              >
+                <i class="el-icon-error" style="color: red"></i>
+              </el-tooltip>
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column :context="_self" label="操作" width="280">
+          <div slot-scope="scope">
+            <el-button
+              size="mini"
+              type="primary"
+              plain
+              icon="el-icon-download"
+              @click="downFile(scope.row)"
+            >
+              下载
+            </el-button>
+            <el-button
+              v-if="!scope.row.enable"
+              size="mini"
+              type="primary"
+              plain
+              @click="enable(scope.row)"
+            >
+              <i class="el-icon-check" aria-hidden="true"></i>启用
+            </el-button>
+            <el-button
+              v-if="scope.row.enable"
+              size="mini"
+              type="danger"
+              @click="disenable(scope.row)"
+            >
+              <i class="el-icon-close" aria-hidden="true"></i>禁用
+            </el-button>
+            <el-button
+              size="mini"
+              type="danger"
+              plain
+              icon="el-icon-remove-outline"
+              @click="deleteFile(scope.row)"
+            >
+              删除
+            </el-button>
+          </div>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          v-if="paginationShow"
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        />
+      </div>
+    </div>
+    <el-dialog
+      title="新增模板"
+      width="550px"
+      :visible.sync="fileModel"
+      :close-on-click-modal="false"
+      @close="closeFileModel"
+    >
+      <el-form
+        ref="fileForm"
+        :key="fileModelKey"
+        :inline="true"
+        :model="fileForm"
+        :rules="fileRules"
+        label-width="90px"
+      >
+        <el-row class="form-row">
+          <el-form-item label="模板名称" prop="templateName">
+            <el-input
+              v-model="fileForm.templateName"
+              class="pull-length"
+              placeholder="请输入模板名称"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row class="form-row">
+          <el-form-item label="模板类型" prop="type" class="form-item">
+            <el-select v-model="fileForm.type" :clearable="true" class="input">
+              <el-option label="试卷导出" value="PAPER_EXPORT"> </el-option>
+              <el-option label="答案导出" value="ANWSER_EXPORT"> </el-option>
+              <el-option label="离线试卷" value="OUTLINE_PAPER_EXPORT">
+              </el-option>
+              <el-option label="试卷预览" value="PAPER_VIEW"> </el-option>
+              <el-option label="答案预览" value="ANWSER_VIEW"> </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row class="form-row">
+          <el-form-item label="文件" prop="dataFile">
+            <el-input
+              v-model="fileForm.fileName"
+              class="pull-length"
+              :readonly="true"
+              placeholder="文件最大限制10M"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-upload
+              :before-upload="handleUploadFile"
+              accept=".ftl,.zip"
+              action="/upload"
+            >
+              <el-button
+                type="primary"
+                size="small"
+                icon="ios-cloud-upload-outline"
+                >请选择文件
+              </el-button>
+            </el-upload>
+          </el-form-item>
+        </el-row>
+        <el-row class="pull-center">
+          <el-button type="primary" :loading="fileForm.loading" @click="subFile"
+            >确定</el-button
+          >
+          <el-button @click="closeFileModel">取消</el-button>
+        </el-row>
+      </el-form>
+    </el-dialog>
+    <el-dialog
+      title="配置信息"
+      width="550px"
+      :visible.sync="configModel"
+      :close-on-click-modal="false"
+      @close="closeConfigModel"
+    >
+      <el-form
+        ref="configForm"
+        :key="configModelKey"
+        :inline="true"
+        :model="configForm"
+        :rules="configRules"
+        label-width="90px"
+      >
+        <el-row v-if="isSuperAdmin" class="form-row">
+          <el-form-item label="学校名称">
+            <el-input
+              v-model="configForm.orgName"
+              disabled="true"
+              class="pull-length"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row class="form-row">
+          <el-form-item label="配置名称" prop="serviceName">
+            <el-input
+              v-model="configForm.serviceName"
+              class="pull-length"
+              placeholder="请输入serviceName"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row class="pull-center">
+          <el-button
+            type="primary"
+            :loading="configForm.loading"
+            @click="subConfig"
+            >确定</el-button
+          >
+          <el-button @click="closeConfigModel">取消</el-button>
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </section>
+</template>
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants.js";
+import { mapState } from "vuex";
+
+export default {
+  name: "ExportTemplate",
+  data() {
+    var reg = /\.zip|\.ftl$/;
+    var validateFile = function (rule, value, callback) {
+      if (value) {
+        var upFileName = value.name;
+        if (value.size > 1024 * 1024 * 10) {
+          return callback(new Error("文件大小限制为10M"));
+        } else if (!upFileName.match(reg)) {
+          return callback(new Error("文件类型只能是ftl或zip"));
+        } else {
+          callback();
+        }
+      } else {
+        callback();
+      }
+    };
+    return {
+      rootOrgList: [],
+      loading: false,
+      fileModel: false,
+      fileModelKey: Math.random(),
+      configModel: false,
+      configModelKey: Math.random(),
+      paginationShow: false,
+      formSearch: {
+        rootOrgId: null,
+        fileName: "",
+        type: "",
+      },
+      fileForm: {
+        templateName: null,
+        type: null,
+        fileName: null,
+        dataFile: null,
+        loading: false,
+      },
+      configForm: {
+        serviceName: null,
+        orgName: null,
+        loading: false,
+      },
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      fileRules: {
+        templateName: [
+          {
+            required: true,
+            type: "string",
+            message: "请输入模板名称",
+            trigger: "change",
+          },
+          {
+            pattern: /^[^\\/\\?%#&=\\+]*$/,
+            message: "名称不能包含特殊字符 / ? % # & = +",
+            trigger: "change",
+          },
+        ],
+        type: [
+          {
+            required: true,
+            type: "string",
+            message: "请选择模板类型",
+            trigger: "change",
+          },
+        ],
+        dataFile: [
+          {
+            required: true,
+            type: "object",
+            message: "请选择文件",
+            trigger: "change",
+          },
+          {
+            validator: validateFile,
+            type: "object",
+            trigger: "change",
+          },
+        ],
+      },
+      configRules: {
+        serviceName: [
+          {
+            required: true,
+            type: "string",
+            message: "请输入配置名称",
+            trigger: "change",
+          },
+          {
+            pattern: /^[^\\/\\?%#&=\\+]*$/,
+            message: "名称不能包含特殊字符 / ? % # & = +",
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    isSuperAdmin() {
+      return this.user.roleList.some((role) => role.roleCode == "SUPER_ADMIN");
+    },
+  },
+  watch: {
+    "formSearch.rootOrgId": {
+      immediate: false,
+      handler(val) {
+        if (val == null) {
+          this.curDir = "";
+        } else {
+          this.curDir = "/";
+          this.formSearch.parentId = -1;
+          this.handleSearchBtn();
+        }
+      },
+    },
+  },
+  //初始化查询
+  created() {
+    this.init();
+  },
+  methods: {
+    downFile(row) {
+      window.location.href = row.fullFilePath;
+    },
+    enable(row) {
+      var url =
+        QUESTION_API +
+        "/exportTemplate/enable/" +
+        this.formSearch.rootOrgId +
+        "/" +
+        row.id;
+      this.$httpWithMsg.put(url).then(() => {
+        this.$notify({
+          type: "success",
+          message: "启用成功!",
+        });
+        this.searchForm();
+      });
+    },
+    disenable(row) {
+      var url =
+        QUESTION_API +
+        "/exportTemplate/disenable/" +
+        this.formSearch.rootOrgId +
+        "/" +
+        row.id;
+      this.$httpWithMsg.put(url).then(() => {
+        this.$notify({
+          type: "success",
+          message: "禁用成功!",
+        });
+        this.searchForm();
+      });
+    },
+    deleteFile(row) {
+      this.$confirm("确定删除?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        var url =
+          QUESTION_API +
+          "/exportTemplate/" +
+          this.formSearch.rootOrgId +
+          "/" +
+          row.id;
+        this.$httpWithMsg.delete(url).then(() => {
+          this.$notify({
+            type: "success",
+            message: "删除成功!",
+          });
+          this.searchForm();
+        });
+      });
+    },
+    handleUploadFile(file) {
+      this.fileForm.dataFile = file;
+      this.fileForm.fileName = file.name;
+      return false;
+    },
+    addFile() {
+      if (this.formSearch.rootOrgId == null) {
+        this.$notify({
+          type: "warning",
+          message: "请选择学校",
+        });
+        return;
+      }
+      this.fileModel = true;
+    },
+    operConfig() {
+      if (this.formSearch.rootOrgId == null) {
+        this.$notify({
+          type: "warning",
+          message: "请选择学校",
+        });
+        return;
+      }
+      this.loading = true;
+      var url =
+        QUESTION_API + "/exportTemplate/config/" + this.formSearch.rootOrgId;
+      this.$httpWithMsg
+        .get(url)
+        .then((response) => {
+          this.configForm.serviceName = response.data.exportServiceName;
+          if (this.isSuperAdmin) {
+            for (const org of this.rootOrgList) {
+              if (this.formSearch.rootOrgId == org.id) {
+                this.configForm.orgName = org.name;
+                break;
+              }
+            }
+          }
+          this.configModel = true;
+        })
+        .finally(() => (this.loading = false));
+    },
+    async subFile() {
+      const res = await this.$refs.fileForm.validate();
+
+      if (res === false) {
+        return;
+      }
+      this.fileForm.loading = true;
+      var params =
+        "?templateName=" +
+        this.fileForm.templateName +
+        "&type=" +
+        this.fileForm.type;
+      var url =
+        QUESTION_API +
+        "/exportTemplate/add/" +
+        this.formSearch.rootOrgId +
+        params;
+      let formData = new FormData();
+      formData.append("dataFile", this.fileForm.dataFile);
+      this.$httpWithMsg
+        .post(url, formData)
+        .then(() => {
+          this.$notify({
+            type: "success",
+            message: "上传成功!",
+          });
+          this.closeFileModel();
+          this.searchForm();
+        })
+        .finally(() => (this.fileForm.loading = false));
+    },
+    async subConfig() {
+      const res = await this.$refs.configForm.validate();
+
+      if (res === false) {
+        return;
+      }
+      this.configForm.loading = true;
+      var params = "?serviceName=" + this.configForm.serviceName;
+      var url =
+        QUESTION_API +
+        "/exportTemplate/config/" +
+        this.formSearch.rootOrgId +
+        params;
+      this.$httpWithMsg
+        .put(url)
+        .then(() => {
+          this.$notify({
+            type: "success",
+            message: "配置成功!",
+          });
+          this.closeConfigModel();
+        })
+        .finally(() => (this.configForm.loading = false));
+    },
+    closeFileModel() {
+      this.fileModel = false;
+      this.$refs.fileForm.resetFields();
+      this.fileModelKey = Math.random();
+      this.fileForm.fileName = null;
+      this.fileForm.type = null;
+      this.fileForm.dataFile = null;
+      this.fileForm.templateName = null;
+    },
+    closeConfigModel() {
+      this.configModel = false;
+      this.$refs.configForm.resetFields();
+      this.configModelKey = Math.random();
+      this.configForm.orgName = null;
+      this.configForm.serviceName = null;
+    },
+    handleSearchBtn() {
+      this.currentPage = 1;
+      this.searchForm();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchForm();
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchForm();
+    },
+    //查询
+    searchForm() {
+      if (this.formSearch.rootOrgId == null) {
+        this.$notify({
+          type: "warning",
+          message: "请选择学校",
+        });
+        return;
+      }
+      this.loading = true;
+      var url =
+        QUESTION_API +
+        "/exportTemplate/page/" +
+        this.currentPage +
+        "/" +
+        this.pageSize;
+      this.$httpWithMsg
+        .get(url, { params: this.formSearch })
+        .then((response) => {
+          this.tableData = response.data.content;
+          this.total = response.data.totalElements;
+          this.loading = false;
+
+          this.$nextTick(function () {
+            this.paginationShow = true;
+          });
+        })
+        .finally(() => (this.loading = false));
+    },
+    init() {
+      if (this.isSuperAdmin) {
+        this.$httpWithMsg
+          .get(CORE_API + "/org/getRootOrgList")
+          .then((response) => {
+            this.rootOrgList = response.data;
+          });
+      } else {
+        this.formSearch.rootOrgId = this.user.rootOrgId;
+        this.searchForm();
+      }
+    },
+  },
+};
+</script>
+
+<style scoped src="../styles/Common.css"></style>
+<style scoped>
+.page {
+  margin-top: 10px;
+}
+.pull-length {
+  width: 300px;
+}
+.details-length {
+  width: 400px;
+}
+.pull-center {
+  margin-top: 20px;
+}
+.editForm .el-form-item {
+  margin-bottom: 12px;
+}
+.form-row {
+  margin-top: 20px;
+}
+</style>

+ 903 - 0
src/modules/questions/views/ExtractPaperInfo.vue

@@ -0,0 +1,903 @@
+<template>
+  <section
+    v-loading="loading"
+    class="content"
+    element-loading-text="保存调卷规则及生成试卷文件中..."
+  >
+    <LinkTitlesCustom :current-paths="['考试管理', '调卷规则', '调卷详情']" />
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        ref="extractPaperForm"
+        :inline="true"
+        :model="extractConfig"
+        label-position="right"
+        label-width="120px"
+      >
+        <el-row>
+          <el-form-item label="考试名称">
+            <el-select
+              v-model="extractConfig.examId"
+              filterable
+              clearable
+              placeholder="请选择"
+              :remote-method="getExams"
+              remote
+              :disabled="isInsert"
+              @change="getExamCourses"
+            >
+              <el-option
+                v-for="item in examList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="课程名称">
+            <el-select
+              v-model="extractConfig.courseCode"
+              filterable
+              clearable
+              placeholder="请选择"
+              :disabled="isInsert"
+              @change="getPaperTypeList"
+            >
+              <el-option
+                v-for="item in courseInfoSelect"
+                :key="item.courseNo"
+                :label="item.courseInfo"
+                :value="item.courseNo"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+
+        <el-tabs v-model="tabs">
+          <el-tab-pane label="步骤一:调卷模式选择" name="first">
+            <el-form-item prop="status">
+              <el-radio-group
+                v-model="extractConfig.callType"
+                class="pull_right_ss"
+              >
+                <el-radio label="WHOLE_SET">成套调用</el-radio>
+                <!-- <el-radio label="RESTRUCT">重组调用</el-radio> -->
+              </el-radio-group>
+            </el-form-item>
+          </el-tab-pane>
+        </el-tabs>
+        <el-tabs v-model="tabs">
+          <el-tab-pane label="步骤二:试卷抽取比例" name="first">
+            <el-row>
+              <el-col :span="24">
+                <el-tag v-if="extractConfig.examType === 'TRADITION'"
+                  >传统考试</el-tag
+                >
+                <el-tag v-if="extractConfig.examType === 'ONLINE'"
+                  >在线考试</el-tag
+                >
+                <el-tag v-if="extractConfig.examType === 'PRACTICE'"
+                  >练习考试</el-tag
+                >
+              </el-col>
+            </el-row>
+            <el-row v-for="(examPaper, num) in examPaperList" :key="num">
+              <el-col :span="24">
+                <el-form-item label="试卷类型" class="pull-left">
+                  <el-input
+                    v-model="examPaper.groupCode"
+                    class="input_width_lg"
+                    placeholder="请输入试卷类型"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+                <el-button
+                  v-if="extractConfig.examType != 'TRADITION'"
+                  type="primary"
+                  @click="addOnLinePaper(examPaper.groupCode)"
+                  ><i class="el-icon-plus"></i> 新增试卷
+                </el-button>
+              </el-col>
+              <el-col
+                v-for="(paperInfo, index) in examPaper.paperInfoList"
+                :key="paperInfo.selectedPaperId"
+                :span="20"
+                :offset="2"
+              >
+                <el-form-item>
+                  <span>试卷选定</span>
+                  <el-select
+                    v-model="paperInfo.paper.id"
+                    filterable
+                    placeholder="请选择"
+                    class="select_width_lg"
+                    @change="searchAudioTimeConfig"
+                  >
+                    <el-option
+                      v-for="item in paperList"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id"
+                    >
+                    </el-option>
+                  </el-select>
+                  <span>选取几率</span>
+                  <span>
+                    <el-input
+                      v-model="paperInfo.weight"
+                      class="input_width_sms"
+                      :disabled="extractConfig.examType === 'TRADITION'"
+                    ></el-input
+                    >%
+                    <i
+                      v-if="index > 0"
+                      class="el-icon-delete"
+                      @click.prevent="
+                        removePaperInfo(examPaper.groupCode, paperInfo)
+                      "
+                    ></i> </span
+                  >&nbsp; <span :class="'errorMsg_' + num + '_' + index"></span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-tab-pane>
+        </el-tabs>
+        <div>
+          <el-tabs v-model="tabs">
+            <el-tab-pane label="步骤三:随机设定" name="first">
+              <el-form-item
+                label="客观题小题乱序"
+                prop="scrambling_the_question_order"
+              >
+                <el-radio-group
+                  v-model="extractConfig.scrambling_the_question_order"
+                  class="pull_right_sm"
+                >
+                  <el-radio :label="1">开启</el-radio>
+                  <el-radio :label="0">关闭</el-radio>
+                </el-radio-group>
+              </el-form-item>
+              <br />
+              <el-form-item
+                label="客观题选项乱序"
+                prop="scrambling_the_option_order"
+              >
+                <el-radio-group
+                  v-model="extractConfig.scrambling_the_option_order"
+                  class="pull_right_sm"
+                >
+                  <el-radio :label="1">开启</el-radio>
+                  <el-radio :label="0">关闭</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+        <div v-show="extractConfig.examType != 'PRACTICE'" style="padding: 5px">
+          <el-tabs v-model="tabs">
+            <el-tab-pane label="步骤四:音频播放次数设置" name="first">
+              <el-row
+                v-show="audioTimeConfigList.length > 0"
+                class="row_header_word"
+              >
+                <el-col :span="4" :offset="1">试卷类型</el-col>
+                <el-col :span="4">试卷名称</el-col>
+                <el-col :span="3">大题号</el-col>
+                <el-col :span="3">小题号</el-col>
+                <el-col :span="4">播放次数</el-col>
+              </el-row>
+              <el-row
+                v-for="(audioTimeConfigInner, index) in audioTimeConfigList"
+                :key="index"
+                class="margin-top-10"
+              >
+                <el-col :span="4" :offset="1">
+                  {{ audioTimeConfigInner.groupCode }}
+                </el-col>
+                <el-col :span="4">
+                  {{ audioTimeConfigInner.paper.name }}
+                </el-col>
+                <el-col :span="3">
+                  {{ audioTimeConfigInner.paperDetailUnit.paperDetail.number }}
+                </el-col>
+                <el-col :span="3">
+                  {{ audioTimeConfigInner.paperDetailUnit.number }}
+                </el-col>
+                <el-col :span="4">
+                  <el-input
+                    v-model="audioTimeConfigInner.playTime"
+                    class="input_width_ss"
+                    size="small"
+                    >次
+                  </el-input>
+                </el-col>
+              </el-row>
+              <el-row v-show="audioTimeConfigList.length == 0">
+                <el-col :span="18" :offset="1"> 无音频试题 </el-col>
+              </el-row>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+        <div>
+          <el-tabs v-model="tabs">
+            <el-row>
+              <el-col>
+                <el-form-item label="是否生成文件">
+                  <el-radio-group v-model="isbuildFile">
+                    <el-radio label="1">是</el-radio>
+                    <el-radio label="0">否</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row v-if="isbuildFile == '1'">
+              <el-form-item label="小题序号">
+                <el-radio-group v-model="seqMode" class="input">
+                  <el-radio label="MODE1">单题型连续</el-radio>
+                  <el-radio label="MODE2">客观题整体连续</el-radio>
+                  <el-radio label="MODE3">按大题独立</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12" :offset="1">
+                <el-button type="primary" @click="submitForm"> 保存 </el-button>
+                <el-button type="primary" @click="back"
+                  ><i class="el-icon-arrow-left"></i> 返 回</el-button
+                >
+              </el-col>
+            </el-row>
+          </el-tabs>
+        </div>
+      </el-form>
+    </div>
+  </section>
+</template>
+<script>
+import { EXAM_WORK_API, QUESTION_API } from "@/constants/constants";
+import { mapState } from "vuex";
+import _ from "lodash";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      loading: false,
+      seqMode: "MODE1",
+      extractConfig: {
+        examId: "", //考试ID
+        examName: "", //考试名称
+        examType: "", //考试类型:网络考试、传统考试
+        courseCode: "", //课程代码
+        courseName: "", //课程名称
+        callType: "WHOLE_SET", //调卷类型:默认成套调用
+        examPaperList: [],
+        scrambling_the_question_order: 0, //小题乱序  默认关闭
+        scrambling_the_option_order: 0, //选项乱序  默认关闭
+      },
+      examPaperList: [],
+      audioTimeConfig: {
+        examId: "",
+        examName: "",
+        paper: {
+          id: "",
+        },
+        groupCode: "",
+        courseCode: "",
+        playTime: "",
+      },
+      audioTimeConfigModelList: [],
+      tabs: "first",
+      paperList: [],
+      isbuildFile: "0",
+      audioTimeConfigList: [],
+      paperName: "",
+      examList: [],
+      courseList: [],
+      courseAll: [],
+      isInsert: false,
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.courseName + "(" + course.courseCode + ")";
+        courseList.push({
+          courseNo: course.courseCode,
+          courseName: course.courseName,
+          courseInfo: courseInfo,
+        });
+      }
+      return courseList;
+    },
+  },
+  //初始化
+  created: function () {
+    let extractConfigId = this.$route.params.extractConfigId;
+    //1.查询考试集合
+    this.getExams("");
+    if (extractConfigId) {
+      this.isInsert = true;
+      //规则ID存在,表示是修改操作
+      this.getExtractConfig(extractConfigId);
+    }
+  },
+  methods: {
+    //根据考务查询考试集合
+    getExams(query) {
+      query = query.trim();
+      this.$http
+        .get(
+          EXAM_WORK_API + "/exam/queryByNameLike?name=" + query + "&enable=true"
+        )
+        .then((response) => {
+          this.examList = response.data;
+        });
+    },
+    //根据考试查询课程
+    getExamCourses() {
+      if (!this.isInsert) {
+        this.extractConfig.courseCode = "";
+        this.courseList = [];
+        if (this.extractConfig.examId) {
+          //查询该考试下的课程
+          this.$http
+            .get(
+              QUESTION_API +
+                "/findCourseByExtractConfig/" +
+                this.extractConfig.examId
+            )
+            .then((response) => {
+              this.courseList = response.data;
+            });
+        }
+        //查询考试对象,获取考试类型:离线,在线,传统
+        if (this.extractConfig.examId) {
+          this.$http
+            .get(EXAM_WORK_API + "/exam/" + this.extractConfig.examId)
+            .then((response) => {
+              var exam = response.data;
+              this.extractConfig.examType = exam.examType;
+              this.extractConfig.examName = exam.name;
+            });
+        }
+      }
+    },
+    //根据考试ID和课程code取出试卷类型集合
+    getPaperTypeList() {
+      if (!this.isInsert) {
+        var id = "";
+        for (let course of this.courseList) {
+          if (this.extractConfig.courseCode == course.courseCode) {
+            id = course.courseId;
+            break;
+          }
+        }
+        this.$http
+          .get(
+            EXAM_WORK_API +
+              "/exam/queryExamCoursePaperTypeList?examId=" +
+              this.extractConfig.examId +
+              "&courseId=" +
+              id
+          )
+          .then((response) => {
+            let examPaperArr = response.data;
+            console.log("examPaperArr:", examPaperArr);
+            if (examPaperArr && examPaperArr.length > 0) {
+              let _examPaperList = [];
+              for (let examPaper of examPaperArr) {
+                let groupCode = examPaper.paperType;
+                let weight =
+                  this.extractConfig.examType == "TRADITION" ? 100 : "";
+                let examPaperListByGroupCode = this.getWeightFromExamPaperList(
+                  groupCode
+                );
+                if (examPaperListByGroupCode) {
+                  _examPaperList.push({
+                    groupCode: groupCode,
+                    paperInfoList: examPaperListByGroupCode,
+                  });
+                } else {
+                  _examPaperList.push({
+                    groupCode: groupCode,
+                    paperInfoList: [
+                      {
+                        weight: weight,
+                        paper: { id: "" },
+                      },
+                    ],
+                  });
+                }
+              }
+              this.examPaperList = _examPaperList;
+            }
+          });
+        this.getPaperList();
+      }
+    },
+    //根据groupCode得到examPaper集合
+    getWeightFromExamPaperList(groupCode) {
+      let examPaperListByGroupCode = [];
+      let examPaperList = this.extractConfig.examPaperList;
+      if (!examPaperList || examPaperList.length == 0) {
+        return null;
+      }
+      for (let examPaper of examPaperList) {
+        if (examPaper.groupCode == groupCode) {
+          examPaperListByGroupCode.push(examPaper);
+        }
+      }
+      return examPaperListByGroupCode;
+    },
+    //根据课程,查询试卷集合
+    getPaperList() {
+      if (this.extractConfig.courseCode) {
+        this.$http
+          .get(
+            QUESTION_API +
+              "/getGenPaper?courseNo=" +
+              this.extractConfig.courseCode
+          )
+          .then((response) => {
+            this.paperList = response.data;
+          });
+      }
+    },
+    //根据选择试卷集合查询音频题
+    searchAudioTimeConfig() {
+      this.audioTimeConfigModelList = [];
+      //取到数组里面每个对象
+      for (var i = 0; i < this.examPaperList.length; i++) {
+        //定一个试卷的集合,用来去重
+        var paperIds = [];
+        for (var j = 0; j < this.examPaperList[i].paperInfoList.length; j++) {
+          paperIds.push(this.examPaperList[i].paperInfoList[j].paper.id);
+        }
+        //对试卷集合进行去重
+        console.log("paperIds:", paperIds);
+        var ids = [];
+        ids = _.uniq(paperIds);
+        for (var k = 0; k < ids.length; k++) {
+          //把对象添加到集合
+          this.audioTimeConfigModelList.push({
+            courseCode: this.extractConfig.courseCode,
+            groupCode: this.examPaperList[i].groupCode,
+            paper: {
+              id: ids[k],
+            },
+            examId: this.extractConfig.examId,
+            examName: this.extractConfig.examName,
+          });
+        }
+      }
+      this.$http
+        .post(
+          QUESTION_API + "/audioTimeConfig/all",
+          this.audioTimeConfigModelList
+        )
+        .then((response) => {
+          this.audioTimeConfigList = response.data;
+        });
+    },
+    //增加在线考试试卷
+    addOnLinePaper(groupCode) {
+      let num = 0;
+      for (let i = 0; i < this.examPaperList.length; i++) {
+        if (this.examPaperList[i].groupCode == groupCode) {
+          num = i;
+          break;
+        }
+      }
+      this.examPaperList[num].paperInfoList.push({
+        weight: 0,
+        paper: {
+          id: "",
+        },
+      });
+    },
+    //删除试卷
+    removePaperInfo(groupCode, item) {
+      let num = 0;
+      for (let i = 0; i < this.examPaperList.length; i++) {
+        if (this.examPaperList[i].groupCode == groupCode) {
+          num = i;
+          break;
+        }
+      }
+      let index = this.examPaperList[num].paperInfoList.indexOf(item);
+      if (index !== -1) {
+        this.examPaperList[num].paperInfoList.splice(index, 1);
+      }
+      if (this.extractConfig.examType != "PRACTICE") {
+        //重新查询一次所有试卷的音频题数
+        this.searchAudioTimeConfig();
+      }
+    },
+    //保存提交
+    submitForm() {
+      this.saveExtractConfig();
+    },
+    //保存调卷规则
+    saveExtractConfig() {
+      if (!this.extractConfig.examId) {
+        this.$notify({
+          message: "请选择考试",
+          type: "error",
+        });
+        return false;
+      }
+      if (!this.extractConfig.courseCode) {
+        if (this.isInsert) {
+          this.$notify({
+            message:
+              "考务数据中,该场考试下已经没有这门课程,无法保存调卷规则。",
+            type: "error",
+          });
+        } else {
+          this.$notify({
+            message: "请选择课程",
+            type: "error",
+          });
+        }
+        return false;
+      }
+      if (this.extractConfig.examType != "PRACTICE") {
+        for (let audioTimeConfig of this.audioTimeConfigList) {
+          if (!this.checkValue(audioTimeConfig.playTime)) {
+            this.$notify({
+              message: "播放次数只能为整数",
+              type: "error",
+            });
+            return false;
+          }
+        }
+      }
+      let myExamPaperList = this.verificationData();
+      if (myExamPaperList.length > 0) {
+        this.loading = true;
+        this.extractConfig.examPaperList = myExamPaperList;
+        this.$http
+          .put(
+            QUESTION_API +
+              "/extractConfig" +
+              "/" +
+              this.isbuildFile +
+              "?seqMode=" +
+              this.seqMode,
+            this.extractConfig
+          )
+          .then(
+            () => {
+              if (this.extractConfig.examType != "PRACTICE") {
+                this.addAudioTimeConfig();
+              }
+              this.loading = false;
+              this.$notify({ type: "success", message: "保存成功" });
+              this.back();
+            },
+            (error) => {
+              this.loading = false;
+              this.$notify({
+                type: "error",
+                message: error.response.data.desc,
+              });
+            }
+          );
+      }
+    },
+    //音频播放次数校验
+    checkValue(val) {
+      if (/(^[0-9]\d*$)/.test(val)) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    //新增音频播放次数
+    addAudioTimeConfig() {
+      if (!this.audioTimeConfigList || this.audioTimeConfigList.length == 0) {
+        /* 未设置时则清理已存在的数据 */
+        this.$http
+          .post(
+            QUESTION_API +
+              "/deleteAudioTimeConfig/" +
+              this.extractConfig.examId +
+              "/" +
+              this.extractConfig.courseCode
+          )
+          .then(() => {});
+      } else {
+        this.$http
+          .post(
+            QUESTION_API +
+              "/addAudioTimeConfig?examId=" +
+              this.extractConfig.examId +
+              "&courseCode=" +
+              this.extractConfig.courseCode,
+            this.audioTimeConfigList
+          )
+          .then(() => {});
+      }
+    },
+    //校验调卷规则数据
+    verificationData() {
+      let myExamPaperList = [];
+      let examPaperList = this.examPaperList;
+      for (let i = 0; i < examPaperList.length; i++) {
+        let selectedPaperIds = []; //校验试卷重复性
+        let weightCount = 0; //校验概率之和
+        let _paperInfoList = examPaperList[i].paperInfoList;
+        let groupCode = examPaperList[i].groupCode;
+        if (!groupCode) {
+          this.$notify({
+            message: "试卷类型不能为空",
+            type: "error",
+            duration: 2000,
+          });
+          break;
+        }
+        for (let j = 0; j < _paperInfoList.length; j++) {
+          if (!_paperInfoList[j].paper.id) {
+            myExamPaperList = [];
+            // $(".errorMsg_" + i + "_" + j)
+            //   .text("试卷不能为空")
+            //   .show();
+            document.getElementsByClassName(
+              "errorMsg_" + i + "_" + j
+            )[0].innerHTML = "试卷不能为空";
+            document.getElementsByClassName(
+              "errorMsg_" + i + "_" + j
+            )[0].style.display = "inline";
+            break;
+          } else {
+            //$(".errorMsg_" + i + "_" + j).hide();
+            document.getElementsByClassName(
+              "errorMsg_" + i + "_" + j
+            )[0].style.display = "none";
+          }
+          var reg = /^([1]?\d{1,2})$/;
+          if (
+            !reg.test(_paperInfoList[j].weight) ||
+            _paperInfoList[j].weight == 0
+          ) {
+            myExamPaperList = [];
+            // $(".errorMsg_" + i + "_" + j)
+            //   .text("请输入1-100的整数")
+            //   .show();
+            document.getElementsByClassName(
+              "errorMsg_" + i + "_" + j
+            )[0].innerHTML = "请输入1-100的整数";
+            document.getElementsByClassName(
+              "errorMsg_" + i + "_" + j
+            )[0].style.display = "inline";
+            break;
+          } else {
+            //$(".errorMsg_" + i + "_" + j).hide();
+            if (document.getElementsByClassName("errorMsg_" + i + "_" + j)) {
+              document.getElementsByClassName(
+                "errorMsg_" + i + "_" + j
+              )[0].style.display = "none";
+            }
+            weightCount += parseInt(_paperInfoList[j].weight);
+            selectedPaperIds.push(_paperInfoList[j].paper.id);
+            myExamPaperList.push({
+              groupCode: groupCode,
+              paper: {
+                id: _paperInfoList[j].paper.id,
+              },
+              weight: _paperInfoList[j].weight,
+            });
+          }
+        }
+        if (myExamPaperList.length == 0) {
+          break;
+        } else {
+          if (_.uniq(selectedPaperIds).length != selectedPaperIds.length) {
+            myExamPaperList = [];
+            this.$notify({
+              message: "试卷选择不能重复",
+              type: "error",
+              duration: 2000,
+            });
+            break;
+          }
+          if (weightCount != 100) {
+            myExamPaperList = [];
+            this.$notify({
+              message: "试卷选取几率总和必须等于100",
+              type: "error",
+              duration: 2000,
+            });
+            break;
+          }
+        }
+      }
+      return myExamPaperList;
+    },
+    //返回
+    back() {
+      if (this.isInsert) {
+        this.$router.push({
+          path: "/questions/extract_paper_rule/1",
+        });
+      } else {
+        if (this.extractConfig.examId) {
+          sessionStorage.setItem(
+            "extract_config_add_examId",
+            this.extractConfig.examId
+          );
+          this.$router.push({
+            path: "/questions/extract_paper_rule/2",
+          });
+        } else {
+          this.$router.push({
+            path: "/questions/extract_paper_rule/0",
+          });
+        }
+      }
+    },
+    //修改调卷规则
+    getExtractConfig(extractConfigId) {
+      this.$http
+        .get(QUESTION_API + "/extractConfig/" + extractConfigId)
+        .then((response) => {
+          this.extractConfig = response.data;
+          //查询该考试下的课程
+          this.$http
+            .get(
+              EXAM_WORK_API +
+                "/exam/queryExamCourseList?examId=" +
+                this.extractConfig.examId +
+                "&enable=true&name=" +
+                this.extractConfig.courseCode
+            )
+            .then((response) => {
+              this.courseList = response.data;
+              if (!this.courseList || this.courseList.length < 1) {
+                this.$notify({
+                  message: "考务中,该场考试下面,没有设置课程!",
+                  type: "error",
+                });
+              } else {
+                //根据考试ID和课程code取出试卷类型集合
+                this.getPaperTypes();
+              }
+            });
+        });
+    },
+    //根据考试ID和课程code取出试卷类型集合
+    getPaperTypes() {
+      var id = "";
+      if (this.courseList && this.courseList.length > 0) {
+        for (let course of this.courseList) {
+          if (this.extractConfig.courseCode == course.courseCode) {
+            id = course.courseId;
+            break;
+          }
+        }
+        if (id != "") {
+          //查询该课程下的所有试卷
+          this.getPaperList();
+          this.$http
+            .get(
+              EXAM_WORK_API +
+                "/exam/queryExamCoursePaperTypeList?examId=" +
+                this.extractConfig.examId +
+                "&courseId=" +
+                id
+            )
+            .then((response) => {
+              let examPaperArr = response.data;
+              if (examPaperArr && examPaperArr.length > 0) {
+                let _examPaperList = [];
+                for (let examPaper of examPaperArr) {
+                  let groupCode = examPaper.paperType;
+                  let weight =
+                    this.extractConfig.examType == "TRADITION" ? 100 : "";
+                  let examPaperListByGroupCode = this.getWeightFromExamPaperList(
+                    groupCode
+                  );
+                  if (examPaperListByGroupCode) {
+                    _examPaperList.push({
+                      groupCode: groupCode,
+                      paperInfoList: examPaperListByGroupCode,
+                    });
+                  } else {
+                    _examPaperList.push({
+                      groupCode: groupCode,
+                      paperInfoList: [
+                        {
+                          weight: weight,
+                          paper: { id: "" },
+                        },
+                      ],
+                    });
+                  }
+                }
+                this.examPaperList = _examPaperList;
+                if (this.extractConfig.examType != "PRACTICE") {
+                  this.searchAddAudioTimeConfig();
+                }
+              }
+            });
+          this.getPaperList();
+        } else {
+          this.$notify({
+            message: "考务中,该场考试下面,没有设置该门课程!",
+            type: "error",
+          });
+        }
+      }
+    },
+    //存在调卷规则id,先查询
+    searchAddAudioTimeConfig() {
+      this.audioTimeConfigModelList = [];
+      //取到数组里面每个对象
+      for (var i = 0; i < this.examPaperList.length; i++) {
+        //定一个试卷的集合,用来去重
+        var paperIds = [];
+        for (var j = 0; j < this.examPaperList[i].paperInfoList.length; j++) {
+          paperIds.push(this.examPaperList[i].paperInfoList[j].paper.id);
+        }
+        //对试卷集合进行去重
+        var ids = [];
+        ids = _.uniq(paperIds);
+        for (var k = 0; k < ids.length; k++) {
+          //把对象添加到集合
+          this.audioTimeConfigModelList.push({
+            courseCode: this.extractConfig.courseCode,
+            groupCode: this.examPaperList[i].groupCode,
+            paper: {
+              id: ids[k],
+            },
+            examId: this.extractConfig.examId,
+            examName: this.extractConfig.examName,
+          });
+        }
+      }
+      this.$http
+        .post(
+          QUESTION_API + "/audioTimeConfig/update",
+          this.audioTimeConfigModelList
+        )
+        .then((response) => {
+          this.audioTimeConfigList = response.data;
+        });
+    },
+  },
+};
+</script>
+<style scoped>
+.pull_right_ss {
+  margin-left: 30px;
+}
+.paper_title_top {
+  margin-top: -10px;
+}
+.input_width_sms {
+  width: 80px;
+}
+.input_width_ss {
+  width: 50px;
+}
+.el-icon-delete {
+  cursor: pointer;
+}
+[class^="errorMsg_"] {
+  color: #ff4949;
+  display: none;
+}
+.margin-top-10 {
+  margin-top: 10px;
+}
+
+.select_width_lg {
+  width: 420px;
+}
+</style>
+<style scoped src="../styles/Common.css"></style>

+ 711 - 0
src/modules/questions/views/ExtractPaperRule.vue

@@ -0,0 +1,711 @@
+<template>
+  <section class="content">
+    <div v-show="isClear == 1">
+      <LinkTitlesCustom :current-paths="['考试管理', '调卷规则']" />
+    </div>
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="考试名称">
+              <el-select
+                v-model="formSearch.examId"
+                class="search_width"
+                filterable
+                :remote-method="getExams"
+                remote
+                clearable
+                placeholder="请选择"
+                size="small"
+                @change="getExamCourses"
+              >
+                <el-option
+                  v-for="item in examList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="课程名称">
+              <el-select
+                v-model="formSearch.courseNo"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                placeholder="请选择"
+                size="small"
+                @focus="(e) => getCourses(e.target.value)"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseNo"
+                  :label="item.courseInfo"
+                  :value="item.courseNo"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" type="primary" @click="markRules"
+                ><i class="el-icon-plus"></i> 新增</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+        <div
+          style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+        ></div>
+        <el-row>
+          <el-form-item>
+            <span>批量操作:</span>
+            <el-button size="small" type="primary" @click="openBatchExpDialog"
+              ><i class="el-icon-download"></i> 下载成卷
+            </el-button>
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div style="width: 100%; margin-bottom: 10px"></div>
+      <!-- 页面列表 -->
+      <el-table :data="tableData" border style="width: 100%">
+        <el-table-column label="考试名称" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.examName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="考试类型">
+          <template slot-scope="scope">
+            <div class="el_table_inner_left">
+              <span v-show="scope.row.examType == 'ONLINE'">在线考试</span>
+              <span v-show="scope.row.examType == 'TRADITION'">传统考试</span>
+              <span v-show="scope.row.examType == 'PRACTICE'">练习考试</span>
+              <span v-show="scope.row.examType == 'OFFLINE'">离线考试</span>
+              <span v-show="scope.row.examType == 'PRINT_EXAM'"
+                >分布式印刷考试</span
+              >
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程名称">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程代码" width="140">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseCode }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="调卷类型">
+          <template slot-scope="scope">
+            <div class="el_table_inner_left">
+              <span v-show="scope.row.callType == 'WHOLE_SET'">成套调用</span>
+              <span v-show="scope.row.callType == 'RESTRUCT'">重组调用</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="调卷规则">
+          <template slot-scope="scope">
+            <span v-show="scope.row.id">
+              <el-tag type="success">已制定</el-tag>
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="180">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                v-if="scope.row.id"
+                size="mini"
+                type="primary"
+                plain
+                @click="updateRules(scope.row)"
+                ><i class="el-icon-edit"></i>编辑
+              </el-button>
+              <el-button
+                v-if="scope.row.ifFinish == 1"
+                size="mini"
+                type="primary"
+                plain
+                @click="openSingleExportPaperDialog(scope.row)"
+                ><i class="el-icon-download"></i> 下载
+              </el-button>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <el-dialog title="导出" :visible.sync="exportDialog">
+      <el-form :model="exportModel" label-position="right" label-width="80px">
+        <el-row>
+          <el-col :md="24">
+            <el-form-item label="考试名称">
+              <el-select
+                v-model="exportModel.examId"
+                filterable
+                clearable
+                :disabled="
+                  exportDialog.exportWay == 'SINGLE' &&
+                  exportModel.courseCode != ''
+                "
+                @change="checkPaperStructure"
+              >
+                <el-option
+                  v-for="item in examList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row v-show="exportModel.courseId">
+          <el-col :md="24">
+            <el-form-item label="课程名称">
+              <el-input v-model="exportModel.courseName" disabled></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :md="24">
+            <el-form-item
+              v-if="exportModel.examType != 'PRINT_EXAM'"
+              label="导出内容"
+            >
+              <el-checkbox-group
+                v-if="exportModel.exportType == 'NORMAL'"
+                v-model="exportModel.exportContentList"
+              >
+                <el-checkbox label="PAPER" :disabled="paperDisabled"
+                  >试卷</el-checkbox
+                >
+                <el-checkbox label="ANSWER" :disabled="answerDisabled"
+                  >答案</el-checkbox
+                >
+                <el-checkbox
+                  label="PAPER_STRUCTURE_OBJECTIVE,PAPER_STRUCTURE_SUBJECTIVE"
+                  :disabled="structureDisabled"
+                  >试卷结构
+                </el-checkbox>
+              </el-checkbox-group>
+              <el-checkbox-group
+                v-if="exportModel.exportType == 'ONLINE'"
+                v-model="exportModel.exportContentList"
+              >
+                <el-checkbox
+                  label="COMPUTERTEST_PACKAGE"
+                  :disabled="onlineDisabled"
+                  >机考数据包
+                </el-checkbox>
+              </el-checkbox-group>
+            </el-form-item>
+            <el-form-item
+              v-if="exportModel.examType == 'PRINT_EXAM'"
+              label="导出内容"
+            >
+              <el-checkbox-group v-model="exportModel.exportContentList">
+                <el-checkbox label="PAPER" :disabled="paperDisabled"
+                  >试卷</el-checkbox
+                >
+                <el-checkbox label="ANSWER" :disabled="answerDisabled"
+                  >答案</el-checkbox
+                >
+                <el-checkbox
+                  v-if="isShowPrintExamPackage"
+                  label="PRINT_EXAM_PACKAGE"
+                  >分布式印刷数据包
+                </el-checkbox>
+                <el-input
+                  v-if="isShowPrintExamPackagePassword"
+                  v-model="printExamPackagePassword"
+                  placeholder="加密密码 (可选)"
+                  type="password"
+                  style="width: 150px"
+                ></el-input>
+              </el-checkbox-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :md="24" class="text-center">
+            <el-button type="primary" @click="exportPaperInfo"
+              >开始导出</el-button
+            >
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </section>
+</template>
+
+<script>
+import { CORE_API, EXAM_WORK_API, QUESTION_API } from "@/constants/constants";
+import { LEVEL_TYPE } from "../constants/constants";
+import { mapState } from "vuex";
+import _ from "lodash";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      isClear: 0,
+      formSearch: {
+        examId: "",
+        courseNo: "",
+        courseName: "",
+      },
+      tableData: [],
+      currentPage: 1,
+      levelList: LEVEL_TYPE,
+      pageSize: 10,
+      total: 0,
+      examList: [], //考试list
+      courseList: [], //课程list
+      loading: false,
+      paperModels: "",
+      exportDialog: false,
+      exportModel: {
+        examType: "",
+        examId: "",
+        courseId: "",
+        courseName: "",
+        exportWay: "",
+        exportType: "NORMAL",
+        exportContentList: [],
+      },
+      paperDisabled: true,
+      answerDisabled: true,
+      structureDisabled: true,
+      onlineDisabled: true,
+      courseAllList: [],
+      isShowPrintExamPackage: false,
+      isShowPrintExamPackagePassword: false,
+      printExamPackagePassword: "",
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        courseList.push({
+          courseNo: course.code,
+          courseName: course.name,
+          courseInfo: courseInfo,
+        });
+      }
+      return courseList;
+    },
+  },
+  watch: {
+    $route: "initVue",
+  },
+  created() {
+    this.initVue();
+  },
+  methods: {
+    //初始化
+    initialize(examId) {
+      this.examList = [];
+      //查询所有考试
+      this.$http.get(EXAM_WORK_API + "/exam/" + examId).then((response) => {
+        this.examList.push(response.data);
+        this.formSearch.examId = parseInt(examId);
+        this.searchRulePaper();
+      });
+    },
+    //查询列表
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchRulePaper();
+    },
+    searchRulePaper() {
+      if (!this.formSearch.examId) {
+        this.$notify({
+          message: "请选择考试",
+          type: "warning",
+        });
+        return false;
+      }
+      this.loading = true;
+      let currentPage = this.currentPage;
+      this.currentPage = 1;
+      this.$http
+        .get(
+          QUESTION_API +
+            "/findPageExtractConfig/" +
+            currentPage +
+            "/" +
+            this.pageSize +
+            "?examId=" +
+            this.formSearch.examId +
+            "&courseNo=" +
+            this.formSearch.courseNo
+        )
+        .then((response) => {
+          this.tableData = response.data.content;
+          this.total = response.data.totalElements;
+          this.currentPage = Number(currentPage);
+          this.loading = false;
+        })
+        .catch((error) => {
+          this.loading = false;
+          this.$notify({
+            message: error.response.data.desc,
+            type: "error",
+          });
+          this.tableData = [];
+          this.total = 0;
+        });
+    },
+    //分页
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchRulePaper();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchRulePaper();
+    },
+    //制定调卷规则
+    markRules() {
+      sessionStorage.setItem("extract_paper_examId", this.formSearch.examId);
+      this.$router.push({
+        name: "extract_paper_info_add",
+      });
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.formSearch.courseName = course.name;
+        }
+      }
+    },
+    //编辑调卷规则
+    updateRules(row) {
+      this.getCourseName(this.formSearch.courseNo);
+      sessionStorage.setItem("extract_paper_examId", row.examId);
+      sessionStorage.setItem(
+        "extract_paper_courseNo",
+        this.formSearch.courseNo
+      );
+      sessionStorage.setItem(
+        "extract_paper_courseName",
+        this.formSearch.courseName
+      );
+      sessionStorage.setItem("extract_paper_currentPage", this.currentPage);
+      this.$router.push({
+        name: "extract_paper_info_edit",
+        params: { extractConfigId: row.id },
+      });
+    },
+    openSingleExportPaperDialog(row) {
+      this.exportModel = {
+        examType: row.examType,
+        examId: row.examId,
+        examName: row.examName,
+        courseId: row.courseCode,
+        courseName: row.courseName,
+        exportType: "NORMAL",
+        exportWay: "SINGLE",
+        exportContentList: [],
+      };
+      this.checkPaperStructure();
+      this.exportDialog = true;
+      /* 分布式印刷数据包设置 */
+      //this.exportModel.examType = 'PRINT_EXAM';//todo
+      this.isShowPrintExamPackage = true;
+      this.isShowPrintExamPackagePassword = false;
+      this.printExamPackagePassword = "";
+    },
+    openBatchExpDialog() {
+      this.exportModel = {
+        examType: "",
+        examId: "",
+        examName: "",
+        courseId: "",
+        courseName: "",
+        exportType: "NORMAL",
+        exportWay: "BATCH",
+        exportContentList: [],
+      };
+      this.resetDisabled();
+      this.exportDialog = true;
+      /* 分布式印刷数据包设置 */
+      this.isShowPrintExamPackage = false;
+      this.isShowPrintExamPackagePassword = false;
+      this.printExamPackagePassword = "";
+    },
+    checkPaperStructure() {
+      if (!this.exportModel.examId) {
+        return false;
+      }
+      this.exportModel.exportContentList = [];
+      let examFile = {
+        examId: this.exportModel.examId,
+        courseId: this.exportModel.courseId ? this.exportModel.courseId : null,
+      };
+      this.resetDisabled();
+      this.$http
+        .post(QUESTION_API + "/findExamFile", examFile)
+        .then((response) => {
+          let examFiles = response.data;
+          if (examFiles != null && examFiles.length > 0) {
+            this.paperDisabled = this.isDisabled(examFiles, "PAPER");
+            this.answerDisabled = this.isDisabled(examFiles, "ANSWER");
+            this.onlineDisabled = this.isDisabled(
+              examFiles,
+              "COMPUTERTEST_PACKAGE"
+            );
+
+            this.$http
+              .get(
+                QUESTION_API + "/findExportStructure/" + this.exportModel.examId
+              )
+              .then((response) => {
+                this.exportModel.exportType = response.data
+                  ? response.data.exportType
+                  : "NORMAL";
+                this.structureDisabled = !response.data;
+                if (this.exportModel.exportWay == "SINGLE") {
+                  this.structureDisabled = this.isDisabled(
+                    examFiles,
+                    "PAPER_STRUCTURE_OBJECTIVE"
+                  );
+                }
+              });
+          } else {
+            this.resetDisabled();
+          }
+        });
+    },
+    isDisabled(examFiles, fileType) {
+      return (
+        _.filter(examFiles, function (examFile) {
+          return examFile.examFileType == fileType;
+        }).length == 0
+      );
+    },
+    resetDisabled() {
+      this.paperDisabled = true;
+      this.answerDisabled = true;
+      this.structureDisabled = true;
+      this.onlineDisabled = true;
+    },
+    exportPaperInfo() {
+      let examId = this.exportModel.examId;
+      let courseId = this.exportModel.courseId;
+      let exportWay = this.exportModel.exportWay;
+      if (!examId) {
+        this.$notify({
+          message: "请选择考试",
+          type: "error",
+        });
+        return false;
+      }
+      if (this.exportModel.exportContentList.length == 0) {
+        this.$notify({
+          message: "请选择导出内容",
+          type: "error",
+        });
+        return false;
+      }
+      if (this.printExamPackagePassword.length > 0) {
+        var reg = /^[0-9A-Za-z]{6,12}$/;
+        if (!reg.test(this.printExamPackagePassword)) {
+          this.$notify({
+            message: "加密密码请输入6至12位的数字或字母!",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      let exportContentList = this.exportModel.exportContentList.toString();
+      var key = this.user.key;
+      var token = this.user.token;
+      this.$http
+        .get(
+          QUESTION_API +
+            "/exportBatchExamPaperInfoCheck/" +
+            exportWay +
+            "/" +
+            examId +
+            "?$key=" +
+            key +
+            "&$token=" +
+            token
+        )
+        .then(
+          (response) => {
+            console.log(response);
+            if (response.data) {
+              this.$notify({
+                message: response.data.desc,
+                type: "error",
+              });
+            } else {
+              var key = this.user.key;
+              var token = this.user.token;
+              if (exportWay == "SINGLE") {
+                window.location.href =
+                  QUESTION_API +
+                  "/exportSingleExamPaperInfo/" +
+                  this.user.rootOrgId +
+                  "/" +
+                  exportWay +
+                  "/" +
+                  examId +
+                  "/" +
+                  courseId +
+                  "/" +
+                  exportContentList +
+                  "/" +
+                  this.user.displayName +
+                  "?psw=" +
+                  this.printExamPackagePassword +
+                  "&$key=" +
+                  key +
+                  "&$token=" +
+                  token;
+              } else if (exportWay == "BATCH") {
+                window.location.href =
+                  QUESTION_API +
+                  "/exportBatchExamPaperInfo/" +
+                  this.user.rootOrgId +
+                  "/" +
+                  exportWay +
+                  "/" +
+                  examId +
+                  "/" +
+                  exportContentList +
+                  "/" +
+                  this.user.displayName +
+                  "?$key=" +
+                  key +
+                  "&$token=" +
+                  token;
+              }
+            }
+          },
+          (error) => {
+            this.$notify({
+              message: error.response.data.desc,
+              type: "error",
+            });
+          }
+        );
+    },
+    removeItem() {
+      sessionStorage.removeItem("extract_paper_currentPage");
+      sessionStorage.removeItem("extract_paper_examId");
+      sessionStorage.removeItem("extract_paper_courseNo");
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          console.log("this.courseList:", this.courseList);
+          this.courseLoading = false;
+        });
+    },
+    //查询考试
+    getExams(query) {
+      query = query.trim();
+      this.$http
+        .get(
+          EXAM_WORK_API + "/exam/queryByNameLike?name=" + query + "&enable=true"
+        )
+        .then((response) => {
+          this.examList = response.data;
+        });
+    },
+    //清空课程列表
+    getExamCourses() {
+      this.formSearch.courseNo = "";
+    },
+    initVue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        this.removeItem();
+        //查询所有考试
+        this.getExams("");
+        this.formSearch = {
+          examId: "",
+          courseNo: "",
+          courseName: "",
+        };
+        this.currentPage = 1;
+      } else if (this.isClear == 2) {
+        this.formSearch.examId =
+          sessionStorage.getItem("extract_config_add_examId") == ""
+            ? ""
+            : parseInt(sessionStorage.getItem("extract_config_add_examId"));
+        if (this.formSearch.examId) {
+          this.initialize(this.formSearch.examId);
+        }
+        this.currentPage = 1;
+      } else {
+        this.formSearch.courseNo =
+          sessionStorage.getItem("extract_paper_courseNo") == "null"
+            ? ""
+            : sessionStorage.getItem("extract_paper_courseNo");
+        this.formSearch.courseName =
+          sessionStorage.getItem("extract_paper_courseName") == "null"
+            ? ""
+            : sessionStorage.getItem("extract_paper_courseName");
+        this.formSearch.examId =
+          sessionStorage.getItem("extract_paper_examId") == ""
+            ? ""
+            : parseInt(sessionStorage.getItem("extract_paper_examId"));
+        this.currentPage =
+          sessionStorage.getItem("extract_paper_currentPage") == null
+            ? 1
+            : parseInt(sessionStorage.getItem("extract_paper_currentPage"));
+        if (this.formSearch.examId) {
+          this.initialize(this.formSearch.examId);
+        }
+        if (this.formSearch.courseName) {
+          this.getCourses(this.formSearch.courseName);
+        }
+      }
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 997 - 0
src/modules/questions/views/GenPaper.vue

@@ -0,0 +1,997 @@
+<template>
+  <section class="content">
+    <div v-show="isClear == 1">
+      <LinkTitlesCustom :current-paths="['题库管理 ', '考试试卷管理']" />
+    </div>
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="课程名称">
+              <el-select
+                v-model="formSearch.courseNo"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                placeholder="全部"
+                size="small"
+                @focus="(e) => getCourses(e.target.value)"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseNo"
+                  :label="item.courseInfo"
+                  :value="item.courseNo"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="试卷名称">
+              <el-input
+                v-model="formSearch.name"
+                class="search_width"
+                placeholder="试卷名称"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="层次">
+              <el-select
+                v-model="formSearch.level"
+                class="search_width"
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in levelList"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="录入人">
+              <el-input
+                v-model="formSearch.creator"
+                class="search_width"
+                placeholder="录入人"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="修改人">
+              <el-input
+                v-model="formSearch.lastModifyName"
+                class="search_width"
+                placeholder="修改人"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" @click="resetForm"
+                ><i class="el-icon-refresh"></i> 重 置</el-button
+              >
+              <el-button size="small" type="primary" @click="genPaperDetail"
+                ><i class="el-icon-plus"></i> 组卷</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+        <div
+          style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+        ></div>
+        <el-row>
+          <el-form-item>
+            <span>批量操作:</span>
+            <el-button
+              size="small"
+              type="danger"
+              :disabled="noBatchSelected"
+              @click="batchDeleteGenPaper"
+            >
+              <i class="el-icon-delete"></i> 删除成卷
+            </el-button>
+            <el-button
+              size="small"
+              type="primary"
+              :disabled="noBatchSelected"
+              @click="openBatchExportPaperDialog"
+              ><i class="el-icon-download"></i> 下载成卷</el-button
+            >
+            <el-button
+              size="small"
+              type="primary"
+              :disabled="noBatchSelected"
+              @click="openPrints"
+              ><i class="el-icon-share"></i> 发送成卷</el-button
+            >
+            <el-button
+              size="small"
+              type="primary"
+              :disabled="noBatchSelected"
+              @click="openQuesPro"
+              ><i class="el-icon-edit"></i>试卷属性</el-button
+            >
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div style="width: 100%; margin-bottom: 10px"></div>
+      <el-table
+        v-loading="loading"
+        element-loading-text="拼命加载中"
+        :data="tableData"
+        border
+        style="width: 100%"
+        @selection-change="selectChange"
+      >
+        <el-table-column type="selection" width="40"></el-table-column>
+        <el-table-column label="课程名称" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.course.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程代码" width="80">
+          <template slot-scope="scope">
+            <span>{{ scope.row.course.code }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="试卷名称" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="试卷总分"
+          width="103"
+          sortable
+          prop="totalScore"
+        >
+        </el-table-column>
+        <el-table-column
+          label="试卷难度"
+          width="103"
+          sortable
+          prop="difficultyDegree"
+        >
+        </el-table-column>
+        <el-table-column
+          label="大题数量"
+          width="103"
+          sortable
+          prop="paperDetailCount"
+        >
+        </el-table-column>
+        <el-table-column label="录入员" width="150">
+          <template slot-scope="scope">
+            <span>{{ scope.row.creator }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="创建时间"
+          width="153"
+          sortable
+          prop="createTime"
+        >
+        </el-table-column>
+        <el-table-column label="修改人" width="150">
+          <template slot-scope="scope">
+            <span>{{ scope.row.lastModifyName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="修改时间"
+          width="153"
+          sortable
+          prop="updateDate"
+        >
+        </el-table-column>
+        <el-table-column label="操作" width="180" fixed="right">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="editGenPaper(scope.row)"
+                ><i class="el-icon-edit"></i>编辑</el-button
+              >
+              <el-dropdown class="button_left">
+                <el-button type="primary" size="mini" plain>
+                  更多 <i class="el-icon-arrow-down el-icon--right"></i>
+                </el-button>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="primary"
+                      plain
+                      @click="openExportDialog(scope.row)"
+                      ><i class="el-icon-download"></i> 下载</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="primary"
+                      plain
+                      @click="previewPDF2(scope.row)"
+                      ><i class="el-icon-zoom-in"></i> 预览</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="primary"
+                      plain
+                      @click="openPrint(scope.row)"
+                      ><i class="el-icon-share"></i> 发送</el-button
+                    >
+                  </el-dropdown-item>
+                  <!-- <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="danger"
+                      @click="deleteGenPaper(scope.row)"
+                      ><i class="el-icon-delete"></i> 删除</el-button
+                    >
+                  </el-dropdown-item> -->
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <el-dialog title="导出" :visible.sync="exportDialog">
+      <el-form :model="exportModel" label-position="right" label-width="80px">
+        <el-row v-if="isShow">
+          <el-col :md="12">
+            <el-form-item label="课程名称">
+              {{ exportModel.courseName }}
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row v-if="isShow">
+          <el-col :md="12">
+            <el-form-item label="课程代码">
+              {{ exportModel.courseCode }}
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :md="24">
+            <el-form-item label="导出内容">
+              <el-checkbox-group v-model="exportModel.exportContentList">
+                <el-checkbox label="PAPER">试卷</el-checkbox>
+                <el-checkbox label="ANSWER">答案</el-checkbox>
+                <el-checkbox label="COMPUTERTEST_PACKAGE"
+                  >机考数据包</el-checkbox
+                >
+                <el-checkbox label="THEMIS_PACKAGE">在线考试数据包</el-checkbox>
+                <!-- <el-checkbox
+                  v-if="isShowPrintExamPackage"
+                  label="PRINT_EXAM_PACKAGE"
+                  >分布式印刷数据包
+                </el-checkbox> -->
+                <el-input
+                  v-if="isShowPrintExamPackagePassword"
+                  v-model="printExamPackagePassword"
+                  placeholder="加密密码 (可选)"
+                  type="password"
+                  style="width: 150px"
+                ></el-input>
+              </el-checkbox-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row v-if="showSeqMode()">
+          <el-form-item label="小题序号">
+            <el-radio-group v-model="exportModel.seqMode" class="input">
+              <el-radio label="MODE1">单题型连续</el-radio>
+              <el-radio label="MODE2">客观题整体连续</el-radio>
+              <el-radio label="MODE3">按大题独立</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-col :md="24" class="text-center">
+            <el-button type="primary" @click="exportPaperInfo"
+              >开始导出</el-button
+            >
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-dialog>
+
+    <el-dialog title="传送印刷平台" :visible.sync="dialogModel" width="500px">
+      <el-form
+        ref="printFrom"
+        :model="printFrom"
+        :rules="rules"
+        label-position="right"
+        label-width="120px"
+        inline-message
+      >
+        <el-row>
+          <el-form-item label="学校名称">
+            <el-input
+              v-model="Org.name"
+              class="dialog_input_width"
+              :disabled="true"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="考试名称" prop="examId">
+            <el-select
+              v-model="printFrom.examId"
+              class="dialog_input_width"
+              filterable
+              clearable
+              :remote-method="getExams"
+              remote
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in examList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button type="primary" @click="sendPrint('printFrom')"
+            >保 存</el-button
+          >
+          <el-button @click="resetForm2('printFrom')"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          >
+          <el-button type="primary" @click="cancel('printFrom')"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+    <el-dialog
+      title="属性修改"
+      :visible.sync="quesPropertyDialog"
+      width="500px"
+    >
+      <el-form
+        :loading="quesLoading"
+        label-position="right"
+        :inline="true"
+        label-width="120px"
+      >
+        <el-row>
+          <el-form-item>
+            <el-checkbox v-model="difficultyDegreeDis"></el-checkbox>
+          </el-form-item>
+          <el-form-item label="难  度">
+            <el-select
+              v-model="difficultyDegree"
+              class="dialog_input_width"
+              :disabled="!difficultyDegreeDis"
+            >
+              <el-option
+                v-for="item in difficultyDegreeList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item>
+            <el-checkbox v-model="publicityDis"></el-checkbox>
+          </el-form-item>
+          <el-form-item label="公开度">
+            <el-select
+              v-model="publicity"
+              class="dialog_input_width"
+              :disabled="!publicityDis"
+            >
+              <el-option
+                v-for="item in publicityList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button
+            v-loading.fullscreen.lock="quesLoading"
+            type="primary"
+            @click="updateQuesPropertyByPaper()"
+            >保 存</el-button
+          >
+          <el-button @click="resetQuesProForm"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          >
+          <el-button type="primary" @click="closePropertyDialog"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </section>
+</template>
+
+<script>
+import { CORE_API, QUESTION_API, EXAM_WORK_API } from "@/constants/constants";
+import { LEVEL_TYPE, PUBLICITY_LIST } from "../constants/constants";
+import { mapState } from "vuex";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      publicityDis: false,
+      difficultyDegreeDis: false,
+      quesLoading: false,
+      quesPropertyDialog: false,
+      difficultyDegree: 0.1,
+      publicity: true,
+      difficultyDegreeList: [
+        { label: 0.1, value: 0.1 },
+        { label: 0.2, value: 0.2 },
+        { label: 0.3, value: 0.3 },
+        { label: 0.4, value: 0.4 },
+        { label: 0.5, value: 0.5 },
+        { label: 0.6, value: 0.6 },
+        { label: 0.7, value: 0.7 },
+        { label: 0.8, value: 0.8 },
+        { label: 0.9, value: 0.9 },
+        { label: 1.0, value: 1.0 },
+      ],
+      publicityList: PUBLICITY_LIST,
+      isClear: 0,
+      courseLoading: false,
+      formSearch: {
+        courseNo: "",
+        courseName: "",
+        creator: "",
+        lastModifyName: "",
+        level: "",
+        name: "",
+      },
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      loading: false,
+      courseList: [],
+      levelList: LEVEL_TYPE,
+      dialogVisible: false,
+      selectedPaperIds: [],
+      fileList: [],
+      uploadAction: "",
+      formUpload: {
+        paperName: "",
+      },
+      uploadData: {},
+      fileLoading: false,
+      exportDialog: false,
+      exportModel: {
+        id: "",
+        courseCode: "",
+        courseName: "",
+        exportContentList: [],
+        seqMode: "MODE1",
+      },
+      isShow: true,
+      examList: [],
+      Org: {},
+      dialogModel: false,
+      rowIds: [],
+      isShowPrintExamPackage: false,
+      isShowPrintExamPackagePassword: false,
+      printExamPackagePassword: "",
+      printFrom: {
+        examId: "",
+      },
+      rules: {
+        examId: [{ required: true, message: "请输入名称", trigger: "change" }],
+      },
+    };
+  },
+  computed: {
+    paperIds() {
+      var paperIds = "";
+      for (let paperId of this.selectedPaperIds) {
+        if (!paperIds) {
+          paperIds += paperId;
+        } else {
+          paperIds += "," + paperId;
+        }
+      }
+      return paperIds;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        var courseName = course.name;
+        courseList.push({
+          courseNo: courseNo,
+          courseInfo: courseInfo,
+          courseName: courseName,
+        });
+      }
+      return courseList;
+    },
+    noBatchSelected() {
+      return this.selectedPaperIds.length === 0;
+    },
+    ...mapState({ user: (state) => state.user }),
+  },
+  watch: {
+    $route: "initVue",
+  },
+  created() {
+    this.initVue();
+    this.searchExamList();
+    this.searchOrgName();
+  },
+  methods: {
+    showSeqMode() {
+      for (let i = 0; i < this.exportModel.exportContentList.length; i++) {
+        let element = this.exportModel.exportContentList[i];
+        if (element == "PAPER" || element == "ANSWER") {
+          // this.showSeqMode = true;
+          return true;
+        }
+      }
+      // this.showSeqMode = false;
+      return false;
+    },
+    updateQuesPropertyByPaper() {
+      if (!this.difficultyDegreeDis && !this.publicityDis) {
+        this.$notify({
+          title: "警告",
+          message: "请勾选难度或公开度",
+          type: "warning",
+        });
+        return;
+      }
+      var params = "?ids=" + this.selectedPaperIds;
+      if (this.difficultyDegreeDis) {
+        params = params + "&difficultyDegree=" + this.difficultyDegree;
+      }
+      if (this.publicityDis) {
+        params = params + "&publicity=" + this.publicity;
+      }
+      this.quesLoading = true;
+      this.$httpWithMsg
+        .put(QUESTION_API + "/question/updatePro/paperIds" + params)
+        .then(() => {
+          this.$notify({
+            message: "更新成功",
+            type: "success",
+          });
+          this.searchGenPaper();
+          this.quesLoading = false;
+          this.closePropertyDialog();
+        })
+        .catch(() => {
+          this.quesLoading = false;
+        });
+    },
+    openQuesPro() {
+      if (!this.selectedPaperIds || this.selectedPaperIds.length < 1) {
+        this.$notify({
+          title: "警告",
+          message: "请选择试卷",
+          type: "warning",
+        });
+        return;
+      }
+      this.difficultyDegree = 0.1;
+      this.publicity = true;
+      this.quesPropertyDialog = true;
+    },
+    closePropertyDialog() {
+      this.quesPropertyDialog = false;
+    },
+    resetQuesProForm() {
+      this.difficultyDegree = 0.1;
+      this.publicity = true;
+    },
+    resetForm() {
+      this.formSearch = {
+        courseNo: "",
+        courseName: "",
+        level: "",
+        name: "",
+      };
+    },
+    //查询
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchGenPaper();
+    },
+    searchGenPaper() {
+      var pageNo = this.currentPage;
+      this.currentPage = 1;
+      this.loading = true;
+      var url = QUESTION_API + "/genPaper/" + pageNo + "/" + this.pageSize;
+      this.$http.get(url, { params: this.formSearch }).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.currentPage = Number(pageNo);
+      });
+      this.loading = false;
+    },
+    genPaperDetail() {
+      var courseNo = this.formSearch.courseNo;
+      if (!courseNo) {
+        this.$notify({
+          title: "警告",
+          message: "请选择课程",
+          type: "warning",
+        });
+      } else {
+        var course = this.getCourseObj(courseNo);
+        this.formSearch.courseName = course.name;
+        sessionStorage.setItem("gen_paper", JSON.stringify(this.formSearch));
+        sessionStorage.setItem("gen_paper_currentPage", this.currentPage);
+        this.$router.push({
+          path: "/questions/gen_paper_detail/" + courseNo,
+        });
+      }
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchGenPaper();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchGenPaper();
+    },
+    getCourseObj(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          return course;
+        }
+      }
+      return "";
+    },
+    exportGenPaper(row) {
+      window.open(
+        QUESTION_API + "/paper/export/" + row.id + "/" + this.user.rootOrgName
+      );
+    },
+    editGenPaper(row) {
+      var course = this.getCourseObj(this.formSearch.courseNo);
+      if (course) {
+        this.formSearch.courseName = course.name;
+      }
+      sessionStorage.setItem("gen_paper", JSON.stringify(this.formSearch));
+      sessionStorage.setItem("gen_paper_currentPage", this.currentPage);
+      sessionStorage.setItem("question_back", "false");
+      this.$router.push({
+        path: "/edit_paper/" + row.id + "/gen_paper",
+      });
+    },
+    deleteGenPaper(row) {
+      this.$confirm("确认删除试卷吗?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http.delete(QUESTION_API + "/paper/" + row.id).then(
+          () => {
+            this.$notify({
+              message: "删除成功",
+              type: "success",
+            });
+            this.searchGenPaper();
+          },
+          (response) => {
+            this.$notify({
+              message: response.response.data.desc,
+              type: "error",
+            });
+            this.loading = false;
+          }
+        );
+      });
+    },
+    selectChange(row) {
+      this.selectedPaperIds = [];
+      row.forEach((element) => {
+        this.selectedPaperIds.push(element.id);
+      });
+    },
+    batchDeleteGenPaper() {
+      var paperIds = this.paperIds;
+      if (this.selectedPaperIds.length != 0) {
+        this.$confirm("确认删除试卷吗?", "提示", {
+          type: "warning",
+        }).then(() => {
+          this.loading = true;
+          this.$http.delete(QUESTION_API + "/paper/" + paperIds).then(
+            () => {
+              this.$notify({
+                message: "删除成功",
+                type: "success",
+              });
+              this.searchGenPaper();
+              this.selectedPaperIds = [];
+            },
+            (response) => {
+              this.$notify({
+                message: response.response.data.desc,
+                type: "error",
+              });
+              this.loading = false;
+            }
+          );
+        });
+      } else {
+        this.$notify({
+          message: "请勾选删除的数据",
+          type: "warning",
+        });
+      }
+    },
+    //查询所有课程
+    getCourses(query) {
+      if (query) {
+        query = query.trim();
+      }
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+        });
+    },
+    //打开导出弹框
+    openExportDialog(row) {
+      this.isShow = true;
+      this.isShow = true;
+      this.exportDialog = true;
+      this.exportModel.id = row.id;
+      this.exportModel.courseCode = row.course.code;
+      this.exportModel.courseName = row.course.name;
+      this.exportModel.exportContentList = [];
+      /* 分布式印刷数据包设置 */
+      this.isShowPrintExamPackage = true;
+      this.isShowPrintExamPackagePassword = false;
+      this.printExamPackagePassword = "";
+    },
+    //打开批量弹出框
+    openBatchExportPaperDialog() {
+      if (this.selectedPaperIds.length != 0) {
+        this.exportDialog = true;
+        this.isShow = false;
+        this.isShow = false;
+        this.exportModel.exportContentList = [];
+      } else {
+        this.$notify({
+          message: "请勾选导出的数据",
+          type: "warning",
+        });
+      }
+      /* 分布式印刷数据包设置 */
+      this.isShowPrintExamPackage = false;
+      this.isShowPrintExamPackagePassword = false;
+      this.printExamPackagePassword = "";
+    },
+    //导出试卷,答案,机考数据包
+    exportPaperInfo() {
+      if (this.exportModel.exportContentList.length == 0) {
+        this.$notify({
+          message: "请选择导出内容",
+          type: "error",
+        });
+        return false;
+      }
+      if (this.printExamPackagePassword.length > 0) {
+        var reg = /^[0-9A-Za-z]{6,12}$/;
+        if (!reg.test(this.printExamPackagePassword)) {
+          this.$notify({
+            message: "加密密码请输入6至12位的数字或字母!",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      var key = this.user.key;
+      var token = this.user.token;
+      if (this.isShow) {
+        window.location.href =
+          QUESTION_API +
+          "/paper/export/" +
+          this.exportModel.id +
+          "/" +
+          this.exportModel.exportContentList +
+          "/onLine" +
+          "?psw=" +
+          this.printExamPackagePassword +
+          "&$key=" +
+          key +
+          "&$token=" +
+          token +
+          "&seqMode=" +
+          this.exportModel.seqMode;
+        this.exportDialog = false;
+      } else {
+        var paperIds = this.paperIds;
+        window.location.href =
+          QUESTION_API +
+          "/paper/batch_export/" +
+          paperIds +
+          "/" +
+          this.exportModel.exportContentList +
+          "/onLine" +
+          "?psw=" +
+          this.printExamPackagePassword +
+          "&$key=" +
+          key +
+          "&$token=" +
+          token +
+          "&seqMode=" +
+          this.exportModel.seqMode;
+        this.exportDialog = false;
+      }
+    },
+    removeItem() {
+      sessionStorage.removeItem("gen_paper");
+      sessionStorage.removeItem("gen_paper_currentPage");
+    },
+    previewPDF2(row) {
+      window.open(QUESTION_API + "/paper/pdf/" + row.id);
+    },
+    openPrint(row) {
+      this.dialogModel = true;
+      this.rowIds.push(row.id);
+    },
+    openPrints() {
+      this.dialogModel = true;
+      this.rowIds = this.selectedPaperIds;
+    },
+    cancel(formData) {
+      this.resetForm2(formData);
+      this.dialogModel = false;
+    },
+    resetForm2(formData) {
+      this.printFrom.examId = "";
+      this.$refs[formData].clearValidate();
+    },
+    //传至印刷模块
+    sendPrint(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          var orgId = "";
+          for (let exam of this.examList) {
+            if (exam.id == this.printFrom.examId) {
+              orgId = exam.rootOrgId;
+            }
+          }
+          if (orgId != "" || orgId == "0") {
+            for (let printPid of this.rowIds) {
+              this.$http.get(
+                QUESTION_API +
+                  "/sendPrint/" +
+                  printPid +
+                  "/" +
+                  this.printFrom.examId +
+                  "/" +
+                  orgId
+              );
+            }
+            this.$notify({
+              message: "已推送",
+              type: "success",
+            });
+            this.resetForm2(formData);
+            this.dialogModel = false;
+            this.rowIds = [];
+          }
+        } else {
+          return false;
+        }
+      });
+    },
+    //查询考试
+    getExams(query) {
+      if (query) {
+        query = query.trim();
+      }
+      this.$http
+        .get(
+          EXAM_WORK_API + "/exam/queryByNameLike?name=" + query + "&enable=true"
+        )
+        .then((response) => {
+          this.examList = response.data;
+        });
+    },
+    searchExamList() {
+      //查询所有考试
+      this.$http
+        .get(EXAM_WORK_API + "/exam/queryByNameLike?name=&enable=true")
+        .then((response) => {
+          var list = response.data;
+          for (var i = 0; i < list.length; i++) {
+            if (list[i].examType == "TRADITION") {
+              this.examList.push(list[i]);
+            }
+          }
+        });
+    },
+    //根据orgId查询学校名称
+    searchOrgName() {
+      this.$http
+        .get(CORE_API + "/org/rootOrg/" + this.user.rootOrgId)
+        .then((response) => {
+          this.Org = response.data;
+        });
+    },
+    initVue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        this.removeItem();
+        this.formSearch = {
+          courseNo: "",
+          courseName: "",
+          level: "",
+          name: "",
+        };
+        this.currentPage = 1;
+      } else {
+        this.formSearch = JSON.parse(sessionStorage.getItem("gen_paper"));
+        this.currentPage =
+          sessionStorage.getItem("gen_paper_currentPage") == null
+            ? 1
+            : parseInt(sessionStorage.getItem("gen_paper_currentPage"));
+      }
+      this.getCourses(this.formSearch.courseName);
+      this.searchGenPaper();
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 1327 - 0
src/modules/questions/views/GenPaperDetail.vue

@@ -0,0 +1,1327 @@
+<template>
+  <div v-loading="fullscreenLoading" element-loading-text="正在组卷中...">
+    <div class="tabs">
+      <el-form
+        :inline="true"
+        :model="genPaper"
+        label-position="right"
+        label-width="100px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="课程代码" class="form-item">
+              <el-input
+                v-model="genPaper.courseNo"
+                class="search_width"
+                :disabled="true"
+                placeholder="课程代码"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="课程名称" class="form-item">
+              <el-input
+                v-model="genPaper.courseName"
+                class="search_width"
+                :disabled="true"
+                placeholder="课程名称"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="试卷名称" class="form-item">
+              <el-input
+                v-model="genPaper.paperName"
+                class="search_width"
+                placeholder="试卷名称"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="组卷套数" class="form-item">
+              <el-input
+                v-model="genPaper.genNumber"
+                class="search_width"
+                placeholder="组卷套数"
+                :disabled="genModelType == 'M-Type'"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="组卷模式" class="form-item">
+              <el-radio-group
+                v-model="genModelType"
+                @change="genModelTypeChange"
+              >
+                <el-radio label="A-Type">自动组卷</el-radio>
+                <el-radio label="M-Type">手动组卷</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </div>
+    <div v-show="genModelType == 'A-Type'">
+      <div class="tabs">
+        <el-tabs v-model="tabs">
+          <el-tab-pane label="步骤一:组卷模式选择" name="first">
+            <el-radio-group
+              v-model="genType"
+              class="pull-left"
+              @change="genTypeChange"
+            >
+              <el-radio label="SIMPLE">简易成卷</el-radio>
+              <el-radio label="NORMAL">精确成卷</el-radio>
+              <el-radio label="BLUE">蓝图成卷</el-radio>
+            </el-radio-group>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+      <div class="tabs">
+        <el-tabs v-model="tabs">
+          <el-tab-pane label="步骤二:题源选择" name="first">
+            <div v-show="genType == 'SIMPLE'" style="width: 50%">
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item label="题源选择" class="form-item">
+                  <el-select
+                    v-model="paperType"
+                    clearable
+                    placeholder="题源选择"
+                    style="width: 120px"
+                    @change="changePaperType"
+                  >
+                    <el-option label="题库来源" value="IMPORT"></el-option>
+                    <el-option label="卷库来源" value="GENERATE"></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-form>
+              <el-table
+                v-loading="loading"
+                :data="selectPapers"
+                border
+                @selection-change="selectChange"
+              >
+                <el-table-column type="selection" width="55"></el-table-column>
+                <el-table-column label="名称" width="200">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="总分" width="100">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.totalScore }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="小题数量">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.unitCount }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div class="page pull-right">
+                <el-pagination
+                  :current-page="curSelect"
+                  :page-size="pageSize"
+                  layout="total, prev, pager, next, jumper"
+                  :total="totalSelect"
+                  @current-change="selectCurrentChange"
+                >
+                </el-pagination>
+              </div>
+            </div>
+            <!-- 精确组卷题源 -->
+            <div
+              v-show="genType == 'NORMAL'"
+              style="width: 50%; overflow: auto"
+            >
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item label="题源选择" class="form-item">
+                  <el-select
+                    v-model="paperType"
+                    clearable
+                    placeholder="题源选择"
+                    style="width: 120px"
+                    @change="changePaperType"
+                  >
+                    <el-option label="题库来源" value="IMPORT"></el-option>
+                    <el-option label="卷库来源" value="GENERATE"></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-form>
+              <el-table
+                v-loading="loading"
+                :data="selectPapers"
+                border
+                @selection-change="selectChange"
+              >
+                <el-table-column type="selection" width="55"></el-table-column>
+                <el-table-column label="名称" width="200">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="总分" width="100">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.totalScore }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="小题数量">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.unitCount }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div class="page pull-right">
+                <el-pagination
+                  :current-page="curSelect"
+                  :page-size="pageSize"
+                  layout="total, prev, pager, next, jumper"
+                  :total="totalSelect"
+                  @current-change="selectCurrentChange"
+                >
+                </el-pagination>
+              </div>
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item
+                  label="选中试卷列表"
+                  class="form-item"
+                ></el-form-item>
+              </el-form>
+              <el-table :data="selectedPapers" border>
+                <el-table-column label="名称" width="200">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作">
+                  <template slot-scope="scope">
+                    <div>
+                      <el-button
+                        type="danger"
+                        size="mini"
+                        @click="removeSelected(scope.row.id)"
+                        >移除</el-button
+                      >
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+            <!-- 蓝图组卷题源 -->
+            <div v-show="genType == 'BLUE'" style="width: 50%; overflow: auto">
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item label="题源选择" class="form-item">
+                  <el-select
+                    v-model="paperType"
+                    clearable
+                    placeholder="题源选择"
+                    style="width: 120px"
+                    @change="changePaperType"
+                  >
+                    <el-option label="题库来源" value="IMPORT"></el-option>
+                    <el-option label="卷库来源" value="GENERATE"></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-form>
+              <el-table
+                v-loading="loading"
+                :data="selectPapers"
+                border
+                @selection-change="selectChange"
+              >
+                <el-table-column type="selection" width="55"></el-table-column>
+                <el-table-column label="名称" width="200">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="总分" width="100">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.totalScore }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="小题数量">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.unitCount }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div class="page pull-right">
+                <el-pagination
+                  :current-page="curSelect"
+                  :page-size="pageSize"
+                  layout="total, prev, pager, next, jumper"
+                  :total="totalSelect"
+                  @current-change="selectCurrentChange"
+                >
+                </el-pagination>
+              </div>
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item
+                  label="选中试卷列表"
+                  class="form-item"
+                ></el-form-item>
+              </el-form>
+              <el-table :data="selectedPapers" border>
+                <el-table-column label="名称" width="200">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作">
+                  <template slot-scope="scope">
+                    <div>
+                      <el-button
+                        type="danger"
+                        size="mini"
+                        @click="removeSelected(scope.row.id)"
+                        >移除</el-button
+                      >
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+
+      <div class="tabs">
+        <el-tabs v-model="tabs">
+          <el-tab-pane label="步骤三:确定构成" name="first">
+            <!-- 简易 -->
+            <div
+              v-show="genType == 'SIMPLE'"
+              style="width: 98%; overflow: auto"
+            >
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item label="抽取策略" class="form-item">
+                  <el-select
+                    v-model="genPaper.simpleGenPaperPolicy"
+                    clearable
+                    placeholder="抽取策略"
+                  >
+                    <el-option
+                      v-for="item in simpleGenPaperPolicys"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-form>
+              <el-table :data="tempPapers" border>
+                <el-table-column label="冻结" width="90">
+                  <template slot-scope="scope">
+                    <el-checkbox
+                      @change="(checked) => frozenChange(checked, scope.row.id)"
+                    ></el-checkbox>
+                  </template>
+                </el-table-column>
+                <el-table-column label="名称" width="140">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="总分" width="80">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.totalScore }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="小题数量" width="80">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.unitCount }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <!-- 新增公开度和难度 -->
+                <el-table-column label="公开(简单)" width="100">
+                  <template slot-scope="scope">
+                    <div
+                      @mouseenter="showActiveIn(1, 1, scope.row)"
+                      @mouseleave="showActiveOff"
+                    >
+                      <el-tooltip placement="top">
+                        <div slot="content">{{ message }}</div>
+                        <span
+                          ><el-input
+                            v-model="scope.row.publicSimple"
+                            @input="
+                              sum(
+                                scope.row,
+                                scope.row.publicSimple,
+                                'publicSimple'
+                              )
+                            "
+                          ></el-input
+                        ></span>
+                      </el-tooltip>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="公开(中等)" width="100">
+                  <template slot-scope="scope">
+                    <div
+                      @mouseenter="showActiveIn(1, 2, scope.row)"
+                      @mouseleave="showActiveOff"
+                    >
+                      <el-tooltip placement="top">
+                        <div slot="content">{{ message }}</div>
+                        <span
+                          ><el-input
+                            v-model="scope.row.publicMedium"
+                            @input="
+                              sum(
+                                scope.row,
+                                scope.row.publicMedium,
+                                'publicMedium'
+                              )
+                            "
+                          ></el-input
+                        ></span>
+                      </el-tooltip>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="公开(困难)" width="100">
+                  <template slot-scope="scope">
+                    <div
+                      @mouseenter="showActiveIn(1, 3, scope.row)"
+                      @mouseleave="showActiveOff"
+                    >
+                      <el-tooltip placement="top">
+                        <div slot="content">{{ message }}</div>
+                        <span
+                          ><el-input
+                            v-model="scope.row.publicDifficulty"
+                            @input="
+                              sum(
+                                scope.row,
+                                scope.row.publicDifficulty,
+                                'publicDifficulty'
+                              )
+                            "
+                          ></el-input
+                        ></span>
+                      </el-tooltip>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="非公开(简单)" width="100">
+                  <template slot-scope="scope">
+                    <div
+                      @mouseenter="showActiveIn(0, 1, scope.row)"
+                      @mouseleave="showActiveOff"
+                    >
+                      <el-tooltip placement="top">
+                        <div slot="content">{{ message }}</div>
+                        <span
+                          ><el-input
+                            v-model="scope.row.noPublicSimple"
+                            @input="
+                              sum(
+                                scope.row,
+                                scope.row.noPublicSimple,
+                                'noPublicSimple'
+                              )
+                            "
+                          ></el-input
+                        ></span>
+                      </el-tooltip>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="非公开(中等)" width="100">
+                  <template slot-scope="scope">
+                    <div
+                      @mouseenter="showActiveIn(0, 2, scope.row)"
+                      @mouseleave="showActiveOff"
+                    >
+                      <el-tooltip placement="top">
+                        <div slot="content">{{ message }}</div>
+                        <span
+                          ><el-input
+                            v-model="scope.row.noPublicMedium"
+                            @input="
+                              sum(
+                                scope.row,
+                                scope.row.noPublicMedium,
+                                'noPublicMedium'
+                              )
+                            "
+                          ></el-input
+                        ></span>
+                      </el-tooltip>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="非公开(困难)" width="100">
+                  <template slot-scope="scope">
+                    <div
+                      @mouseenter="showActiveIn(0, 3, scope.row)"
+                      @mouseleave="showActiveOff"
+                    >
+                      <el-tooltip placement="top">
+                        <div slot="content">{{ message }}</div>
+                        <span
+                          ><el-input
+                            v-model="scope.row.noPublicDifficulty"
+                            @input="
+                              sum(
+                                scope.row,
+                                scope.row.noPublicDifficulty,
+                                'noPublicDifficulty'
+                              )
+                            "
+                          ></el-input
+                        ></span>
+                      </el-tooltip>
+                    </div>
+                  </template>
+                </el-table-column>
+                <!-- 总数求和 -->
+                <el-table-column :label="setName()" width="100">
+                  <template slot-scope="scope">
+                    <div>
+                      <span
+                        ><el-input
+                          v-model="scope.row.count"
+                          placeholder="抽取数值"
+                          :disabled="true"
+                        ></el-input
+                      ></span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作">
+                  <template slot-scope="scope">
+                    <div>
+                      <el-button
+                        type="danger"
+                        size="mini"
+                        @click="removeSelected(scope.row.id)"
+                        >移除</el-button
+                      >
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+            <!-- 精确 -->
+            <div
+              v-show="genType == 'NORMAL'"
+              style="width: 50%; overflow: auto"
+            >
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item label="试卷结构" class="form-item">
+                  <el-select
+                    v-model="genPaper.paperStructId"
+                    clearable
+                    placeholder="试卷结构"
+                    @change="changePaperStruct"
+                  >
+                    <el-option
+                      v-for="item in exactStructs"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-form>
+              <el-table :data="paperDetailStructs" border>
+                <el-table-column label="名称" width="250">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="总分" width="100">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.totalScore }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="题量">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.detailCount }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+            <!-- 蓝图 -->
+            <div v-show="genType == 'BLUE'" style="width: 50%; overflow: auto">
+              <el-form
+                :inline="true"
+                label-position="right"
+                label-width="100px"
+              >
+                <el-form-item label="试卷结构" class="form-item">
+                  <el-select
+                    v-model="genPaper.paperStructId"
+                    clearable
+                    placeholder="试卷结构"
+                    style="width: 120px"
+                    @change="changePaperStruct"
+                  >
+                    <el-option
+                      v-for="item in blueStructs"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="难度">
+                  <el-input
+                    v-model="difficulty"
+                    style="width: 50px"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-form>
+              <el-table :data="paperDetailStructs" border>
+                <el-table-column label="名称" width="250">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.name }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="总分" width="100">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.totalScore }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                <el-table-column label="题量">
+                  <template slot-scope="scope">
+                    <div>
+                      <span>{{ scope.row.detailCount }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+        <div class="pull-right">
+          <el-button type="primary" icon="check" @click="confirmGenPaper"
+            >确定</el-button
+          >
+          <el-button type="primary" icon="caret-left" @click="back"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <div v-show="genModelType == 'M-Type'">
+      <div style="width: 50%; overflow: auto">
+        <el-form :inline="true" label-position="right" label-width="100px">
+          <el-form-item label="试卷结构" class="form-item">
+            <el-select
+              v-model="genPaper.paperStructId"
+              clearable
+              placeholder="试卷结构"
+              @change="changePaperStruct"
+            >
+              <el-option
+                v-for="item in exactStructs"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
+        <el-table :data="paperDetailStructs" border>
+          <el-table-column label="名称" width="250">
+            <template slot-scope="scope">
+              <div>
+                <span>{{ scope.row.name }}</span>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="总分" width="100">
+            <template slot-scope="scope">
+              <div>
+                <span>{{ scope.row.totalScore }}</span>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="题量">
+            <template slot-scope="scope">
+              <div>
+                <span>{{ scope.row.detailCount }}</span>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="pull-right">
+        <el-button type="primary" icon="check" @click="confirmGenPaperMType"
+          >确定</el-button
+        >
+        <el-button type="primary" icon="caret-left" @click="back"
+          ><i class="el-icon-arrow-left"></i> 返 回</el-button
+        >
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { QUESTION_API, CORE_API } from "@/constants/constants";
+import { mapState } from "vuex";
+export default {
+  data() {
+    return {
+      tabs: "first",
+      genType: "SIMPLE",
+      genModelType: "A-Type",
+      frozenPaperSet: new Set(),
+      genPaper: {
+        frozenPaperIds: [],
+        courseNo: "",
+        courseName: "",
+        level: "",
+        paperName: "",
+        paperIds: [],
+        genNumber: "",
+        paperStructId: "",
+        simpleParams: {},
+        simpleGenPaperPolicy: "BY_QUESTIONNUM",
+      },
+      simpleGenPaperPolicys: [
+        { label: "按试题数量抽取", value: "BY_QUESTIONNUM" },
+        { label: "按试题分数抽取", value: "BY_SCORE" },
+      ],
+      tempPapers: [],
+      tempPaperIds: [],
+      selectPapers: [],
+      selectAllPapers: [],
+      paperStructs: [],
+      paperDetailStructs: [],
+      blueStructs: [], //蓝图组卷
+      exactStructs: [], //精确组卷
+      curSelect: 1,
+      totalSelect: 10,
+      pageSize: 10,
+      loading: false,
+      fullscreenLoading: false,
+      difficulty: "",
+      coursePropertyList: [],
+      paperType: "IMPORT",
+      selectedNPapers: [],
+      message: "",
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    selectedPapers() {
+      var selectedPapers = [];
+      for (let paper of this.tempPapers) {
+        selectedPapers.push({
+          id: paper.id,
+          name: paper.name,
+          totalScore: paper.totalScore,
+          unitCount: paper.unitCount,
+          simpleParam: "",
+        });
+      }
+      return selectedPapers;
+    },
+    selectedIds() {
+      var selectedIds = [];
+      for (let paper of this.selectedPapers) {
+        selectedIds.push(paper.id);
+      }
+      return selectedIds;
+    },
+  },
+  created() {
+    this.genPaper.courseNo = this.$route.params.courseNo;
+    var url =
+      CORE_API +
+      "/course/byCode?" +
+      "rootOrgId=" +
+      this.user.rootOrgId +
+      "&code=" +
+      this.genPaper.courseNo;
+    this.$httpWithMsg
+      .get(url)
+      .then((response) => {
+        this.genPaper.courseName = response.data.name;
+        this.genPaper.level = response.data.level;
+      })
+      .finally(() => {});
+    this.searchPaper();
+    this.getCoursePropertyList();
+  },
+  methods: {
+    frozenChange(val, paperId) {
+      if (val) {
+        this.frozenPaperSet.add(paperId);
+      } else {
+        this.frozenPaperSet.delete(paperId);
+      }
+    },
+    setName() {
+      if (this.genPaper.simpleGenPaperPolicy == "BY_QUESTIONNUM") {
+        return "抽取数量";
+      } else {
+        return "抽取分数";
+      }
+    },
+    genModelTypeChange() {
+      if (this.genModelType == "M-Type") {
+        this.genPaper.genNumber = "1";
+      }
+    },
+    genTypeChange() {
+      this.curSelect = 1;
+      this.tempPapers = [];
+      this.paperIds = [];
+      this.genPaper.paperStructId = "";
+      //this.genPaper.simpleGenPaperPolicy = '';
+      this.tempPaperIds = [];
+      this.paperDetailStructs = [];
+      this.searchPaper();
+    },
+    selectChange(val) {
+      console.log("val123:");
+      val.forEach((element) => {
+        element.publicSimple = 0;
+        element.publicMedium = 0;
+        element.publicDifficulty = 0;
+        element.noPublicSimple = 0;
+        element.noPublicMedium = 0;
+        element.noPublicDifficulty = 0;
+        element.count = 0;
+        this.tempPapers.push(Object.assign({}, element));
+        this.tempPaperIds.push(element.id);
+      });
+      this.searchPaper();
+    },
+    searchPaper() {
+      this.loading = true;
+      if (this.paperType == "IMPORT") {
+        this.$http
+          .get(
+            QUESTION_API +
+              "/importPaper/" +
+              this.tempPaperIds +
+              "/" +
+              this.curSelect +
+              "/" +
+              this.pageSize +
+              "?courseNo=" +
+              this.genPaper.courseNo
+          )
+          .then((response) => {
+            console.log("response:", response);
+            this.selectPapers = response.data.content;
+            this.totalSelect = response.data.totalElements;
+            this.curSelect = response.data.pageable.pageNumber + 1;
+            this.loading = false;
+          });
+      } else if (this.paperType == "GENERATE") {
+        this.$http
+          .get(
+            QUESTION_API +
+              "/genPaper/" +
+              this.tempPaperIds +
+              "/" +
+              this.curSelect +
+              "/" +
+              this.pageSize +
+              "?courseNo=" +
+              this.genPaper.courseNo
+          )
+          .then((response) => {
+            this.selectPapers = response.data.content;
+            this.totalSelect = response.data.totalElements;
+            this.curSelect = response.data.pageable.pageNumber + 1;
+            this.loading = false;
+          });
+      } else {
+        console.log("空值");
+        this.selectPapers = [];
+        this.totalSelect = 0;
+        this.loading = false;
+      }
+    },
+    changePaperStruct() {
+      this.paperDetailStructs = [];
+      this.searchPaperDetailStructs();
+    },
+    searchPaperStructs() {
+      console.log("bbb");
+      var courseNo = this.genPaper.courseNo;
+      var url = QUESTION_API + "/paperStruct?courseNo=" + courseNo;
+      this.loading = true;
+      this.$http.get(url).then((response) => {
+        this.paperStructs = response.data;
+        for (let paperStructObj of this.paperStructs) {
+          if (paperStructObj.paperStrucType == "BLUEPRINT") {
+            for (let couProperty of this.coursePropertyList) {
+              if (couProperty.id == paperStructObj.coursePropertyId) {
+                this.blueStructs.push(paperStructObj);
+              }
+            }
+          } else {
+            this.exactStructs.push(paperStructObj);
+          }
+        }
+        this.loading = false;
+      });
+    },
+    searchPaperDetailStructs() {
+      console.log("this.genPaper.paperStructId:", this.genPaper.paperStructId);
+      for (let paperStruct of this.paperStructs) {
+        if (paperStruct.id == this.genPaper.paperStructId) {
+          this.paperDetailStructs = paperStruct.paperDetailStructs;
+          this.difficulty = paperStruct.difficulty;
+          break;
+        }
+      }
+    },
+    checkGenPaper() {
+      if (!this.genPaper.paperName) {
+        this.$notify({
+          message: "试卷名称不能为空!",
+          type: "error",
+        });
+        return false;
+      }
+      var numReg = /^[1-9]\d*$/;
+      if (!this.genPaper.genNumber) {
+        this.$notify({
+          message: "请输入需要组卷套数!",
+          type: "error",
+        });
+        return false;
+      }
+      if (
+        !numReg.test(this.genPaper.genNumber) ||
+        parseInt(this.genPaper.genNumber) >= 100
+      ) {
+        this.$notify({
+          message: "组卷套数必须为1-99之间的整数!",
+          type: "error",
+        });
+        return false;
+      }
+      if (this.genType == "SIMPLE" && this.selectedIds.length == 0) {
+        this.$notify({
+          message: "请选择题源范围!",
+          type: "error",
+        });
+        return false;
+      }
+      if (this.genType == "NORMAL") {
+        if (!this.selectedIds || this.selectedIds.length == 0) {
+          this.$notify({
+            message: "请选择题源范围!",
+            type: "error",
+          });
+          return false;
+        } else if (this.paperDetailStructs.length == 0) {
+          this.$notify({
+            message: "请选择试卷结构!",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      if (this.genType == "BLUE") {
+        if (!this.selectedIds || this.selectedIds.length == 0) {
+          this.$notify({
+            message: "请选择题源范围!",
+            type: "error",
+          });
+          return false;
+        } else if (this.paperDetailStructs.length == 0) {
+          this.$notify({
+            message: "请选择试卷结构!",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      return true;
+    },
+    confirmGenPaperMType() {
+      if (!this.genPaper.paperName) {
+        this.$notify({
+          message: "试卷名称不能为空!",
+          type: "error",
+        });
+        return false;
+      }
+      if (this.paperDetailStructs.length == 0) {
+        this.$notify({
+          message: "请选择试卷结构!",
+          type: "error",
+        });
+        return false;
+      }
+      var url = QUESTION_API + "/genPaper/manual";
+
+      this.fullscreenLoading = true;
+      this.$http.post(url, this.genPaper).then(
+        (response) => {
+          this.$notify({
+            message: "组卷成功",
+            type: "success",
+          });
+          this.fullscreenLoading = false;
+          let paperId = response.data.paper.id;
+          this.$router.push({
+            path: "/edit_paper/" + paperId + "/gen_paper",
+          });
+        },
+        (error) => {
+          this.$notify({
+            message: error.response.data.msg,
+            type: "error",
+          });
+          this.fullscreenLoading = false;
+        }
+      );
+    },
+    confirmGenPaper() {
+      if (!this.checkGenPaper()) {
+        return false;
+      } else {
+        var url = "";
+        if (this.genType == "SIMPLE") {
+          url = QUESTION_API + "/genPaper/simple";
+        } else if (this.genType == "NORMAL") {
+          url = QUESTION_API + "/genPaper/normal";
+        } else {
+          url = QUESTION_API + "/genPaper/blue";
+        }
+        if (!this.setSimpleParams()) {
+          return;
+        }
+        this.fullscreenLoading = true;
+        this.genPaper.paperIds = this.selectedIds;
+        this.genPaper.frozenPaperIds = [];
+        this.frozenPaperSet.forEach((element) => {
+          this.genPaper.frozenPaperIds.push(element);
+        });
+        this.$http.post(url, this.genPaper).then(
+          (response) => {
+            this.$notify({
+              message: "组卷成功",
+              type: "success",
+            });
+            this.fullscreenLoading = false;
+            if (this.genPaper.genNumber == 1) {
+              let paperId = response.data.paper.id;
+              this.$router.push({
+                path: "/edit_paper/" + paperId + "/gen_paper",
+              });
+            } else {
+              this.back();
+            }
+          },
+          (error) => {
+            this.$notify({
+              message: error.response.data.msg,
+              type: "error",
+            });
+            this.fullscreenLoading = false;
+          }
+        );
+      }
+    },
+    selectCurrentChange(val) {
+      this.curSelect = val;
+      this.searchPaper();
+    },
+    back() {
+      this.$router.push({ path: "/questions/gen_paper/1" });
+    },
+    removePaper(id) {
+      for (let [index, paper] of this.selectPapers.entries()) {
+        if (id == paper.id) {
+          this.selectPapers.splice(index, 1);
+          break;
+        }
+      }
+    },
+    removeSelected(id) {
+      for (let [index, paper] of this.tempPapers.entries()) {
+        if (id == paper.id) {
+          this.tempPapers.splice(index, 1);
+          this.selectPapers.push(paper);
+          break;
+        }
+      }
+      for (var i = 0; i < this.tempPaperIds.length; i++) {
+        if (this.tempPaperIds[i] == id) {
+          this.tempPaperIds.splice(i, 1);
+          break;
+        }
+      }
+      this.searchPaper();
+    },
+    setSimpleParams() {
+      var simpleParams = new Object();
+      for (let paper of this.tempPapers) {
+        console.log("paper", paper);
+        if (this.genPaper.simpleGenPaperPolicy == "BY_SCORE") {
+          if (!this.isNumber(paper.publicSimple)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取分数”列要为正数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isNumber(paper.publicMedium)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取分数”列要为正数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isNumber(paper.publicDifficulty)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取分数”列要为正数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isNumber(paper.noPublicSimple)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取分数”列要为正数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isNumber(paper.noPublicMedium)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取分数”列要为正数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isNumber(paper.noPublicDifficulty)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取分数”列要为正数!",
+              type: "error",
+            });
+            return false;
+          }
+        } else {
+          if (!this.isFloat(paper.publicSimple)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取数量”列要为正整数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isFloat(paper.publicMedium)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取数量”列要为正整数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isFloat(paper.publicDifficulty)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取数量”列要为正整数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isFloat(paper.noPublicSimple)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取数量”列要为正整数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isFloat(paper.noPublicMedium)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取数量”列要为正整数!",
+              type: "error",
+            });
+            return false;
+          }
+          if (!this.isFloat(paper.noPublicDifficulty)) {
+            this.$notify({
+              message: "“抽取策略”中的“抽取数量”列要为正整数!",
+              type: "error",
+            });
+            return false;
+          }
+        }
+        var numberParams = {};
+        (numberParams.publicSimple = paper.publicSimple),
+          (numberParams.publicMedium = paper.publicMedium),
+          (numberParams.publicDifficulty = paper.publicDifficulty),
+          (numberParams.noPublicSimple = paper.noPublicSimple),
+          (numberParams.noPublicMedium = paper.noPublicMedium),
+          (numberParams.noPublicDifficulty = paper.noPublicDifficulty),
+          (numberParams.count = paper.count);
+        simpleParams[paper.id] = numberParams;
+      }
+      this.genPaper.simpleParams = simpleParams;
+      console.log("this.genPaper.simpleParams:", this.genPaper.simpleParams);
+      return true;
+    },
+    //查询课程下开启的课程属性
+    getCoursePropertyList() {
+      var courseNo = this.genPaper.courseNo;
+      this.$http
+        .get(QUESTION_API + "/courseProperty/enable/" + courseNo)
+        .then((response) => {
+          this.coursePropertyList = response.data;
+          this.searchPaperStructs();
+        });
+    },
+    changePaperType() {
+      this.curSelect = 1;
+      this.tempPapers = [];
+      this.tempPaperIds = [];
+      this.searchPaper();
+    },
+    sum(row) {
+      row.count =
+        parseFloat(row.publicSimple) +
+        parseFloat(row.publicMedium) +
+        parseFloat(row.publicDifficulty) +
+        parseFloat(row.noPublicSimple) +
+        parseFloat(row.noPublicMedium) +
+        parseFloat(row.noPublicDifficulty);
+    },
+    //判断是否为正数
+    isNumber(num) {
+      var reg = /^\d+(?=\.{0,1}\d+$|$)/;
+      if (reg.test(num)) {
+        return true;
+      }
+      return false;
+    },
+    //判断正小数
+    isFloat(num) {
+      var reg = /^[0-9]+$/;
+      if (reg.test(num)) {
+        return true;
+      }
+      return false;
+    },
+    //判断输入字符串是否为空或者全部都是空格
+    isNull(str) {
+      if (str == "") {
+        return true;
+      }
+      var regu = "^[ ]+$";
+      var re = new RegExp(regu);
+      return re.test(str);
+    },
+    //鼠标悬浮事件
+    showActiveIn(publicity, difficulty, row) {
+      //console.log('this.genPaper.simpleGenPaperPolicy',row);
+      if (this.genPaper.simpleGenPaperPolicy == "BY_QUESTIONNUM") {
+        this.message = row.quesCount[publicity + "-" + difficulty];
+      }
+      if (this.genPaper.simpleGenPaperPolicy == "BY_SCORE") {
+        this.message = row.quesScore[publicity + "-" + difficulty];
+      }
+    },
+    //鼠标离开事件
+    showActiveOff() {
+      this.message = "";
+    },
+  },
+};
+</script>
+<style lang="css" scoped>
+.tabs {
+  margin-left: 20px;
+  margin-bottom: 10px;
+}
+
+.form-item {
+  width: 300px;
+}
+
+.margin-bottom {
+  margin-bottom: 10px;
+}
+
+.label-margin {
+  margin-right: 10px;
+}
+</style>
+<style scoped src="../styles/Common.css"></style>

+ 874 - 0
src/modules/questions/views/ImportPaper.vue

@@ -0,0 +1,874 @@
+<template>
+  <section class="content">
+    <div v-show="isClear == 1">
+      <LinkTitlesCustom :current-paths="['题库管理 ', '导入试卷管理']" />
+    </div>
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="课程名称">
+              <el-select
+                v-model="formSearch.courseNo"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                placeholder="请输入"
+                size="small"
+                @focus="(e) => getCourses(e.target.value)"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseNo"
+                  :label="item.courseInfo"
+                  :value="item.courseNo"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="试卷名称">
+              <el-input
+                v-model="formSearch.name"
+                class="search_width"
+                placeholder="试卷名称"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="层次">
+              <el-select
+                v-model="formSearch.level"
+                class="search_width"
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in levelList"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="录入人">
+              <el-input
+                v-model="formSearch.creator"
+                class="search_width"
+                placeholder="录入人"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="修改人">
+              <el-input
+                v-model="formSearch.lastModifyName"
+                class="search_width"
+                placeholder="修改人"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" type="primary" @click="impPaper"
+                ><i class="el-icon-upload2"></i> 导入</el-button
+              >
+              <el-dropdown class="button_left">
+                <el-button type="primary" size="small">
+                  更多 <i class="el-icon-arrow-down el-icon--right"></i>
+                </el-button>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item>
+                    <el-button size="small" type="primary" @click="expQuesType"
+                      >试题分布</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button size="small" type="primary" @click="openQuesPro"
+                      >试卷属性</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button size="small" type="primary" @click="openQuesProC"
+                      >课程属性</el-button
+                    >
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+          </el-col>
+        </el-row>
+        <div
+          style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+        ></div>
+        <el-row>
+          <el-form-item>
+            <span>批量操作:</span>
+            <el-button
+              size="small"
+              type="primary"
+              :disabled="noBatchSelected"
+              @click="useBasePaper"
+              ><i class="el-icon-star-on"></i> 使用原卷</el-button
+            >
+            <el-button
+              size="small"
+              type="danger"
+              :disabled="noBatchSelected"
+              @click="batchDeletePaper"
+              ><i class="el-icon-delete"></i> 删除原卷</el-button
+            >
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div style="width: 100%; margin-bottom: 10px"></div>
+      <el-table
+        v-loading="loading"
+        element-loading-text="拼命加载中"
+        :data="tableData"
+        border
+        style="width: 100%"
+        @selection-change="selectChange"
+      >
+        <el-table-column type="selection" width="40"></el-table-column>
+        <el-table-column label="课程名称" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.course.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程代码" width="80">
+          <template slot-scope="scope">
+            <span>{{ scope.row.course.code }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="试卷名称" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="试卷总分"
+          width="103"
+          sortable
+          prop="totalScore"
+        >
+        </el-table-column>
+        <el-table-column
+          label="试卷难度"
+          width="103"
+          sortable
+          prop="difficultyDegree"
+        >
+        </el-table-column>
+        <el-table-column
+          label="大题数量"
+          width="103"
+          sortable
+          prop="paperDetailCount"
+        >
+        </el-table-column>
+        <el-table-column label="录入员" width="150">
+          <template slot-scope="scope">
+            <span>{{ scope.row.creator }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="创建时间"
+          width="153"
+          sortable
+          prop="createTime"
+        >
+        </el-table-column>
+        <el-table-column label="修改人" width="150">
+          <template slot-scope="scope">
+            <span>{{ scope.row.lastModifyName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="修改时间"
+          width="153"
+          sortable
+          prop="updateDate"
+        >
+        </el-table-column>
+        <el-table-column label="操作" width="180" fixed="right">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="editImportPaper(scope.row)"
+                ><i class="el-icon-edit"></i>编辑</el-button
+              >
+              <el-dropdown class="button_left">
+                <el-button type="primary" size="mini" plain>
+                  更多 <i class="el-icon-arrow-down el-icon--right"></i>
+                </el-button>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="primary"
+                      plain
+                      @click="copyImportPaper(scope.row)"
+                      ><i class="el-icon-tickets"></i> 复制</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="primary"
+                      plain
+                      @click="exportOriginalPaper(scope.row)"
+                      ><i class="el-icon-download"></i> 下载</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="primary"
+                      plain
+                      @click="exportPaperAnswer(scope.row)"
+                      ><i class="el-icon-download"></i>导出答案</el-button
+                    >
+                  </el-dropdown-item>
+                  <el-dropdown-item>
+                    <el-button
+                      size="mini"
+                      type="danger"
+                      @click="deleteImportPaper(scope.row)"
+                      ><i class="el-icon-delete"></i> 删除</el-button
+                    >
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+    <el-dialog
+      v-loading.body="cloneLoading"
+      title="克隆试卷"
+      :visible.sync="copyPaperDialog"
+      width="500px"
+    >
+      <el-form
+        ref="copyPaperForm"
+        :model="copyPaperForm"
+        :rules="rules"
+        label-position="right"
+        label-width="120px"
+        inline-message
+      >
+        <el-row>
+          <el-form-item label="试卷名称" prop="paperName">
+            <el-input
+              v-model="copyPaperForm.paperName"
+              class="dialog_input_width"
+              placeholder="请输试卷名称"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="课程名称" prop="courseNo">
+            <el-select
+              v-model="copyPaperForm.courseNo"
+              class="dialog_input_width"
+              :remote-method="getCourses"
+              remote
+              filterable
+              clearable
+              placeholder="全部"
+              @focus="(e) => getCourses(e.target.value)"
+            >
+              <el-option
+                v-for="item in courseInfoSelect"
+                :key="item.courseNo"
+                :label="item.courseInfo"
+                :value="item.courseNo"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button type="primary" @click="submitCopy('copyPaperForm')"
+            >保 存</el-button
+          >
+          <el-button @click="resetForm2('copyPaperForm')"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          >
+          <el-button type="primary" @click="back2('copyPaperForm')"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+    <el-dialog
+      title="属性修改"
+      :visible.sync="quesPropertyDialog"
+      width="500px"
+    >
+      <el-form
+        :loading="quesLoading"
+        label-position="right"
+        label-width="120px"
+      >
+        <el-row>
+          <el-form-item label="难  度">
+            <el-select v-model="difficultyDegree" class="dialog_input_width">
+              <el-option
+                v-for="item in difficultyDegreeList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="公开度">
+            <el-select v-model="publicity" class="dialog_input_width">
+              <el-option
+                v-for="item in publicityList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row class="margin_top_10 margin_left_120">
+          <el-button
+            v-loading.fullscreen.lock="quesLoading"
+            type="primary"
+            @click="submitUpdate()"
+            >保 存</el-button
+          >
+          <el-button @click="resetForm"
+            ><i class="el-icon-refresh"></i> 重 置</el-button
+          >
+          <el-button type="primary" @click="closePropertyDialog"
+            ><i class="el-icon-arrow-left"></i> 返 回</el-button
+          >
+        </el-row>
+      </el-form>
+    </el-dialog>
+  </section>
+</template>
+
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import { LEVEL_TYPE, PUBLICITY_LIST } from "../constants/constants";
+import { mapState } from "vuex";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      courseLoading: false,
+      quesLoading: false,
+      formSearch: {
+        courseNo: "",
+        courseName: "",
+        creator: "",
+        lastModifyName: "",
+        level: "",
+        name: "",
+      },
+      tableData: [],
+      levelList: LEVEL_TYPE,
+      currentPage: 1,
+      pageSize: 10,
+      isClear: 0,
+      total: 0,
+      loading: false,
+      courseList: [],
+      options: [],
+      selectedPaperIds: [],
+      cloneLoading: false,
+      copyPaperDialog: false,
+      copyPaperForm: {
+        paperName: "",
+        courseNo: null,
+      },
+      copyPaperId: "",
+      quesPropertyDialog: false,
+      difficultyDegree: 0.1,
+      publicity: true,
+      updateType: "",
+      difficultyDegreeList: [
+        { label: 0.1, value: 0.1 },
+        { label: 0.2, value: 0.2 },
+        { label: 0.3, value: 0.3 },
+        { label: 0.4, value: 0.4 },
+        { label: 0.5, value: 0.5 },
+        { label: 0.6, value: 0.6 },
+        { label: 0.7, value: 0.7 },
+        { label: 0.8, value: 0.8 },
+        { label: 0.9, value: 0.9 },
+        { label: 1.0, value: 1.0 },
+      ],
+      publicityList: PUBLICITY_LIST,
+      rules: {
+        paperName: [
+          { required: true, message: "请输试卷名称", trigger: "blur" },
+        ],
+        courseNo: [
+          { required: true, message: "请选择课程名称", trigger: "change" },
+        ],
+      },
+    };
+  },
+  computed: {
+    paperIds() {
+      var paperIds = "";
+      for (let paperId of this.selectedPaperIds) {
+        if (!paperIds) {
+          paperIds += paperId;
+        } else {
+          paperIds += "," + paperId;
+        }
+      }
+      return paperIds;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+    noBatchSelected() {
+      return this.selectedPaperIds.length === 0;
+    },
+    ...mapState({ user: (state) => state.user }),
+  },
+  watch: {
+    $route: "initVue",
+  },
+  created() {
+    this.initVue();
+  },
+  methods: {
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchImportPaper();
+    },
+    searchImportPaper() {
+      var pageNo = Number(this.currentPage);
+      this.currentPage = 1;
+      this.loading = true;
+      var url = QUESTION_API + "/importPaper/" + pageNo + "/" + this.pageSize;
+      this.$http.get(url, { params: this.formSearch }).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.currentPage = pageNo;
+        this.loading = false;
+      });
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchImportPaper();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchImportPaper();
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.formSearch.courseName = course.name;
+        }
+      }
+    },
+    editImportPaper(row) {
+      //缓存查询对象
+      this.getCourseName(this.formSearch.courseNo);
+      sessionStorage.setItem("import_paper", JSON.stringify(this.formSearch));
+      sessionStorage.setItem("import_paper_currentPage", this.currentPage);
+      sessionStorage.setItem("question_back", "false");
+      this.$router.push({
+        path: "/edit_paper/" + row.id + "/import_paper",
+      });
+    },
+    deleteImportPaper(row) {
+      this.$confirm("确认删除试卷吗?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http.delete(QUESTION_API + "/paper/" + row.id).then(
+          () => {
+            this.$notify({
+              message: "删除成功",
+              type: "success",
+            });
+            this.searchImportPaper();
+          },
+          (error) => {
+            this.$notify({
+              message: error.response.data.desc,
+              type: "error",
+            });
+            this.loading = false;
+          }
+        );
+      });
+    },
+    selectChange(row) {
+      this.selectedPaperIds = [];
+      row.forEach((element) => {
+        this.selectedPaperIds.push(element.id);
+      });
+    },
+    batchDeletePaper() {
+      var paperIds = this.paperIds;
+      this.$confirm("确认删除试卷吗?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http.delete(QUESTION_API + "/paper/" + paperIds).then(
+          () => {
+            this.$notify({
+              message: "删除成功",
+              type: "success",
+            });
+            this.searchImportPaper();
+            this.selectedPaperIds = [];
+          },
+          (error) => {
+            this.$notify({
+              message: error.response.data.desc,
+              type: "error",
+            });
+            this.loading = false;
+          }
+        );
+      });
+    },
+    //导入试卷
+    impPaper() {
+      this.$router.push({
+        path: "/questions/import_paper_info",
+      });
+    },
+    //查询所有课程
+    getCourses(query) {
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+        });
+    },
+    useBasePaper() {
+      this.loading = true;
+      this.$http
+        .put(QUESTION_API + "/useBasePaper/" + this.selectedPaperIds)
+        .then(
+          (response) => {
+            console.log("come in response:", response);
+            if (!response.data.desc) {
+              this.$notify({
+                message: "操作成功",
+                type: "success",
+              });
+            } else {
+              this.$notify({
+                message: response.data.desc,
+                type: "error",
+              });
+            }
+            this.loading = false;
+          },
+          (error) => {
+            var message = error.response.data.desc;
+            var err = message
+              .replace("[", "")
+              .replace("]", "")
+              .substring(message.indexOf("desc") + 4);
+            this.$notify({
+              message: err,
+              type: "error",
+            });
+            this.loading = false;
+          }
+        );
+    },
+    //克隆试卷
+    copyImportPaper(row) {
+      this.copyPaperForm.paperName = "";
+      this.copyPaperForm.courseNo = "";
+      this.copyPaperId = row.id;
+      this.copyPaperDialog = true;
+    },
+    //保存
+    submitCopy(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          this.cloneLoading = true;
+          this.$http
+            .post(
+              QUESTION_API +
+                "/clonePaper/" +
+                this.copyPaperId +
+                "/" +
+                this.copyPaperForm.paperName +
+                "/" +
+                this.copyPaperForm.courseNo
+            )
+            .then(() => {
+              this.$notify({
+                message: "复制成功",
+                type: "success",
+              });
+              this.cloneLoading = false;
+              this.copyPaperDialog = false;
+              this.searchImportPaper();
+            })
+            .catch((error) => {
+              this.$notify({
+                type: "error",
+                message: error.response.data.msg,
+              });
+              this.cloneLoading = false;
+            });
+        } else {
+          return false;
+        }
+      });
+    },
+    removeItem() {
+      sessionStorage.removeItem("import_paper");
+      sessionStorage.removeItem("import_paper_currentPage");
+    },
+    expQuesType() {
+      if (!this.formSearch.courseNo) {
+        this.$notify({
+          title: "警告",
+          message: "请输入课程",
+          type: "warning",
+        });
+        return;
+      }
+      var key = this.user.key;
+      var token = this.user.token;
+      window.location.href =
+        QUESTION_API +
+        "/paper/export/course/question/" +
+        this.formSearch.courseNo +
+        "?$key=" +
+        key +
+        "&$token=" +
+        token;
+    },
+    exportOriginalPaper(row) {
+      var key = this.user.key;
+      var token = this.user.token;
+      //window.location.href = QUESTION_API+"/originalPaper/export/"+row.id+"/"+ this.user.displayName;
+      window.open(
+        QUESTION_API +
+          "/originalPaper/exportPlus/" +
+          row.id +
+          "/" +
+          this.user.displayName +
+          "?$key=" +
+          key +
+          "&$token=" +
+          token
+      );
+    },
+    exportPaperAnswer(row) {
+      var key = this.user.key;
+      var token = this.user.token;
+      window.open(
+        QUESTION_API +
+          "/paper/answer/export/" +
+          row.id +
+          "?$key=" +
+          key +
+          "&$token=" +
+          token
+      );
+    },
+    openInit() {
+      this.difficultyDegree = 0.1;
+      this.publicity = true;
+      this.quesPropertyDialog = true;
+    },
+    //重置
+    resetForm() {
+      this.difficultyDegree = 0.1;
+      this.publicity = true;
+    },
+    resetForm2(formData) {
+      this.copyPaperForm.paperName = "";
+      this.copyPaperForm.courseNo = "";
+      this.$refs[formData].clearValidate();
+    },
+    back2(formData) {
+      this.resetForm2(formData);
+      this.copyPaperDialog = false;
+    },
+    //试卷修改属性 openQuesPro
+    openQuesPro() {
+      if (!this.selectedPaperIds || this.selectedPaperIds.length < 1) {
+        this.$notify({
+          title: "警告",
+          message: "请选择试卷",
+          type: "warning",
+        });
+        return;
+      }
+      this.updateType = "paper";
+      this.openInit();
+    },
+    //课程修改属性 openQuesProC
+    openQuesProC() {
+      if (!this.formSearch.courseNo) {
+        this.$notify({
+          title: "警告",
+          message: "请输入课程",
+          type: "warning",
+        });
+        return;
+      }
+      this.updateType = "course";
+      this.openInit();
+    },
+    closePropertyDialog() {
+      this.quesPropertyDialog = false;
+    },
+    submitUpdate() {
+      console.log("aaa");
+      if (this.updateType == "paper") {
+        console.log("paper");
+        this.updateQuesPropertyByPaper();
+      }
+      if (this.updateType == "course") {
+        console.log("course");
+        this.updateQuesPropertyByCourse();
+      }
+    },
+    updateQuesPropertyByPaper() {
+      var params = "?ids=" + this.selectedPaperIds;
+      params = params + "&difficultyDegree=" + this.difficultyDegree;
+      params = params + "&publicity=" + this.publicity;
+      this.quesLoading = true;
+      this.$http
+        .put(QUESTION_API + "/question/updatePro/paperIds" + params)
+        .then(() => {
+          this.$notify({
+            message: "更新成功",
+            type: "success",
+          });
+          this.searchImportPaper();
+          this.quesLoading = false;
+          this.closePropertyDialog();
+        })
+        .catch(() => {
+          this.$notify({
+            type: "error",
+            message: "更新失败",
+          });
+          this.quesLoading = false;
+          this.closePropertyDialog();
+        });
+    },
+    updateQuesPropertyByCourse() {
+      this.quesLoading = true;
+      var orgId = this.user.rootOrgId;
+      this.$http
+        .put(
+          QUESTION_API +
+            "/question/updatePro/courseCode/" +
+            this.formSearch.courseNo +
+            "/" +
+            this.difficultyDegree +
+            "/" +
+            this.publicity +
+            "/" +
+            orgId
+        )
+        .then(() => {
+          this.$notify({
+            message: "更新成功",
+            type: "success",
+          });
+          this.quesLoading = false;
+          this.closePropertyDialog();
+        })
+        .catch(() => {
+          this.$notify({
+            type: "error",
+            message: "更新失败",
+          });
+          this.quesLoading = false;
+          this.closePropertyDialog();
+        });
+    },
+    initVue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        this.removeItem();
+        this.formSearch = {
+          courseNo: "",
+          level: "",
+          name: "",
+        };
+        this.currentPage = 1;
+      } else {
+        this.formSearch = JSON.parse(sessionStorage.getItem("import_paper"));
+        this.currentPage =
+          sessionStorage.getItem("import_paper_currentPage") == null
+            ? 1
+            : parseInt(sessionStorage.getItem("import_paper_currentPage"));
+      }
+      if (this.formSearch.courseName) {
+        this.getCourses(this.formSearch.courseName);
+      }
+      this.handleCurrentChange(this.currentPage);
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 315 - 0
src/modules/questions/views/ImportPaperInfo.vue

@@ -0,0 +1,315 @@
+<template>
+  <section class="content">
+    <LinkTitlesCustom :current-paths="['word文件上传']" />
+    <!-- 正文信息 -->
+    <div
+      v-loading.body="fileLoading"
+      class="box-body"
+      element-loading-text="试题上传中,请稍后..."
+    >
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="100px"
+      >
+        <el-row>
+          <el-form-item label="课程名称" label-width="120px" class="pull-left">
+            <el-select
+              v-model="formSearch.courseNo"
+              class="form_width"
+              filterable
+              :remote-method="getCourses"
+              remote
+              clearable
+              placeholder="请选择"
+              @change="searchCourseName"
+              @focus="(e) => getCourses(e.target.value)"
+            >
+              <el-option
+                v-for="item in courseInfoSelect"
+                :key="item.courseNo"
+                :label="item.courseInfo"
+                :value="item.courseNo"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item class="pull-right">
+            <el-button type="primary" @click="back"
+              ><i class="el-icon-arrow-left"></i> 返回</el-button
+            >
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="试卷名称" label-width="120px" class="pull-left">
+            <el-input
+              v-model="formSearch.name"
+              placeholder="试卷名称"
+              class="form_width"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="总分校验" label-width="120px" class="pull-left">
+            <el-radio v-model="scoreCheck" label="1">开启</el-radio>
+            <el-radio v-model="scoreCheck" label="0">关闭</el-radio>
+          </el-form-item>
+        </el-row>
+        <el-row v-if="scoreCheck == 1">
+          <el-form-item label="试卷总分" label-width="120px" class="pull-left">
+            <el-input
+              v-model="formSearch.totalScore"
+              placeholder="试卷总分"
+              class="form_width"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+        <!--
+              <el-row>
+                  <el-form-item label="相同大题名称" label-width="120px" class="pull-left">
+                      <el-radio  v-model="formSearch.sameName"  label="1">合并</el-radio>
+                      <el-radio  v-model="formSearch.sameName"  label="0">不合并</el-radio>
+                  </el-form-item>
+              </el-row>
+            -->
+        <el-row>
+          <el-form-item>
+            <el-upload
+              ref="upload"
+              class="form_left"
+              accept=".docx,.json"
+              :action="uploadAction"
+              :headers="uploadHeaders"
+              :data="uploadData"
+              :before-upload="beforeUpload"
+              :on-progress="uploadProgress"
+              :on-success="uploadSuccess"
+              :on-error="uploadError"
+              :file-list="fileList"
+              :auto-upload="false"
+              :multiple="false"
+            >
+              <el-button slot="trigger" type="primary"
+                ><i class="el-icon-search"></i> 选择文件</el-button
+              >
+              <el-button
+                style="margin-left: 10px"
+                type="primary"
+                @click="submitUpload"
+                ><i class="el-icon-check"></i> 确认上传
+              </el-button>
+              <el-button
+                style="margin-left: 10px"
+                type="primary"
+                @click="removeFile"
+                ><i class="el-icon-refresh"></i> 清空文件
+              </el-button>
+              <div slot="tip" class="el-upload__tip">
+                只能上传docx和json文件
+              </div>
+            </el-upload>
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <el-dialog title="错误提示" :visible.sync="errDialog">
+        <span style="font-size: large">{{ errMessage }} !</span>
+        <span slot="footer" class="dialog-footer">
+          <el-button @click="errDialog = false">确定</el-button>
+        </span>
+      </el-dialog>
+    </div>
+  </section>
+</template>
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import { mapState } from "vuex";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      scoreCheck: "0",
+      formSearch: {
+        courseNo: "",
+        couresName: "",
+        name: "",
+        totalScore: "",
+        courseLevel: "",
+        sameName: "0",
+      },
+      courseList: [],
+      uploadAction: "",
+      errMessage: "",
+      uploadData: {},
+      fileLoading: false,
+      button_tpye: false,
+      errDialog: false,
+      fileList: [],
+      uploadHeaders: {},
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+  },
+  watch: {
+    scoreCheck: function () {
+      if (this.scoreCheck == 0) {
+        this.formSearch.totalScore = "";
+      }
+    },
+  },
+  created() {
+    this.uploadAction = QUESTION_API + "/importPaper";
+    this.uploadData = { name: this.formSearch.name };
+    this.uploadHeaders = {
+      key: this.user.key,
+      token: this.user.token,
+    };
+  },
+  methods: {
+    searchCourseName() {
+      for (let course of this.courseList) {
+        if (course.code == this.formSearch.courseNo) {
+          this.formSearch.courseName = course.name;
+          this.formSearch.courseLevel = course.level;
+          console.log(this.formSearch.courseLevel);
+        }
+      }
+    },
+    beforeUpload(file) {
+      console.log(file);
+    },
+    uploadProgress() {
+      console.log("uploadProgress");
+    },
+    uploadSuccess(response) {
+      this.$notify({
+        message: "上传成功",
+        type: "success",
+      });
+      this.fileLoading = false;
+      sessionStorage.setItem("question_back", "true");
+      this.$router.push({
+        path: "/edit_paper/" + response.id + "/import_paper",
+      });
+    },
+    uploadError(err) {
+      var result = err.message.match(/\{.+}/);
+      var errMessage = JSON.parse(result[0]).desc;
+      this.errDialog = true;
+      this.errMessage = errMessage;
+      this.fileLoading = false;
+    },
+    initUpload() {
+      this.fileList = [];
+      this.formSearch.name = "";
+      this.formSearch.courseNo = "";
+    },
+    checkUpload() {
+      if (!this.formSearch.courseNo) {
+        this.$notify({
+          message: "课程名称不能为空",
+          type: "error",
+        });
+        return false;
+      } else {
+        this.uploadData.courseNo = this.formSearch.courseNo;
+        this.uploadData.courseName = this.formSearch.courseName;
+        this.uploadData.level = this.formSearch.courseLevel;
+        this.uploadData.sameName = this.formSearch.sameName;
+      }
+      if (!this.formSearch.name) {
+        this.$notify({
+          message: "试卷名称不能为空",
+          type: "error",
+        });
+        return false;
+      } else {
+        this.uploadData.name = this.formSearch.name;
+      }
+      if (this.scoreCheck == 1 && !this.formSearch.totalScore) {
+        this.$notify({
+          message: "试卷总分不能为空",
+          type: "error",
+        });
+        return false;
+      } else {
+        this.uploadData.totalScore = this.formSearch.totalScore;
+      }
+      var fileList = this.$refs.upload.uploadFiles;
+      if (fileList.length == 0) {
+        this.$notify({
+          message: "上传文件不能为空",
+          type: "error",
+        });
+        return false;
+      }
+      if (fileList.length > 1) {
+        this.$notify({
+          message: "每次只能上传一个word或json文件",
+          type: "error",
+        });
+        return false;
+      }
+      for (let file of fileList) {
+        if (!file.name.endsWith(".docx") && !file.name.endsWith(".json")) {
+          this.$notify({
+            message: "上传文件必须为docx或json格式",
+            type: "error",
+          });
+          this.initUpload();
+          return false;
+        }
+      }
+      return true;
+    },
+    //确定上传
+    submitUpload() {
+      console.log("this form:", this.formSearch);
+      if (!this.checkUpload()) {
+        return false;
+      }
+      this.$refs.upload.submit();
+      this.fileLoading = true;
+    },
+    //清空文件
+    removeFile() {
+      // this.fileList = [];
+      this.$refs.upload.clearFiles();
+    },
+    //返回
+    back() {
+      this.$router.push({ path: "/questions/import_paper/0" });
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+        });
+    },
+  },
+};
+</script>
+<style scoped>
+.form_width {
+  width: 200px;
+}
+.form_left {
+  margin-left: 50px;
+}
+</style>

+ 657 - 0
src/modules/questions/views/InsertBluePaperStructure.vue

@@ -0,0 +1,657 @@
+<template>
+  <div id="insertBlueApp">
+    <LinkTitlesCustom
+      :current-paths="['基础信息 ', '蓝图结构预设', '蓝图结构创建']"
+    />
+    <section class="content">
+      <!-- 正文信息 -->
+      <div class="box-body">
+        <el-form
+          :inline="true"
+          :model="blueStruct"
+          label-position="right"
+          label-width="78px"
+        >
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="结构名称">
+                <el-input
+                  v-model="blueStruct.name"
+                  class="search_width"
+                  placeholder="试卷结构名称"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="设置类型">
+                <el-select
+                  v-model="blueStruct.genPaperType"
+                  class="search_width"
+                  filterable
+                  disabled
+                  size="small"
+                >
+                  <el-option label="细节组卷" value="SPECIFIC"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="总 分">
+                <el-input
+                  v-model.number="blueStruct.totalScore"
+                  class="search_width"
+                  placeholder="请输入总分"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="制定课程">
+                <el-select
+                  v-model="blueStruct.courseNo"
+                  class="search_width"
+                  :disabled="showcoursePropertyId"
+                  filterable
+                  :remote-method="getCourses"
+                  remote
+                  clearable
+                  placeholder="课程名称"
+                  size="small"
+                  @change="clearCourseProperty"
+                  @focus="(e) => getCourses(e.target.value)"
+                >
+                  <el-option
+                    v-for="item in courseInfoSelect"
+                    :key="item.courseNo"
+                    :label="item.courseInfo"
+                    :value="item.courseNo"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="课程属性">
+                <el-select
+                  v-model="blueStruct.coursePropertyId"
+                  class="search_width"
+                  filterable
+                  :disabled="showcoursePropertyId"
+                  placeholder="课程属性"
+                  size="small"
+                  @change="getCoursePropertyName"
+                >
+                  <el-option
+                    v-for="item in coursePropertyList"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-form-item label="考试说明">
+            <ckeditor
+              v-model="blueStruct.examRemark"
+              :display="display"
+              :width="wValue"
+              :height="hValue"
+            ></ckeditor>
+          </el-form-item>
+        </el-form>
+
+        <!-- 添加大题弹出框 -->
+        <el-dialog title="大题信息" :visible.sync="detailDialog" width="500px">
+          <el-form
+            ref="paperDetailStructForm"
+            :model="paperDetailStructForm"
+            :rules="rules"
+            label-position="right"
+            label-width="90px"
+            inline-message
+          >
+            <el-row>
+              <el-form-item label="大题名称" label-width="120px" prop="name">
+                <el-input
+                  v-model="paperDetailStructForm.name"
+                  class="dialog_input_width"
+                  placeholder="请输入题型名称"
+                ></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row>
+              <el-form-item label="题型描述" label-width="120px" prop="remark">
+                <el-input
+                  v-model="paperDetailStructForm.remark"
+                  class="dialog_input_width"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="请输入题型描述"
+                ></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row class="margin_top_10 margin_left_120">
+              <span v-if="dialogType == 'ADD'">
+                <el-button
+                  type="primary"
+                  @click="saveDetail('paperDetailStructForm')"
+                  >保 存</el-button
+                >
+              </span>
+              <span v-else>
+                <el-button
+                  type="primary"
+                  @click="saveEditDetail('paperDetailStructForm')"
+                  >保 存</el-button
+                >
+              </span>
+              <span class="margin_left_10">
+                <el-button @click="resetForm('paperDetailStructForm')"
+                  ><i class="el-icon-refresh"></i> 重 置</el-button
+                >
+              </span>
+              <span class="margin_left_10">
+                <el-button
+                  type="primary"
+                  @click="closeDialog('paperDetailStructForm')"
+                  ><i class="el-icon-arrow-left"></i> 返 回</el-button
+                >
+              </span>
+            </el-row>
+          </el-form>
+        </el-dialog>
+
+        <!-- 页面列表 -->
+        <div style="margin-top: 10px"></div>
+        <el-table :data="paperDetailStructs" border style="width: 100%">
+          <el-table-column label="大题名称">
+            <template slot-scope="scope">
+              <span>{{ scope.row.name }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="小题数">
+            <template slot-scope="scope">
+              <span>{{ scope.row.detailCount }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="大题分数">
+            <template slot-scope="scope">
+              <span>{{ scope.row.totalScore }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="题型">
+            <template slot-scope="scope">
+              <span>{{ getTpye(scope.row.questionType) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="292">
+            <template slot-scope="scope">
+              <div class="operate_left">
+                <el-button
+                  size="mini"
+                  type="primary"
+                  @click="detailInfo(scope.row)"
+                  ><i class="el-icon-plus"></i> 大题详情</el-button
+                >
+                <el-button
+                  size="mini"
+                  type="primary"
+                  @click="editDetail(scope.row)"
+                  ><i class="el-icon-edit"></i>编辑</el-button
+                >
+                <el-button
+                  size="mini"
+                  type="danger"
+                  @click="deleteDetail(scope.row)"
+                  ><i class="el-icon-delete"></i> 删除</el-button
+                >
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="search_down margin_top_10">
+        <el-button size="small" type="primary" @click="saveBlueStruct"
+          >保存试卷结构</el-button
+        >
+        <el-button size="small" type="primary" @click="insertDetail"
+          ><i class="el-icon-plus"></i> 添加大题</el-button
+        >
+        <el-button size="small" type="primary" @click="back"
+          ><i class="el-icon-arrow-left"></i> 返 回</el-button
+        >
+      </div>
+    </section>
+  </div>
+</template>
+<script>
+import { QUESTION_TYPES } from "../constants/constants";
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import ckeditor from "../component/ckeditor.vue";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  name: "InsertBlueApp",
+  components: {
+    ckeditor,
+    LinkTitlesCustom,
+  },
+  data() {
+    return {
+      hValue: "100px",
+      wValue: "800px",
+      display: "block",
+      blueStruct: {
+        name: "",
+        totalScore: 0, //结构总分
+        courseNo: "", //课程
+        courseName: "",
+        coursePropertyId: "", //课程属性
+        coursePropertyName: "", //课程属性名称
+        paperDetailStructs: [],
+        type: "BLUEPRINT", //试卷结构类型
+        genPaperType: "SPECIFIC", //组卷类型
+        examRemark: "",
+      },
+      detailDialog: false,
+      dialogType: "",
+      paperDetailStructForm: {
+        id: "",
+        number: "",
+        name: "", //大题名称
+        questionType: "", //题型
+        detailCount: 0, //小题总数
+        score: 0, //每题分数
+        totalScore: 0, //大题分数
+        quesNames: [], //来源大题
+        publicSimpleCount: 0, //公开简单
+        publicMediumCount: 0, //公开中等
+        publicDifficultyCount: 0, //公开困难
+        noPublicSimpleCount: 0, //非公开简单
+        noPublicMediumCount: 0, //非公开中等
+        noPublicDifficultyCount: 0, //非公开困难
+        courseProperties: [], //课程属性列表
+        remark: "", //备注
+        coursePropertyNumberDtos: [], //
+      },
+      quesTypes: QUESTION_TYPES,
+      paperDetailStructs: [],
+      courseList: [],
+      loading: false,
+      coursePropertyList: [],
+      rules: {
+        name: [{ required: true, message: "请输入名称", trigger: "blur" }],
+        remark: [
+          { required: true, message: "请输入题型描述", trigger: "blur" },
+        ],
+      },
+    };
+  },
+  computed: {
+    totalScore() {
+      var sum = 0.0;
+      for (let paperDetailStruct of this.paperDetailStructs) {
+        sum += paperDetailStruct.score;
+      }
+      return sum;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+    showcoursePropertyId() {
+      if (this.blueStruct.paperDetailStructs.length > 0) {
+        for (let paperDetailStruct of this.blueStruct.paperDetailStructs) {
+          if (paperDetailStruct.detailCount > 0) {
+            return true;
+          }
+        }
+      }
+      return false;
+    },
+  },
+  created() {
+    this.paperStructId = this.$route.params.id;
+    this.searchForm();
+  },
+  methods: {
+    //查询大题结合
+    searchForm() {
+      this.loading = true;
+      var blueStructStorge = sessionStorage.getItem("blueStruct");
+      if (typeof blueStructStorge == "string") {
+        var blueStruct = JSON.parse(blueStructStorge);
+        this.getCourses(blueStruct.courseName);
+        this.blueStruct = blueStruct;
+        this.$http
+          .get(
+            QUESTION_API + "/courseProperty/enable/" + this.blueStruct.courseNo
+          )
+          .then((response) => {
+            this.coursePropertyList = response.data;
+            if (this.coursePropertyList && this.coursePropertyList.length > 0) {
+              for (let objCouPro of this.coursePropertyList) {
+                if (objCouPro.id == this.blueStruct.coursePropertyId) {
+                  this.blueStruct.coursePropertyName = objCouPro.name;
+                  console.log("blueStruct", blueStruct);
+                }
+              }
+            }
+          });
+        this.paperDetailStructs = blueStruct.paperDetailStructs;
+      }
+      this.loading = false;
+    },
+    getProperties() {
+      for (var course of this.courseList) {
+        if (this.blueStruct.courseNo == course.code) {
+          var courseId = course.id;
+          this.$http
+            .get(QUESTION_API + "/courseProperty/all/" + courseId)
+            .then((response) => {
+              this.coursePropertyList = response.data;
+              console.log("this.coursePropertyList:", this.coursePropertyList);
+            });
+        }
+      }
+    },
+    //查询课程下的课程属性
+    getCoursePropertyList() {
+      this.$http
+        .get(
+          QUESTION_API + "/courseProperty/enable/" + this.blueStruct.courseNo
+        )
+        .then((response) => {
+          this.coursePropertyList = response.data;
+          console.log("this.coursePropertyList:", this.coursePropertyList);
+          if (this.coursePropertyList && this.coursePropertyList.length == 0) {
+            this.blueStruct.coursePropertyId = "";
+          }
+        });
+    },
+    checkPaperStruct() {
+      if (!this.blueStruct.name) {
+        this.$notify({
+          message: "请填写试卷结构名称",
+          type: "error",
+        });
+        return false;
+      }
+      let curPropertyId = this.blueStruct.coursePropertyId;
+      if (!curPropertyId || curPropertyId.length < 1) {
+        this.$notify({
+          type: "error",
+          message: "选择课程属性",
+        });
+        return false;
+      }
+      if (
+        !(
+          this.blueStruct.paperDetailStructs &&
+          this.blueStruct.paperDetailStructs.length > 0
+        )
+      ) {
+        this.$notify({
+          message: "请添加大题",
+          type: "error",
+        });
+        return false;
+      }
+      for (let paperDetailStruct of this.blueStruct.paperDetailStructs) {
+        if (!(paperDetailStruct.detailCount > 0)) {
+          this.$notify({
+            message: "请添加大题下的小题数量",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      return true;
+    },
+    //验证总分
+    checkTotalScore() {
+      var totalScore = 0;
+      for (let paperDetailStruct of this.blueStruct.paperDetailStructs) {
+        totalScore = parseFloat(paperDetailStruct.totalScore) + totalScore;
+      }
+      if (totalScore != this.blueStruct.totalScore) {
+        return false;
+      } else {
+        return true;
+      }
+    },
+    //保存试卷结构
+    saveBlueStruct() {
+      if (this.blueStruct.name.trim().length == 0) {
+        this.$notify({
+          type: "error",
+          message: "结构名称不能为空格",
+        });
+        return false;
+      }
+      if (!this.checkPaperStruct()) {
+        return false;
+      }
+      if (!this.checkTotalScore()) {
+        this.$notify({
+          type: "error",
+          message: "试卷结构总分与大题总分不一致",
+        });
+        return false;
+      }
+      this.loading = true;
+      var url = QUESTION_API + "/paperStruct";
+      if (this.paperStructId != "add") {
+        //假如没ID就是新增
+        this.$http
+          .put(url, this.blueStruct)
+          .then(() => {
+            this.$notify({
+              message: "保存成功",
+              type: "success",
+            });
+            this.loading = false;
+            this.removeItem();
+            this.back();
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "试卷结构名称重复,请重新命名",
+            });
+            this.loading = false;
+          });
+      } else {
+        this.$http
+          .post(url, this.blueStruct)
+          .then(() => {
+            this.$notify({
+              message: "保存成功",
+              type: "success",
+            });
+            this.loading = false;
+            this.removeItem();
+            this.back();
+          })
+          .catch(() => {
+            this.loading = false;
+            this.$notify({
+              type: "error",
+              message: "试卷结构名称重复,请重新命名",
+            });
+          });
+      }
+    },
+    //添加大题
+    insertDetail() {
+      this.dialogType = "ADD";
+      this.paperDetailStructForm.name = "";
+      this.paperDetailStructForm.remark = "";
+      this.detailDialog = true;
+    },
+    //保存新增大题
+    saveDetail(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          //定义一个id的集合,用来id自加
+          var ids = [];
+          for (let paperDetailStruct of this.paperDetailStructs) {
+            ids.push(paperDetailStruct.id);
+          }
+          ids.sort();
+          var maxId;
+          if (ids.length == 0) {
+            maxId = 0;
+          } else {
+            maxId = ids[ids.length - 1];
+          }
+          var paperDetailStruct = Object.assign({}, this.paperDetailStructForm);
+          paperDetailStruct.id = maxId + 1;
+          paperDetailStruct.number = this.paperDetailStructs.length + 1;
+          this.paperDetailStructs.push(paperDetailStruct);
+          this.detailDialog = false;
+          this.blueStruct.paperDetailStructs = this.paperDetailStructs;
+          sessionStorage.setItem("blueStruct", JSON.stringify(this.blueStruct));
+        } else {
+          return false;
+        }
+      });
+    },
+    //删除大题
+    deleteDetail(row) {
+      for (var i = 0; i < this.paperDetailStructs.length; i++) {
+        if (row.id === this.paperDetailStructs[i].id) {
+          this.paperDetailStructs.splice(i, 1);
+        }
+      }
+      this.blueStruct.paperDetailStructs = this.paperDetailStructs;
+      sessionStorage.setItem("blueStruct", JSON.stringify(this.blueStruct));
+    },
+    //修改大题
+    editDetail(row) {
+      this.dialogType = "EDIT";
+      for (let paperDetailStruct of this.paperDetailStructs) {
+        if (row.id === paperDetailStruct.id) {
+          this.paperDetailStructForm.name = paperDetailStruct.name;
+          this.paperDetailStructForm.remark = paperDetailStruct.remark;
+          this.paperDetailStructForm.id = row.id;
+        }
+      }
+      this.detailDialog = true;
+    },
+    //保存修改大题
+    saveEditDetail(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          for (let paperDetailStruct of this.paperDetailStructs) {
+            if (this.paperDetailStructForm.id === paperDetailStruct.id) {
+              console.log("paperDetailStruct", paperDetailStruct);
+              paperDetailStruct.name = this.paperDetailStructForm.name;
+              paperDetailStruct.remark = this.paperDetailStructForm.remark;
+            }
+          }
+          this.paperDetailStructForm = { name: "", remark: "", id: "" };
+          this.blueStruct.paperDetailStructs = this.paperDetailStructs;
+          sessionStorage.setItem("blueStruct", JSON.stringify(this.blueStruct));
+          this.detailDialog = false;
+        }
+      });
+    },
+    //返回
+    back() {
+      this.$router.push({
+        path: "/questions/blue_paper_structure/1",
+      });
+    },
+    //关闭窗口
+    closeDialog(formData) {
+      this.resetForm(formData);
+      this.detailDialog = false;
+    },
+    //得到题型
+    getTpye(val) {
+      //console.log(val);
+      for (var ques of this.quesTypes) {
+        if (val === ques.value) {
+          return ques.label;
+        }
+      }
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.blueStruct.courseName = course.name;
+        }
+      }
+    },
+    //大题详情
+    detailInfo(row) {
+      if (!this.blueStruct.coursePropertyId) {
+        this.$notify({
+          type: "error",
+          message: "请选择课程属性",
+        });
+        return false;
+      }
+      this.getCourseName(this.blueStruct.courseNo);
+      sessionStorage.setItem("blueStruct", JSON.stringify(this.blueStruct));
+      this.$router.push({
+        path:
+          "/questions/insert_blue_paper_structure_info/" +
+          row.id +
+          "/" +
+          this.paperStructId,
+      });
+    },
+    //清空
+    removeItem() {
+      sessionStorage.removeItem("blueStruct");
+    },
+    getCoursePropertyName() {
+      if (this.blueStruct.coursePropertyId) {
+        for (let objCouPro of this.coursePropertyList) {
+          if (objCouPro.id == this.blueStruct.coursePropertyId) {
+            this.blueStruct.coursePropertyName = objCouPro.name;
+          }
+        }
+      } else {
+        this.blueStruct.coursePropertyName = "";
+      }
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+          if (this.blueStruct.courseNo) {
+            this.getCoursePropertyList();
+          }
+        });
+    },
+    clearCourseProperty() {
+      this.blueStruct.coursePropertyId = "";
+      this.coursePropertyList = [];
+      this.getCoursePropertyList();
+    },
+    resetForm(formData) {
+      this.paperDetailStructForm.name = "";
+      this.paperDetailStructForm.remark = "";
+      this.$refs[formData].clearValidate();
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 829 - 0
src/modules/questions/views/InsertBluePaperStructureInfo.vue

@@ -0,0 +1,829 @@
+<template>
+  <section class="content">
+    <LinkTitlesCustom :current-paths="['大题详情']" />
+    <div class="box box-info">
+      <!-- 正文信息 -->
+      <div class="box-body">
+        <el-form
+          :inline="true"
+          :model="paperDetailStruct"
+          label-position="right"
+          label-width="70px"
+        >
+          <el-row>
+            <el-col :span="6">
+              <el-form-item label="题型结构">
+                <el-select
+                  v-model="paperDetailStruct.questionType"
+                  class="search_width"
+                  placeholder="请选择"
+                  size="small"
+                  @change="getQuesNameList"
+                >
+                  <el-option
+                    v-for="item in quesTypes"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="题目数量" class="pull-left">
+                <el-input
+                  v-model="count"
+                  class="search_width"
+                  disabled
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="每题分值" class="pull-left">
+                <el-input
+                  v-model="paperDetailStruct.score"
+                  class="search_width"
+                  placeholder="请输入"
+                  size="small"
+                  @change="muli(paperDetailStruct)"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="6">
+              <el-form-item label="来源大题" class="pull-left">
+                <el-select
+                  v-model="paperDetailStruct.quesNames"
+                  class="search_width"
+                  multiple
+                  placeholder="请选择"
+                  size="small"
+                >
+                  <el-option
+                    v-for="item in quesNameList"
+                    :key="item.code"
+                    :label="item.name"
+                    :value="item.code"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="题型总分" class="pull-left">
+                <el-input
+                  v-model="paperDetailStruct.totalScore"
+                  class="search_width"
+                  disabled
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <div class="search_down">
+                <el-button size="small" type="primary" @click="saveForm"
+                  >保 存</el-button
+                >
+                <el-button size="small" type="primary" @click="back"
+                  ><i class="el-icon-arrow-left"></i> 返 回</el-button
+                >
+              </div>
+            </el-col>
+          </el-row>
+        </el-form>
+
+        <!-- 页面列表 -->
+        <el-table
+          :data="coursePropertyNumberDtos"
+          border
+          :row-class-name="tableRowClassName"
+        >
+          <el-table-column min-width="300" label="属性名称">
+            <template slot-scope="scope">
+              <div class="el_table_inner_left">
+                <p
+                  class="class-a"
+                  :class="{ 'class-b': scope.row.propertyParentId != '0' }"
+                >
+                  {{ scope.row.propertyName }}
+                </p>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="数量">
+            <template slot-scope="scope">
+              <div class="el_table_inner_left">
+                <el-input
+                  v-model="scope.row.number"
+                  class="input_width_ss"
+                  disabled
+                ></el-input>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="公开(简单)">
+            <template slot-scope="scope">
+              <div
+                class="el_table_inner_left"
+                @mouseenter="showActiveIn(1, 1, scope.row)"
+                @mouseleave="showActiveOff"
+              >
+                <el-tooltip placement="top">
+                  <div slot="content">{{ message }}</div>
+                  <el-input
+                    v-model="scope.row.publicSimple"
+                    class="input_width_ss"
+                    :disabled="scope.row.disable"
+                    @change="
+                      sum(scope.row, scope.row.publicSimple, 'publicSimple')
+                    "
+                  ></el-input>
+                </el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="公开(中等)">
+            <template slot-scope="scope">
+              <div
+                class="el_table_inner_left"
+                @mouseenter="showActiveIn(1, 2, scope.row)"
+                @mouseleave="showActiveOff"
+              >
+                <el-tooltip placement="top">
+                  <div slot="content">{{ message }}</div>
+                  <el-input
+                    v-model="scope.row.publicMedium"
+                    class="input_width_ss"
+                    :disabled="scope.row.disable"
+                    @change="
+                      sum(scope.row, scope.row.publicMedium, 'publicMedium')
+                    "
+                  ></el-input>
+                </el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="公开(困难)">
+            <template slot-scope="scope">
+              <div
+                class="el_table_inner_left"
+                @mouseenter="showActiveIn(1, 3, scope.row)"
+                @mouseleave="showActiveOff"
+              >
+                <el-tooltip placement="top">
+                  <div slot="content">{{ message }}</div>
+                  <el-input
+                    v-model="scope.row.publicDifficulty"
+                    class="input_width_ss"
+                    :disabled="scope.row.disable"
+                    @change="
+                      sum(
+                        scope.row,
+                        scope.row.publicDifficulty,
+                        'publicDifficulty'
+                      )
+                    "
+                  ></el-input>
+                </el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="非公开(简单)">
+            <template slot-scope="scope">
+              <div
+                class="el_table_inner_left"
+                @mouseenter="showActiveIn(0, 1, scope.row)"
+                @mouseleave="showActiveOff"
+              >
+                <el-tooltip placement="top">
+                  <div slot="content">{{ message }}</div>
+                  <el-input
+                    v-model="scope.row.noPublicSimple"
+                    class="input_width_ss"
+                    :disabled="scope.row.disable"
+                    @change="
+                      sum(scope.row, scope.row.noPublicSimple, 'noPublicSimple')
+                    "
+                  ></el-input>
+                </el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="非公开(中等)">
+            <template slot-scope="scope">
+              <div
+                class="el_table_inner_left"
+                @mouseenter="showActiveIn(0, 2, scope.row)"
+                @mouseleave="showActiveOff"
+              >
+                <el-tooltip placement="top">
+                  <div slot="content">{{ message }}</div>
+                  <el-input
+                    v-model="scope.row.noPublicMedium"
+                    class="input_width_ss"
+                    :disabled="scope.row.disable"
+                    @change="
+                      sum(scope.row, scope.row.noPublicMedium, 'noPublicMedium')
+                    "
+                  ></el-input>
+                </el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="非公开(困难)">
+            <template slot-scope="scope">
+              <div
+                class="el_table_inner_left"
+                @mouseenter="showActiveIn(0, 3, scope.row)"
+                @mouseleave="showActiveOff"
+              >
+                <el-tooltip placement="top">
+                  <div slot="content">{{ message }}</div>
+                  <el-input
+                    v-model="scope.row.noPublicDifficulty"
+                    class="input_width_ss"
+                    :disabled="scope.row.disable"
+                    @change="
+                      sum(
+                        scope.row,
+                        scope.row.noPublicDifficulty,
+                        'noPublicDifficulty'
+                      )
+                    "
+                  ></el-input>
+                </el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <div
+          style="margin-top: 20px; padding-top: 20px"
+          class="grid-content bg-purple-light"
+        >
+          <el-form label-width="80px">
+            <el-row>
+              <el-col :span="6">
+                <el-form-item label="公开"> </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="简单总数">
+                  <el-input
+                    v-model="publicSimpleCount"
+                    class="input_width_ss"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="中等总数">
+                  <el-input
+                    v-model="publicMediumCount"
+                    class="input_width_ss"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="困难总数">
+                  <el-input
+                    v-model="publicDifficultyCount"
+                    class="input_width_ss"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row>
+              <el-col :span="6">
+                <el-form-item label="非公开"> </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="简单总数">
+                  <el-input
+                    v-model="noPublicSimpleCount"
+                    class="input_width_ss"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="中等总数">
+                  <el-input
+                    v-model="noPublicMediumCount"
+                    class="input_width_ss"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="困难总数">
+                  <el-input
+                    v-model="noPublicDifficultyCount"
+                    class="input_width_ss"
+                    disabled
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </div>
+      </div>
+    </div>
+  </section>
+</template>
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { QUESTION_TYPES } from "../constants/constants";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: {
+    LinkTitlesCustom,
+  },
+  data() {
+    return {
+      paperDetailStruct: {
+        id: "",
+        number: "",
+        name: "", //大题名称
+        questionType: "", //题型
+        detailCount: 0, //小题总数
+        score: 0, //每题分数
+        totalScore: 0, //大题分数
+        quesNames: [], //来源大题
+        publicSimpleCount: 0, //公开简单总数
+        publicMediumCount: 0, //公开中等总数
+        publicDifficultyCount: 0, //公开困难总数
+        noPublicSimpleCount: 0, //非公开简单总数
+        noPublicMediumCount: 0, //非公开中等总数
+        noPublicDifficultyCount: 0, //非公开困难总数
+        coursePropertyNumberDtos: [], //课程属性列表
+        remark: "", //备注
+      },
+      paperDetailStructId: "",
+      quesTypes: QUESTION_TYPES,
+      coursePropertyNumberDtos: [],
+      properties: [],
+      loading: false,
+      quesNameList: [],
+      blueStruct: "",
+      disable: false,
+      count: 0,
+      publicSimpleCount: 0,
+      publicMediumCount: 0,
+      publicDifficultyCount: 0,
+      noPublicSimpleCount: 0,
+      noPublicMediumCount: 0,
+      noPublicDifficultyCount: 0,
+      paperStructId: "",
+      message: "",
+      question: {
+        questionType: "",
+        courseNo: "",
+        coursePropertyName: "",
+        publicity: "",
+        firstPropertyId: "",
+        secondPropertyId: "",
+      },
+    };
+  },
+  computed: {},
+  watch: {
+    count() {
+      this.paperDetailStruct.totalScore =
+        (this.paperDetailStruct.score * 1000 * this.count) / 1000;
+    },
+  },
+  //初始化查询
+  created() {
+    this.paperStructId = this.$route.params.paperStructId;
+    this.paperDetailStructId = this.$route.params.id;
+    this.searchPaperDetail();
+  },
+  methods: {
+    tableRowClassName(row) {
+      if (row.disable) {
+        return "positive-row";
+      }
+      return "";
+    },
+    //查询课程下的所有属性
+    searchPaperDetail() {
+      //this.loading = true;
+      var bluePaperStructStorge = sessionStorage.getItem("blueStruct");
+      if (typeof bluePaperStructStorge === "string") {
+        var blueStruct = JSON.parse(bluePaperStructStorge);
+        this.blueStruct = blueStruct;
+        var paperDetailStructs = blueStruct.paperDetailStructs;
+        for (let paperDetailStruct of paperDetailStructs) {
+          if (paperDetailStruct.id == this.paperDetailStructId) {
+            this.paperDetailStruct = paperDetailStruct;
+            //新增,做初始化查询
+            if (this.paperDetailStruct.coursePropertyNumberDtos.length == 0) {
+              //查询课程下的所有属性
+              this.$http
+                .get(QUESTION_API + "/property/" + blueStruct.coursePropertyId)
+                .then((response) => {
+                  this.properties = response.data;
+                  this.init();
+                });
+            } else {
+              //修改
+              this.coursePropertyNumberDtos = this.paperDetailStruct.coursePropertyNumberDtos;
+              this.count = paperDetailStruct.detailCount;
+              this.publicSimpleCount = paperDetailStruct.publicSimpleCount;
+              this.publicMediumCount = paperDetailStruct.publicMediumCount;
+              this.publicDifficultyCount =
+                paperDetailStruct.publicDifficultyCount;
+              this.noPublicSimpleCount = paperDetailStruct.noPublicSimpleCount;
+              this.noPublicMediumCount = paperDetailStruct.noPublicMediumCount;
+              this.noPublicDifficultyCount =
+                paperDetailStruct.noPublicDifficultyCount;
+              this.initQuesNameList();
+            }
+          }
+        }
+      }
+    },
+    //初始化方法
+    init() {
+      var i = 1;
+      for (let property of this.properties) {
+        var coursePropertyNumber = {
+          id: i,
+          propertyId: property.id,
+          propertyParentId: property.parentId,
+          propertyName: property.name,
+          number: 0,
+          publicSimple: 0,
+          publicMedium: 0,
+          publicDifficulty: 0,
+          noPublicSimple: 0,
+          noPublicMedium: 0,
+          noPublicDifficulty: 0,
+          disable: false,
+        };
+        //判断父节点
+        if (property.parentId == "0") {
+          //判断父节点下面有没有  子节点
+          for (let propertySon of this.properties) {
+            //如果有子节点,就不能输入
+            if (propertySon.parentId == property.id) {
+              this.disable = true;
+            }
+          }
+          coursePropertyNumber.disable = this.disable;
+        }
+        this.coursePropertyNumberDtos.push(coursePropertyNumber);
+        this.disable = false;
+        i = i + 1;
+      }
+    },
+    //初始化来源大题名称
+    initQuesNameList() {
+      var url =
+        QUESTION_API +
+        "/paperStruct/quesNames?quesType=" +
+        this.paperDetailStruct.questionType +
+        "&courseNo=" +
+        this.blueStruct.courseNo;
+      this.$http.get(url).then((response) => {
+        this.quesNameList = response.data;
+      });
+    },
+    //查询来源大题名称
+    getQuesNameList(quesType) {
+      if (quesType && quesType.length > 0) {
+        var url =
+          QUESTION_API +
+          "/paperStruct/quesNames?quesType=" +
+          quesType +
+          "&courseNo=" +
+          this.blueStruct.courseNo;
+        this.$http.get(url).then((response) => {
+          this.quesNameList = response.data;
+        });
+      }
+      this.paperDetailStruct.quesNames = [];
+      //需要记录题型,查询题目数量
+    },
+    //判断是否为正整数
+    isPositiveInteger(s) {
+      //是否为正整数
+      var re = /^[0-9]+$/;
+      return re.test(s);
+    },
+    //计算数量
+    sum(row, data, rowType) {
+      //计算前校验正整数
+      if (!this.isPositiveInteger(data)) {
+        if (rowType === "publicSimple") {
+          setTimeout(function () {
+            row.publicSimple = 0;
+          }, 1);
+        } else if (rowType === "publicMedium") {
+          setTimeout(function () {
+            row.publicMedium = 0;
+          }, 1);
+        } else if (rowType === "publicDifficulty") {
+          setTimeout(function () {
+            row.publicDifficulty = 0;
+          }, 1);
+        } else if (rowType === "noPublicSimple") {
+          setTimeout(function () {
+            row.noPublicSimple = 0;
+          }, 1);
+        } else if (rowType === "noPublicMedium") {
+          setTimeout(function () {
+            row.noPublicMedium = 0;
+          }, 1);
+        } else if (rowType === "noPublicDifficulty") {
+          setTimeout(function () {
+            row.noPublicDifficulty = 0;
+          }, 1);
+        }
+      }
+      var _this = this;
+      setTimeout(function () {
+        //每行总和计算
+        console.log("row:", row);
+        row.publicSimple = parseInt(row.publicSimple);
+        row.publicMedium = parseInt(row.publicMedium);
+        row.publicDifficulty = parseInt(row.publicDifficulty);
+        row.noPublicSimple = parseInt(row.noPublicSimple);
+        row.noPublicMedium = parseInt(row.noPublicMedium);
+        row.noPublicDifficulty = parseInt(row.noPublicDifficulty);
+        var number = 0;
+        row.number =
+          parseInt(row.publicSimple) +
+          parseInt(row.publicMedium) +
+          parseInt(row.publicDifficulty) +
+          parseInt(row.noPublicSimple) +
+          parseInt(row.noPublicMedium) +
+          parseInt(row.noPublicDifficulty);
+        //父节点总和计算
+        if (row.propertyParentId != "0") {
+          var publicSimple = 0;
+          var publicMedium = 0;
+          var publicDifficulty = 0;
+          var noPublicSimple = 0;
+          var noPublicMedium = 0;
+          var noPublicDifficulty = 0;
+          //查询到父节点this
+          var coursePropertyNumber = _this.getCoursePropertyNumber(
+            row.propertyParentId
+          );
+          //查询所有子节点
+          var coursePropertyNumberSons = _this.getCoursePropertyNumberSons(
+            coursePropertyNumber.propertyId
+          );
+          for (let coursePropertyNumberSon of coursePropertyNumberSons) {
+            publicSimple =
+              parseInt(coursePropertyNumberSon.publicSimple) + publicSimple;
+            publicMedium =
+              parseInt(coursePropertyNumberSon.publicMedium) + publicMedium;
+            publicDifficulty =
+              parseInt(coursePropertyNumberSon.publicDifficulty) +
+              publicDifficulty;
+            noPublicSimple =
+              parseInt(coursePropertyNumberSon.noPublicSimple) + noPublicSimple;
+            noPublicMedium =
+              parseInt(coursePropertyNumberSon.noPublicMedium) + noPublicMedium;
+            noPublicDifficulty =
+              parseInt(coursePropertyNumberSon.noPublicDifficulty) +
+              noPublicDifficulty;
+          }
+          coursePropertyNumber.publicSimple = publicSimple;
+          coursePropertyNumber.publicMedium = publicMedium;
+          coursePropertyNumber.publicDifficulty = publicDifficulty;
+          coursePropertyNumber.noPublicSimple = noPublicSimple;
+          coursePropertyNumber.noPublicMedium = noPublicMedium;
+          coursePropertyNumber.noPublicDifficulty = noPublicDifficulty;
+          coursePropertyNumber.number =
+            coursePropertyNumber.publicSimple +
+            coursePropertyNumber.publicMedium +
+            coursePropertyNumber.publicDifficulty +
+            coursePropertyNumber.noPublicSimple +
+            coursePropertyNumber.noPublicMedium +
+            coursePropertyNumber.noPublicDifficulty;
+        }
+        //计算所有公开简单数量
+        var publicSimpleCount = 0;
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          if (coursePropertyNumber.disable != true) {
+            publicSimpleCount =
+              parseInt(coursePropertyNumber.publicSimple) + publicSimpleCount;
+          }
+        }
+        _this.publicSimpleCount = publicSimpleCount;
+        //计算所有公开中等数量
+        var publicMediumCount = 0;
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          if (coursePropertyNumber.disable != true) {
+            publicMediumCount =
+              parseInt(coursePropertyNumber.publicMedium) + publicMediumCount;
+          }
+        }
+        _this.publicMediumCount = publicMediumCount;
+        //计算所有公开困难数量
+        var publicDifficultyCount = 0;
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          if (coursePropertyNumber.disable != true) {
+            publicDifficultyCount =
+              parseInt(coursePropertyNumber.publicDifficulty) +
+              publicDifficultyCount;
+          }
+        }
+        _this.publicDifficultyCount = publicDifficultyCount;
+        //计算所有非公开简单数量
+        var noPublicSimpleCount = 0;
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          if (coursePropertyNumber.disable != true) {
+            noPublicSimpleCount =
+              parseInt(coursePropertyNumber.noPublicSimple) +
+              noPublicSimpleCount;
+          }
+        }
+        _this.noPublicSimpleCount = noPublicSimpleCount;
+        //计算所有非公开等单数量
+        var noPublicMediumCount = 0;
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          if (coursePropertyNumber.disable != true) {
+            noPublicMediumCount =
+              parseInt(coursePropertyNumber.noPublicMedium) +
+              noPublicMediumCount;
+          }
+        }
+        _this.noPublicMediumCount = noPublicMediumCount;
+        //计算所有非公开困难数量
+        var noPublicDifficultyCount = 0;
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          if (coursePropertyNumber.disable != true) {
+            noPublicDifficultyCount =
+              parseInt(coursePropertyNumber.noPublicDifficulty) +
+              noPublicDifficultyCount;
+          }
+        }
+        _this.noPublicDifficultyCount = noPublicDifficultyCount;
+        //计算总数
+        for (let coursePropertyNumber of _this.coursePropertyNumberDtos) {
+          //父节点相加即可
+          if (coursePropertyNumber.propertyParentId == "0") {
+            number = parseInt(coursePropertyNumber.number) + number;
+          }
+        }
+        _this.count = number;
+      }, 5);
+    },
+    //根据propertyId 取到对应的 coursePropertyNumber
+    getCoursePropertyNumber(propertyParentId) {
+      for (let coursePropertyNumber of this.coursePropertyNumberDtos) {
+        if (propertyParentId == coursePropertyNumber.propertyId) {
+          return coursePropertyNumber;
+        }
+      }
+    },
+    //根据父节点,找到子节点集合
+    getCoursePropertyNumberSons(propertyId) {
+      var coursePropertyNumberSons = [];
+      for (let coursePropertyNumber of this.coursePropertyNumberDtos) {
+        if (propertyId == coursePropertyNumber.propertyParentId) {
+          coursePropertyNumberSons.push(coursePropertyNumber);
+        }
+      }
+      return coursePropertyNumberSons;
+    },
+    //计算总分
+    muli(paperDetailStruct) {
+      if (isNaN(paperDetailStruct.score) || paperDetailStruct.score < 0) {
+        setTimeout(function () {
+          paperDetailStruct.score = 0;
+        }, 1);
+        this.paperDetailStruct.totalScore = 0;
+      } else {
+        this.paperDetailStruct.totalScore =
+          (this.paperDetailStruct.score * 1000 * this.count) / 1000;
+      }
+    },
+    //保存
+    saveForm() {
+      if (!this.checkSave()) {
+        return false;
+      }
+      this.paperDetailStruct.detailCount = this.count;
+      this.paperDetailStruct.id = this.paperDetailStructId;
+      this.paperDetailStruct.coursePropertyNumberDtos = this.coursePropertyNumberDtos;
+      this.paperDetailStruct.publicSimpleCount = this.publicSimpleCount;
+      this.paperDetailStruct.publicMediumCount = this.publicMediumCount;
+      this.paperDetailStruct.publicDifficultyCount = this.publicDifficultyCount;
+      this.paperDetailStruct.noPublicSimpleCount = this.noPublicSimpleCount;
+      this.paperDetailStruct.noPublicMediumCount = this.noPublicMediumCount;
+      this.paperDetailStruct.noPublicDifficultyCount = this.noPublicDifficultyCount;
+      for (let paperDetailStruct of this.blueStruct.paperDetailStructs) {
+        if (this.paperDetailStruct.id == paperDetailStruct.id) {
+          paperDetailStruct = this.paperDetailStruct;
+        }
+      }
+      //保存蓝图试卷结构
+      sessionStorage.setItem("blueStruct", JSON.stringify(this.blueStruct));
+      this.back();
+    },
+    //返回
+    back() {
+      this.$router.push({
+        path: "/questions/insert_blue_paper_structure/" + this.paperStructId,
+      });
+    },
+    //验证
+    checkSave() {
+      if (!this.paperDetailStruct.questionType) {
+        this.$notify({
+          type: "error",
+          message: "请选择题型结构",
+        });
+        return false;
+      }
+      if (this.paperDetailStruct.quesNames.length < 1) {
+        this.$notify({
+          type: "error",
+          message: "请选择来源大题",
+        });
+        return false;
+      }
+      if (this.paperDetailStruct.score == 0) {
+        this.$notify({
+          type: "error",
+          message: "分数不能为0",
+        });
+        return false;
+      }
+      if (this.count < 1) {
+        this.$notify({
+          type: "error",
+          message: "题目数量不能为0",
+        });
+        return false;
+      }
+      return true;
+    },
+    //鼠标悬浮事件
+    showActiveIn(publicity, difficulty, row) {
+      this.question.questionType = this.paperDetailStruct.questionType;
+      this.question.courseNo = this.blueStruct.courseNo;
+      this.question.coursePropertyName = this.blueStruct.coursePropertyName;
+      this.question.quesNames = this.paperDetailStruct.quesNames.join(",");
+      console.log("this.question:", this.question);
+      if (publicity == 0) {
+        this.question.publicity = false;
+      } else {
+        this.question.publicity = true;
+      }
+      if (difficulty == 1) {
+        this.question.difficulty = "易";
+      } else if (difficulty == 2) {
+        this.question.difficulty = "中";
+      } else {
+        this.question.difficulty = "难";
+      }
+      if (row.propertyParentId != "0") {
+        this.question.firstPropertyId = row.propertyParentId;
+        this.question.secondPropertyId = row.propertyId;
+      } else {
+        this.question.firstPropertyId = row.propertyId;
+        this.question.secondPropertyId = "";
+      }
+      //参数序列化
+      this.$http
+        .get(QUESTION_API + "/question/count", { params: this.question })
+        .then((response) => {
+          this.message = response.data;
+          console.log("this.message:", this.message);
+        });
+    },
+    //鼠标离开事件
+    showActiveOff() {
+      this.message = "";
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>
+<style scoped>
+.bg-purple-light {
+  background: #e5e9d0;
+}
+.class-a {
+  white-space: nowrap;
+  margin-left: 5px;
+  text-align: left;
+}
+.class-b {
+  margin-left: 30px;
+}
+.el-table .positive-row {
+  background: #e2f0e4;
+}
+</style>

+ 566 - 0
src/modules/questions/views/InsertPaperStructure.vue

@@ -0,0 +1,566 @@
+<template>
+  <div id="insertApp">
+    <LinkTitlesCustom
+      :current-paths="['基础信息 ', '精确结构预设', '精确结构创建']"
+    />
+    <section class="content">
+      <!-- 正文信息 -->
+      <div class="box-body">
+        <el-form
+          ref="paperStruct"
+          :inline="true"
+          :model="paperStruct"
+          :rules="rules2"
+          label-position="right"
+          label-width="78px"
+        >
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="结构名称" prop="name">
+                <el-input
+                  v-model="paperStruct.name"
+                  class="search_width"
+                  placeholder="试卷结构名称"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="总分" prop="score">
+                <el-input
+                  v-model.number="paperStruct.totalScore"
+                  class="search_width"
+                  placeholder="请输入总分"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="制定课程">
+                <el-select
+                  v-model="paperStruct.courseNo"
+                  class="search_width"
+                  filterable
+                  :remote-method="getCourses"
+                  remote
+                  clearable
+                  placeholder="请选择"
+                  size="small"
+                >
+                  <el-option key="ALL" label="请选择" value="ALL"></el-option>
+                  <el-option key="" label="公用" value=""></el-option>
+                  <el-option
+                    v-for="item in courseInfoSelect"
+                    :key="item.courseNo"
+                    :label="item.courseInfo"
+                    :value="item.courseNo"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <div style="height: 20px"></div>
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="考试说明" class="pull-left">
+                <ckeditor
+                  v-model="paperStruct.examRemark"
+                  :display="display"
+                  :width="wValue"
+                  :height="hValue"
+                ></ckeditor>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+
+        <!-- 添加大题弹出框 -->
+        <el-dialog
+          title="大题信息"
+          :visible.sync="paperDetailStructDialog"
+          width="500px"
+        >
+          <el-form
+            ref="paperDetailStructForm"
+            :model="paperDetailStructForm"
+            :rules="rules"
+            label-position="right"
+            label-width="120px"
+            inline-message
+          >
+            <el-row>
+              <el-form-item label="大题名称" prop="name">
+                <el-input
+                  v-model="paperDetailStructForm.name"
+                  class="dialog_input_width"
+                  placeholder="请输入题型名称"
+                ></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row>
+              <el-form-item label="题型描述" prop="remark">
+                <el-input
+                  v-model="paperDetailStructForm.remark"
+                  class="dialog_input_width"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="请输入题型描述"
+                ></el-input>
+              </el-form-item>
+            </el-row>
+            <el-row class="margin_top_10 margin_left_120">
+              <span v-if="dialogType == 'ADD'">
+                <el-button
+                  type="primary"
+                  @click="submitForm('paperDetailStructForm')"
+                  >保 存</el-button
+                >
+              </span>
+              <span v-else>
+                <el-button
+                  type="primary"
+                  @click="submitEditForm('paperDetailStructForm')"
+                  >保 存</el-button
+                >
+              </span>
+              <span class="margin_left_10">
+                <el-button @click="resetForm('paperDetailStructForm')"
+                  ><i class="el-icon-refresh"></i> 重 置</el-button
+                >
+              </span>
+              <span class="margin_left_10">
+                <el-button
+                  type="primary"
+                  @click="back2('paperDetailStructForm')"
+                  ><i class="el-icon-arrow-left"></i> 返 回</el-button
+                >
+              </span>
+            </el-row>
+          </el-form>
+        </el-dialog>
+
+        <!-- 页面列表 -->
+        <div class="margin_top_10"></div>
+        <el-table :data="paperDetailStructs" border style="width: 88.5%">
+          <el-table-column label="大题名称">
+            <template slot-scope="scope">
+              <span>{{ scope.row.name }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="小题数">
+            <template slot-scope="scope">
+              <span>{{ scope.row.detailCount }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="大题分数">
+            <template slot-scope="scope">
+              <span>{{ scope.row.totalScore }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="292">
+            <template slot-scope="scope">
+              <div class="operate_left">
+                <el-button
+                  size="mini"
+                  type="primary"
+                  @click="insertTopicStruct(scope.row)"
+                  ><i class="el-icon-plus"></i> 添加小题</el-button
+                >
+                <el-button
+                  size="mini"
+                  type="primary"
+                  @click="editPaperDetail(scope.row.id)"
+                  ><i class="el-icon-edit"></i>编辑
+                </el-button>
+                <el-button
+                  size="mini"
+                  type="danger"
+                  @click="deletePaperDetail(scope.row.id)"
+                  ><i class="el-icon-delete"></i> 删除</el-button
+                >
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="search_down margin_top_10">
+        <el-button
+          size="small"
+          type="primary"
+          :disabled="button_disabled"
+          @click="savePaperStruct()"
+          >保存试卷结构</el-button
+        >
+        <el-button
+          size="small"
+          type="primary"
+          :disabled="button_disabled"
+          @click="insert()"
+          ><i class="el-icon-plus"></i> 新增大题</el-button
+        >
+        <el-button size="small" type="primary" @click="back"
+          ><i class="el-icon-arrow-left"></i> 返 回</el-button
+        >
+      </div>
+    </section>
+  </div>
+</template>
+
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import ckeditor from "../component/ckeditor.vue";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  name: "InsertApp",
+  components: {
+    ckeditor,
+    LinkTitlesCustom,
+  },
+
+  data() {
+    return {
+      hValue: "100px",
+      wValue: "800px",
+      display: "block",
+      loading: false,
+      courseList: [],
+      paperDetailStructForm: {
+        id: "",
+        number: "",
+        name: "",
+        remark: "",
+        totalScore: "",
+        detailCount: "",
+        unitStructs: [],
+      },
+      paperStruct: {
+        name: "",
+        totalScore: "",
+        paperDetailStructs: [],
+        courseNo: "ALL",
+        courseName: "",
+        type: "EXACT", //试卷结构类型
+        genPaperType: "COMMON", //组卷类型
+        examRemark: "",
+      },
+      paperDetailStructs: [],
+      paperDeatilId: "",
+      rules: {
+        name: [{ required: true, message: "请输入名称", trigger: "blur" }],
+        remark: [
+          { required: true, message: "请输入题型描述", trigger: "blur" },
+        ],
+      },
+      rules2: {
+        name: [
+          { required: true, message: "请输入试卷结构名称", trigger: "blur" },
+        ],
+      },
+      paperStructId: "",
+      paperDetailStructDialog: false,
+      dialogType: "",
+    };
+  },
+  computed: {
+    button_disabled: function () {
+      //var reg = /^[-\+]?\d+(\.\d+)?$/; //正小数
+      var reg = /^\d+(?=\.{0,1}\d+$|$)/;
+      if (
+        reg.test(this.paperStruct.totalScore) &&
+        this.paperStruct.totalScore > 0 &&
+        this.paperStruct.name
+      ) {
+        return false;
+      } else {
+        return true;
+      }
+    },
+    totalScore() {
+      var sum = 0.0;
+      for (let paperDetailStruct of this.paperDetailStructs) {
+        sum += paperDetailStruct.totalScore;
+      }
+      return sum;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+  },
+  created() {
+    this.paperStructId = this.$route.params.id;
+    this.searchForm();
+  },
+
+  methods: {
+    //查询列表集合
+    searchForm() {
+      this.loading = true;
+      var paperStructStorge = sessionStorage.getItem("paperStruct");
+      if (typeof paperStructStorge == "string") {
+        var paperStruct = JSON.parse(paperStructStorge);
+        this.getCourses(paperStruct.courseName);
+        this.paperStruct = paperStruct;
+        this.paperDetailStructs = paperStruct.paperDetailStructs;
+      }
+      this.loading = false;
+    },
+    //保存
+    savePaperStruct() {
+      if (this.paperStruct.name.trim().length == 0) {
+        this.$notify({
+          message: "结构名称不能为空格",
+          type: "error",
+        });
+        return false;
+      }
+      if (!this.checkPaperStruct()) {
+        return false;
+      }
+      if (this.paperStruct.courseNo == "ALL") {
+        this.$notify({
+          message: "请选择课程",
+          type: "error",
+        });
+        return false;
+      }
+      this.loading = true;
+      var url = QUESTION_API + "/paperStruct";
+      console.log(this.paperStructId);
+      if (this.paperStructId != "add") {
+        //假如没ID就是新增
+        this.$http
+          .put(url, this.paperStruct)
+          .then(() => {
+            this.$notify({
+              message: "保存成功",
+              type: "success",
+            });
+            this.loading = false;
+            this.removeItem();
+            this.back();
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "试卷结构名称重复,请重新命名",
+            });
+
+            this.loading = false;
+          });
+      } else {
+        this.$http
+          .post(url, this.paperStruct)
+          .then(() => {
+            this.$notify({
+              message: "保存成功",
+              type: "success",
+            });
+            this.loading = false;
+            this.removeItem();
+            this.back();
+          })
+          .catch(() => {
+            this.loading = false;
+            this.$notify({
+              type: "error",
+              message: "试卷结构名称重复,请重新命名",
+            });
+          });
+      }
+    },
+    checkPaperStruct() {
+      if (
+        !(
+          this.paperStruct.paperDetailStructs &&
+          this.paperStruct.paperDetailStructs.length > 0
+        )
+      ) {
+        this.$notify({
+          message: "请添加大题",
+          type: "error",
+        });
+        return false;
+      }
+      for (let paperDetailStruct of this.paperStruct.paperDetailStructs) {
+        if (
+          !(
+            paperDetailStruct.unitStructs &&
+            paperDetailStruct.unitStructs.length > 0
+          )
+        ) {
+          this.$notify({
+            message: "请添加大题下的题型",
+            type: "error",
+          });
+          return false;
+        }
+      }
+      if (this.paperStruct.totalScore !== this.totalScore) {
+        this.$notify({
+          message: "大题总分与结构满分不一致",
+          type: "error",
+        });
+        return false;
+      }
+      return true;
+    },
+    //保存大题
+    submitForm(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          var ids = [];
+          for (let paperDetailStruct of this.paperDetailStructs) {
+            ids.push(paperDetailStruct.id);
+          }
+          ids.sort();
+          var maxId;
+          if (ids.length == 0) {
+            maxId = 1;
+          } else {
+            maxId = ids[ids.length - 1];
+          }
+          var paperDetailStruct = Object.assign({}, this.paperDetailStructForm);
+          paperDetailStruct.id = maxId + 1;
+          paperDetailStruct.number = this.paperDetailStructs.length + 1;
+          this.paperDetailStructs.push(paperDetailStruct);
+          this.paperDetailStructDialog = false;
+          this.paperDetailStructForm = { name: "", remark: "" };
+          this.paperStruct.paperDetailStructs = this.paperDetailStructs;
+          sessionStorage.setItem(
+            "paperStruct",
+            JSON.stringify(this.paperStruct)
+          );
+        } else {
+          return false;
+        }
+      });
+    },
+    submitEditForm(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          var paperDetailStructs = this.paperDetailStructs;
+          for (let i = paperDetailStructs.length - 1; i >= 0; i--) {
+            if (paperDetailStructs[i].id == this.paperDeatilId) {
+              paperDetailStructs[i].remark = this.paperDetailStructForm.remark;
+              paperDetailStructs[i].name = this.paperDetailStructForm.name;
+            }
+          }
+          this.paperDetailStructDialog = false;
+          this.paperDetailStructForm = { name: "", remark: "" };
+          this.paperStruct.paperDetailStructs = paperDetailStructs;
+          sessionStorage.setItem(
+            "paperStruct",
+            JSON.stringify(this.paperStruct)
+          );
+        } else {
+          return false;
+        }
+      });
+    },
+    deletePaperDetail(id) {
+      var paperDetailStructs = this.paperDetailStructs;
+      for (let i = paperDetailStructs.length - 1; i >= 0; i--) {
+        if (paperDetailStructs[i].id == id) {
+          paperDetailStructs.splice(i, 1);
+        }
+      }
+      this.paperStruct.paperDetailStructs = paperDetailStructs;
+      sessionStorage.setItem("paperStruct", JSON.stringify(this.paperStruct));
+      console.log(sessionStorage.getItem("paperStruct"));
+    },
+    setPaperStruct() {
+      var paperStruct = sessionStorage.getItem("paperStruct");
+      this.paperStruct = JSON.parse(paperStruct);
+    },
+    removeItem() {
+      sessionStorage.removeItem("paperStruct");
+    },
+    //重置
+    resetForm(formData) {
+      this.paperDetailStructForm.remark = "";
+      this.paperDetailStructForm.name = "";
+      this.$refs[formData].clearValidate();
+    },
+
+    //添加大题型
+    insert() {
+      this.dialogType = "ADD";
+      this.paperDetailStructForm.remark = "";
+      this.paperDetailStructForm.name = "";
+      this.paperDetailStructDialog = true;
+    },
+    editPaperDetail(paperDeatilId) {
+      this.dialogType = "EDIT";
+      this.paperDeatilId = paperDeatilId;
+      var paperDetailStructs = this.paperDetailStructs;
+      for (let i = paperDetailStructs.length - 1; i >= 0; i--) {
+        if (paperDetailStructs[i].id == this.paperDeatilId) {
+          this.paperDetailStructForm.remark = paperDetailStructs[i].remark;
+          this.paperDetailStructForm.name = paperDetailStructs[i].name;
+        }
+      }
+      this.paperDetailStructDialog = true;
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.paperStruct.courseName = course.name;
+        }
+      }
+    },
+    //添加小题
+    insertTopicStruct(row) {
+      let courseNo =
+        this.paperStruct.courseNo === "" ? "all" : this.paperStruct.courseNo;
+      this.getCourseName(this.paperStruct.courseNo);
+      sessionStorage.setItem("paperStruct", JSON.stringify(this.paperStruct));
+      this.$router.push({
+        path:
+          "/questions/insert_paper_structure_info/" +
+          this.paperStructId +
+          "/" +
+          row.name +
+          "/" +
+          row.id +
+          "/" +
+          courseNo,
+      });
+    },
+    //返回
+    back() {
+      this.$router.push({
+        path: "/questions/paper_structure/1",
+      });
+    },
+    back2(formData) {
+      this.resetForm(formData);
+      this.paperDetailStructDialog = false;
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      if (query) {
+        if (!(query.indexOf("(") > -1 && query.indexOf(")") > -1)) {
+          this.courseLoading = true;
+          this.$http
+            .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+            .then((response) => {
+              this.courseList = response.data;
+              this.courseLoading = false;
+            });
+        }
+      } else {
+        this.courseList = [];
+      }
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 1247 - 0
src/modules/questions/views/InsertPaperStructureInfo.vue

@@ -0,0 +1,1247 @@
+<template>
+  <section class="content">
+    <LinkTitlesCustom :current-paths="['题型创建', detailName]" />
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        ref="paperUnitForm"
+        :inline="true"
+        :model="paperUnitForm"
+        :rules="rules"
+        label-position="right"
+        label-width="78px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="题型结构" prop="questionType">
+              <el-select
+                v-model="paperUnitForm.questionType"
+                class="search_width"
+                placeholder="请选择"
+                size="small"
+                @change="getQuesNameList"
+              >
+                <el-option
+                  v-for="item in quesTypes"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="每题分值" prop="score">
+              <el-input
+                v-model.number="paperUnitForm.score"
+                class="search_width"
+                placeholder="请输入"
+                size="small"
+                @change="muli"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="题目数量" prop="count">
+              <el-input
+                v-model.number="paperUnitForm.count"
+                class="search_width_80px"
+                :disabled="true"
+              >
+              </el-input>
+              <el-button
+                icon="el-icon-plus"
+                @click="propertyDialog = true"
+              ></el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="题型总分" prop="totalScore">
+              <el-input
+                v-model="paperUnitForm.totalScore"
+                class="search_width"
+                :disabled="true"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="来源大题" prop="quesNames">
+              <el-select
+                v-model="paperUnitForm.quesNames"
+                class="search_width"
+                multiple
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in quesNameList"
+                  :key="item.code"
+                  :label="item.name"
+                  :value="item.code"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button
+                size="small"
+                type="primary"
+                @click="submitForm('paperUnitForm')"
+                >保 存</el-button
+              >
+              <el-button size="small" @click="resetForm('paperUnitForm')"
+                ><i class="el-icon-refresh"></i> 重 置</el-button
+              >
+              <el-button
+                size="small"
+                type="primary"
+                icon="el-icon-arrow-left"
+                @click="back"
+                >返 回</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div
+        style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+      ></div>
+      <!-- 页面列表 -->
+      <el-table
+        :data="unitStructs"
+        border
+        style="width: 100%; text-align: center"
+      >
+        <el-table-column label="大题名称" width="100">
+          <template slot-scope="scope">
+            <span v-if="false">{{ scope.detailName }}</span>
+            <span>{{ detailName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题型结构" width="80">
+          <template slot-scope="scope">
+            <span>{{ getQuesTypeName(scope.row.questionType) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题目总数" width="80">
+          <template slot-scope="scope">
+            <span>{{ scope.row.count }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="公开(简单)" width="88">
+          <template slot-scope="scope">
+            <span>{{ scope.row.publicSimple }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="公开(中等)" width="88">
+          <template slot-scope="scope">
+            <span>{{ scope.row.publicMedium }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="公开(困难)" width="88">
+          <template slot-scope="scope">
+            <span>{{ scope.row.publicDifficulty }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="非公开(简单)" width="102">
+          <template slot-scope="scope">
+            <span>{{ scope.row.noPublicSimple }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="非公开(中等)" width="102">
+          <template slot-scope="scope">
+            <span>{{ scope.row.noPublicMedium }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="非公开(困难)" width="102">
+          <template slot-scope="scope">
+            <span>{{ scope.row.noPublicDifficulty }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="每题分值" width="80">
+          <template slot-scope="scope">
+            <span>{{ scope.row.score }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题型总分" width="80">
+          <template slot-scope="scope">
+            <span>{{ scope.row.totalScore }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="来源大题" width="150">
+          <template slot-scope="scope">
+            <span>{{ getQuesNames(scope.row.quesNames) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="175">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                @click="openPaperDetailUnitStructDialog(scope.row.id)"
+                ><i class="el-icon-edit"></i>编辑</el-button
+              >
+              <el-button
+                size="mini"
+                type="danger"
+                @click="deleteUnit(scope.row.id)"
+                ><i class="el-icon-delete"></i> 删除</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 小题信息 -->
+      <el-dialog title="小题信息" :visible.sync="paperDetailUnitStructDialog">
+        <div>
+          <el-form
+            ref="paperUnitForm2"
+            :inline="true"
+            :model="paperUnitForm2"
+            :rules="rules"
+          >
+            <el-row>
+              <el-col :span="12">
+                <el-form-item
+                  label="题型结构"
+                  prop="questionType"
+                  label-width="78px"
+                >
+                  <el-select
+                    v-model="paperUnitForm2.questionType"
+                    class="form_width"
+                    placeholder="请选择"
+                    @change="getQuesNameList"
+                  >
+                    <el-option
+                      v-for="item in quesTypes"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value"
+                    ></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="每题分值" prop="score" label-width="78px">
+                  <el-input
+                    v-model.number="paperUnitForm2.score"
+                    class="form_width"
+                    placeholder="请输入"
+                    @change="muli2"
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row>
+              <el-col :span="12">
+                <el-form-item
+                  label="题型总分"
+                  prop="totalScore"
+                  label-width="78px"
+                >
+                  <el-input
+                    v-model="paperUnitForm2.totalScore"
+                    class="form_width"
+                    :disabled="true"
+                  ></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item
+                  label="来源大题"
+                  prop="quesNames"
+                  label-width="78px"
+                >
+                  <el-select
+                    v-model="paperUnitForm2.quesNames"
+                    class="form_width"
+                    multiple
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in quesNameList"
+                      :key="item.code"
+                      :label="item.name"
+                      :value="item.code"
+                    ></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">公开(简单)</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">公开(中等)</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">公开(困难)</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">公开总数</div>
+                </div>
+              </el-col>
+            </el-row>
+            <!-- 第二列 -->
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.publicSimple"
+                    @change="
+                      unitCount(
+                        paperUnitForm2.publicSimple,
+                        'publicSimple',
+                        paperUnitForm2
+                      )
+                    "
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.publicMedium"
+                    @change="
+                      unitCount(
+                        paperUnitForm2.publicMedium,
+                        'publicMedium',
+                        paperUnitForm2
+                      )
+                    "
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.publicDifficulty"
+                    @change="
+                      unitCount(
+                        paperUnitForm2.publicDifficulty,
+                        'publicDifficulty',
+                        paperUnitForm2
+                      )
+                    "
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.publicSum"
+                    :disabled="true"
+                  ></el-input>
+                </div>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">非公开(简单)</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">非公开(中等)</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">非公开(困难)</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">非公开总数</div>
+                </div>
+              </el-col>
+            </el-row>
+            <!-- 第四列 -->
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.noPublicSimple"
+                    @change="
+                      unitCount(
+                        paperUnitForm2.noPublicSimple,
+                        'noPublicSimple',
+                        paperUnitForm2
+                      )
+                    "
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.noPublicMedium"
+                    @change="
+                      unitCount(
+                        paperUnitForm2.noPublicMedium,
+                        'noPublicMedium',
+                        paperUnitForm2
+                      )
+                    "
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.noPublicDifficulty"
+                    @change="
+                      unitCount(
+                        paperUnitForm2.noPublicDifficulty,
+                        'noPublicDifficulty',
+                        paperUnitForm2
+                      )
+                    "
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.noPublicSum"
+                    :disabled="true"
+                  ></el-input>
+                </div>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">简单总数</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">中等总数</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">困难总数</div>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <div class="form_font_size">总数</div>
+                </div>
+              </el-col>
+            </el-row>
+            <!-- 第六列 -->
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.simpleSum"
+                    :disabled="true"
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.mediumSum"
+                    :disabled="true"
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.difficultySum"
+                    :disabled="true"
+                  ></el-input>
+                </div>
+              </el-col>
+              <el-col :span="6">
+                <div class="grid-content bg-purple">
+                  <el-input
+                    v-model="paperUnitForm2.count"
+                    :disabled="true"
+                  ></el-input>
+                </div>
+              </el-col>
+            </el-row>
+            <el-row>
+              <div style="margin-left: 45%">
+                <el-button
+                  type="primary"
+                  @click="editSubmitForm('paperUnitForm2')"
+                  >保 存</el-button
+                >
+              </div>
+            </el-row>
+          </el-form>
+        </div>
+      </el-dialog>
+      <!-- 设置题目数量弹框 -->
+      <el-dialog title="题目数量" :visible.sync="propertyDialog">
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">公开(简单)</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">公开(中等)</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">公开(困难)</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">公开总数</div>
+            </div>
+          </el-col>
+        </el-row>
+        <!-- 第二列 -->
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.publicSimple"
+                @change="
+                  unitCount(
+                    paperUnitForm.publicSimple,
+                    'publicSimple',
+                    paperUnitForm
+                  )
+                "
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.publicMedium"
+                @change="
+                  unitCount(
+                    paperUnitForm.publicMedium,
+                    'publicMedium',
+                    paperUnitForm
+                  )
+                "
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.publicDifficulty"
+                @change="
+                  unitCount(
+                    paperUnitForm.publicDifficulty,
+                    'publicDifficulty',
+                    paperUnitForm
+                  )
+                "
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.publicSum"
+                :disabled="true"
+              ></el-input>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">非公开(简单)</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">非公开(中等)</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">非公开(困难)</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">非公开总数</div>
+            </div>
+          </el-col>
+        </el-row>
+        <!-- 第四列 -->
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.noPublicSimple"
+                @change="
+                  unitCount(
+                    paperUnitForm.noPublicSimple,
+                    'noPublicSimple',
+                    paperUnitForm
+                  )
+                "
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.noPublicMedium"
+                @change="
+                  unitCount(
+                    paperUnitForm.noPublicMedium,
+                    'noPublicMedium',
+                    paperUnitForm
+                  )
+                "
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.noPublicDifficulty"
+                @change="
+                  unitCount(
+                    paperUnitForm.noPublicDifficulty,
+                    'noPublicDifficulty',
+                    paperUnitForm
+                  )
+                "
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.noPublicSum"
+                :disabled="true"
+              ></el-input>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">简单总数</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">中等总数</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">困难总数</div>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <div class="form_font_size">总数</div>
+            </div>
+          </el-col>
+        </el-row>
+        <!-- 第六列 -->
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.simpleSum"
+                :disabled="true"
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.mediumSum"
+                :disabled="true"
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.difficultySum"
+                :disabled="true"
+              ></el-input>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="grid-content bg-purple">
+              <el-input
+                v-model="paperUnitForm.count"
+                :disabled="true"
+              ></el-input>
+            </div>
+          </el-col>
+        </el-row>
+        <div style="margin-top: 20px; margin-left: 42%">
+          <el-button type="primary" @click="propertyDialog = false"
+            >保 存</el-button
+          >
+          <el-button type="primary" @click="propertyDialog = false"
+            >返 回</el-button
+          >
+        </div>
+      </el-dialog>
+    </div>
+  </section>
+</template>
+
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { QUESTION_TYPES } from "../constants/constants";
+import _ from "lodash";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    var checkIntegerNumber = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error("数值不能为空"));
+      }
+      setTimeout(() => {
+        if (!Number.isInteger(value)) {
+          callback(new Error("请输入整数"));
+        } else {
+          if (value < 0) {
+            callback(new Error("必须大于0"));
+          } else {
+            callback();
+          }
+        }
+      }, 100);
+    };
+    var checkDoubleNumber = (rule, value, callback) => {
+      //var reg = /^[-\+]?\d+(\.\d+)?$/;
+      var reg = /^\d+(?=\.{0,1}\d+$|$)/;
+      if (!value) {
+        return callback(new Error("数值不能为空"));
+      }
+      setTimeout(() => {
+        if (!reg.test(value)) {
+          callback(new Error("请输入数值"));
+        } else {
+          if (value < 0) {
+            callback(new Error("必须大于等于0"));
+          } else {
+            callback();
+          }
+        }
+      }, 100);
+    };
+    var validateQuesNames = (rule, value, callback) => {
+      if (Array.isArray(value) && value.length == 0) {
+        callback(new Error("请选择来源大题"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      quesTypes: QUESTION_TYPES,
+      courseNo: "",
+      quesNameList: [],
+      paperUnitForm: {
+        id: "",
+        questionType: "",
+        count: 0,
+        score: "",
+        totalScore: "",
+        quesNames: [],
+        publicSimple: 0,
+        publicMedium: 0,
+        publicDifficulty: 0,
+        noPublicSimple: 0,
+        noPublicMedium: 0,
+        noPublicDifficulty: 0,
+        publicSum: 0,
+        noPublicSum: 0,
+        simpleSum: 0,
+        mediumSum: 0,
+        difficultySum: 0,
+      },
+      paperUnitForm2: {
+        id: "",
+        questionType: "",
+        count: "",
+        score: "",
+        totalScore: "",
+        quesNames: [],
+        publicSimple: 0,
+        publicMedium: 0,
+        publicDifficulty: 0,
+        noPublicSimple: 0,
+        noPublicMedium: 0,
+        noPublicDifficulty: 0,
+        publicSum: 0,
+        noPublicSum: 0,
+        simpleSum: 0,
+        mediumSum: 0,
+        difficultySum: 0,
+      },
+      unitId: "",
+      detailName: "",
+      detailId: "",
+      paperStructId: "",
+      paperStruct: {},
+      unitStructs: [],
+      paperDetailUnitStructDialog: false,
+      propertyDialog: false,
+      rules: {
+        questionType: [
+          { required: true, message: "题型不能为空", trigger: "change" },
+        ],
+        count: [
+          { validator: checkIntegerNumber, required: true, trigger: "blur" },
+        ],
+        score: [
+          { required: true, validator: checkDoubleNumber, trigger: "blur" },
+        ],
+        quesNames: [{ validator: validateQuesNames, trigger: "change" }],
+      },
+    };
+  },
+  computed: {
+    totalScore() {
+      var sum = 0.0;
+      for (let unitStruct of this.unitStructs) {
+        sum += unitStruct.totalScore;
+      }
+      return sum;
+    },
+    detailCount() {
+      var count = 0.0;
+      for (let unitStruct of this.unitStructs) {
+        count += unitStruct.count;
+      }
+      return count;
+    },
+    quesNames() {
+      var quesNames = [];
+      for (let quesName of this.quesNameList) {
+        quesNames.push(quesName.code);
+      }
+      return quesNames;
+    },
+  },
+  //初始化查询
+  created() {
+    this.paperStructId = this.$route.params.id;
+    this.detailName = this.$route.params.name;
+    this.detailId = this.$route.params.detailId;
+    this.courseNo = this.$route.params.courseNo;
+    this.searchForm();
+  },
+  methods: {
+    //查询来源大题名称
+    getQuesNameList(quesType) {
+      if (quesType && quesType.length > 0) {
+        var quesTypeObj = {};
+        var url = QUESTION_API + "/paperStruct/quesNames";
+        if (this.courseNo !== "all") {
+          quesTypeObj = {
+            quesType: quesType,
+            courseNo: this.courseNo,
+          };
+        } else {
+          quesTypeObj = {
+            quesType: quesType,
+          };
+        }
+        this.$http.get(url, { params: quesTypeObj }).then((response) => {
+          console.log(response);
+          this.quesNameList = response.data;
+          if (this.quesNameList != null) {
+            for (let quesName of this.quesNameList) {
+              if (quesName.name.length > 50) {
+                quesName.name = quesName.name.substring(0, 50) + "...";
+              }
+            }
+          }
+          this.initQuesNames();
+        });
+      }
+    },
+    initQuesNames() {
+      var arr1 = _.difference(this.paperUnitForm.quesNames, this.quesNames);
+      if (arr1.length > 0) {
+        this.paperUnitForm.quesNames = [];
+      }
+      var arr2 = _.difference(this.paperUnitForm2.quesNames, this.quesNames);
+      if (arr2.length > 0) {
+        this.paperUnitForm2.quesNames = [];
+      }
+    },
+    getQuesNames(quesNameList) {
+      var quesNameStr = "";
+      if (!Array.isArray(quesNameList)) {
+        return quesNameStr;
+      }
+      for (let quesName of quesNameList) {
+        if (quesNameStr === "") {
+          quesNameStr += quesName;
+        } else {
+          quesNameStr += "," + quesName;
+        }
+      }
+      return quesNameStr;
+    },
+    //查询列表集合
+    searchForm() {
+      this.loading = true;
+      var paperStructStorge = sessionStorage.getItem("paperStruct");
+      if (typeof paperStructStorge === "string") {
+        var paperStruct = JSON.parse(paperStructStorge);
+        this.paperStruct = paperStruct;
+        this.unitStructs = this.getUnits();
+        console.log("this.unitStructs:", this.unitStructs);
+      }
+    },
+    //保存
+    submitForm(formData) {
+      this.loading = true;
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          var unitStruct = Object.assign({}, this.paperUnitForm);
+          var ids = [];
+          for (let unitStruct of this.unitStructs) {
+            ids.push(unitStruct.id);
+          }
+          ids.sort();
+          var maxId;
+          if (ids.length == 0) {
+            maxId = 1;
+          } else {
+            maxId = ids[ids.length - 1];
+          }
+          unitStruct.id = maxId + 1;
+          this.unitStructs.push(unitStruct);
+          this.resetForm(formData);
+          this.paperUnitForm.publicSimple = 0;
+          this.paperUnitForm.publicMedium = 0;
+          this.paperUnitForm.publicDifficulty = 0;
+          this.paperUnitForm.noPublicSimple = 0;
+          this.paperUnitForm.noPublicMedium = 0;
+          this.paperUnitForm.noPublicDifficulty = 0;
+          this.paperUnitForm.publicSum = 0;
+          this.paperUnitForm.noPublicSum = 0;
+          this.paperUnitForm.simpleSum = 0;
+          this.paperUnitForm.mediumSum = 0;
+          this.paperUnitForm.difficultySum = 0;
+          this.setUnits();
+          sessionStorage.setItem(
+            "paperStruct",
+            JSON.stringify(this.paperStruct)
+          );
+        } else {
+          return false;
+        }
+      });
+    },
+
+    editSubmitForm(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          for (let unitStruct of this.unitStructs) {
+            if (unitStruct.id == this.paperUnitForm2.id) {
+              unitStruct.questionType = this.paperUnitForm2.questionType;
+              unitStruct.count = this.paperUnitForm2.count;
+              unitStruct.score = this.paperUnitForm2.score;
+              unitStruct.totalScore = this.paperUnitForm2.totalScore;
+              unitStruct.quesNames = this.paperUnitForm2.quesNames;
+              unitStruct.publicSimple = this.paperUnitForm2.publicSimple;
+              unitStruct.publicMedium = this.paperUnitForm2.publicMedium;
+              unitStruct.publicDifficulty = this.paperUnitForm2.publicDifficulty;
+              unitStruct.noPublicSimple = this.paperUnitForm2.noPublicSimple;
+              unitStruct.noPublicMedium = this.paperUnitForm2.noPublicMedium;
+              unitStruct.noPublicDifficulty = this.paperUnitForm2.noPublicDifficulty;
+              unitStruct.publicSum = this.paperUnitForm2.publicSum;
+              unitStruct.noPublicSum = this.paperUnitForm2.noPublicSum;
+              unitStruct.simpleSum = this.paperUnitForm2.simpleSum;
+              unitStruct.mediumSum = this.paperUnitForm2.mediumSum;
+              unitStruct.difficultySum = this.paperUnitForm2.difficultySum;
+              this.setUnits();
+              this.paperDetailUnitStructDialog = false;
+              sessionStorage.setItem(
+                "paperStruct",
+                JSON.stringify(this.paperStruct)
+              );
+            }
+          }
+        }
+      });
+    },
+    //重置
+    resetForm(formData) {
+      this.$refs[formData].resetFields();
+      //this.$refs[formData].clearValidate();
+      // this.paperUnitForm = {
+      //   id: "",
+      //   questionType: "",
+      //   count: 0,
+      //   score: "",
+      //   totalScore: "",
+      //   quesNames: [],
+      //   publicSimple: 0,
+      //   publicMedium: 0,
+      //   publicDifficulty: 0,
+      //   noPublicSimple: 0,
+      //   noPublicMedium: 0,
+      //   noPublicDifficulty: 0,
+      //   publicSum: 0,
+      //   noPublicSum: 0,
+      //   simpleSum: 0,
+      //   mediumSum: 0,
+      //   difficultySum: 0
+      // };
+    },
+    //返回
+    back() {
+      this.$router.push({
+        path: "/questions/insert_paper_structure/" + this.paperStructId,
+      });
+    },
+    muli() {
+      this.paperUnitForm.totalScore =
+        (this.paperUnitForm.score * 1000 * this.paperUnitForm.count) / 1000;
+    },
+    muli2() {
+      this.paperUnitForm2.totalScore =
+        (this.paperUnitForm2.score * 1000 * this.paperUnitForm2.count) / 1000;
+    },
+    getUnits() {
+      var unitStructs = [];
+      for (let paperDetailStruct of this.paperStruct.paperDetailStructs) {
+        if (parseInt(paperDetailStruct.id) == this.detailId) {
+          if (paperDetailStruct.unitStructs) {
+            return paperDetailStruct.unitStructs;
+          }
+        }
+      }
+      return unitStructs;
+    },
+    setUnits() {
+      for (let paperDetailStruct of this.paperStruct.paperDetailStructs) {
+        if (parseInt(paperDetailStruct.id) == this.detailId) {
+          paperDetailStruct.unitStructs = this.unitStructs;
+          paperDetailStruct.totalScore = this.totalScore;
+          paperDetailStruct.detailCount = this.detailCount;
+        }
+      }
+    },
+    getQuesTypeName(value) {
+      for (let quesType of this.quesTypes) {
+        if (value == quesType.value) {
+          return quesType.label;
+        }
+      }
+    },
+
+    openPaperDetailUnitStructDialog(id) {
+      this.unitId = id;
+      for (let unitStruct of this.unitStructs) {
+        if (unitStruct.id == id) {
+          this.paperUnitForm2.id = id;
+          this.paperUnitForm2.questionType = unitStruct.questionType;
+          this.paperUnitForm2.count = unitStruct.count;
+          this.paperUnitForm2.score = unitStruct.score;
+          this.paperUnitForm2.totalScore = unitStruct.totalScore;
+          this.paperUnitForm2.quesNames = unitStruct.quesNames;
+          this.paperUnitForm2.publicSimple = unitStruct.publicSimple;
+          this.paperUnitForm2.publicMedium = unitStruct.publicMedium;
+          this.paperUnitForm2.publicDifficulty = unitStruct.publicDifficulty;
+          this.paperUnitForm2.noPublicSimple = unitStruct.noPublicSimple;
+          this.paperUnitForm2.noPublicMedium = unitStruct.noPublicMedium;
+          this.paperUnitForm2.noPublicDifficulty =
+            unitStruct.noPublicDifficulty;
+          //实时计算,不存数据库
+          this.paperUnitForm2.publicSum =
+            this.paperUnitForm2.publicSimple +
+            this.paperUnitForm2.publicMedium +
+            this.paperUnitForm2.publicDifficulty;
+          this.paperUnitForm2.noPublicSum =
+            this.paperUnitForm2.noPublicSimple +
+            this.paperUnitForm2.noPublicMedium +
+            this.paperUnitForm2.noPublicDifficulty;
+          this.paperUnitForm2.simpleSum =
+            this.paperUnitForm2.publicSimple +
+            this.paperUnitForm2.noPublicSimple;
+          this.paperUnitForm2.mediumSum =
+            this.paperUnitForm2.publicMedium +
+            this.paperUnitForm2.noPublicMedium;
+          this.paperUnitForm2.difficultySum =
+            this.paperUnitForm2.publicDifficulty +
+            this.paperUnitForm2.noPublicDifficulty;
+        }
+      }
+      this.getQuesNameList(this.paperUnitForm2.questionType);
+      this.paperDetailUnitStructDialog = true;
+    },
+    deleteUnit(id) {
+      var unitStructs = this.unitStructs;
+      for (let i = unitStructs.length - 1; i >= 0; i--) {
+        if (unitStructs[i].id == id) {
+          unitStructs.splice(i, 1);
+        }
+      }
+      this.unitStructs = unitStructs;
+      this.setUnits();
+      sessionStorage.setItem("paperStruct", JSON.stringify(this.paperStruct));
+      console.log(sessionStorage.getItem("paperStruct"));
+    },
+    //判断是否为正整数
+    isPositiveInteger(s) {
+      //是否为正整数
+      var re = /^[0-9]+$/;
+      return re.test(s);
+    },
+    //计算数量
+    unitCount(data, rowType, paperUnitForm) {
+      //计算前校验正整数
+      if (!this.isPositiveInteger(data)) {
+        if (rowType === "publicSimple") {
+          setTimeout(function () {
+            paperUnitForm.publicSimple = 0;
+          }, 1);
+        } else if (rowType === "publicMedium") {
+          setTimeout(function () {
+            paperUnitForm.publicMedium = 0;
+          }, 1);
+        } else if (rowType === "publicDifficulty") {
+          setTimeout(function () {
+            paperUnitForm.publicDifficulty = 0;
+          }, 1);
+        } else if (rowType === "noPublicSimple") {
+          setTimeout(function () {
+            paperUnitForm.noPublicSimple = 0;
+          }, 1);
+        } else if (rowType === "noPublicMedium") {
+          setTimeout(function () {
+            paperUnitForm.noPublicMedium = 0;
+          }, 1);
+        } else if (rowType === "noPublicDifficulty") {
+          setTimeout(function () {
+            paperUnitForm.noPublicDifficulty = 0;
+          }, 1);
+        }
+      }
+      setTimeout(function () {
+        paperUnitForm.publicSimple = parseInt(paperUnitForm.publicSimple);
+        paperUnitForm.publicMedium = parseInt(paperUnitForm.publicMedium);
+        paperUnitForm.publicDifficulty = parseInt(
+          paperUnitForm.publicDifficulty
+        );
+        paperUnitForm.noPublicSimple = parseInt(paperUnitForm.noPublicSimple);
+        paperUnitForm.noPublicMedium = parseInt(paperUnitForm.noPublicMedium);
+        paperUnitForm.noPublicDifficulty = parseInt(
+          paperUnitForm.noPublicDifficulty
+        );
+        //公开总数
+        paperUnitForm.publicSum =
+          parseInt(paperUnitForm.publicSimple) +
+          parseInt(paperUnitForm.publicMedium) +
+          parseInt(paperUnitForm.publicDifficulty);
+        //非公开总数
+        paperUnitForm.noPublicSum =
+          parseInt(paperUnitForm.noPublicSimple) +
+          parseInt(paperUnitForm.noPublicMedium) +
+          parseInt(paperUnitForm.noPublicDifficulty);
+        //简单总数
+        paperUnitForm.simpleSum =
+          parseInt(paperUnitForm.publicSimple) +
+          parseInt(paperUnitForm.noPublicSimple);
+        //中等总数
+        paperUnitForm.mediumSum =
+          parseInt(paperUnitForm.publicMedium) +
+          parseInt(paperUnitForm.noPublicMedium);
+        //困难总数
+        paperUnitForm.difficultySum =
+          parseInt(paperUnitForm.publicDifficulty) +
+          parseInt(paperUnitForm.noPublicDifficulty);
+        //总数
+        paperUnitForm.count =
+          paperUnitForm.publicSum + paperUnitForm.noPublicSum;
+        //分数
+        paperUnitForm.totalScore =
+          (paperUnitForm.score * 1000 * paperUnitForm.count) / 1000;
+      }, 5);
+      if (this.clearCheck(paperUnitForm.count)) {
+        this.$refs["paperUnitForm"].clearValidate("count");
+      }
+    },
+    clearCheck(value) {
+      var reg = /^\d+(?=\.{0,1}\d+$|$)/;
+      if (!value) {
+        return true;
+      }
+      if (!reg.test(value)) {
+        return true;
+      } else {
+        if (value < 0) {
+          return true;
+        } else {
+          return false;
+        }
+      }
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>
+<style scoped>
+.el-row {
+  margin-bottom: 20px;
+}
+.el-col {
+  border-radius: 4px;
+}
+.bg-purple-dark {
+  background: #dcdfe6;
+}
+.bg-purple {
+  background: #dcdfe6;
+}
+.bg-purple-light {
+  background: #f9fafc;
+}
+.grid-content {
+  border-radius: 4px;
+  min-height: 36px;
+}
+.row-bg {
+  padding: 10px 0;
+  background-color: #f9fafc;
+}
+.el-form--inline .el-form-item__label {
+  float: none;
+  display: inline-block;
+  width: 78px;
+}
+</style>

+ 375 - 0
src/modules/questions/views/InsertPaperTitle.vue

@@ -0,0 +1,375 @@
+<template>
+  <section class="content">
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form :inline="true" :model="paperTitleForm" label-width="150px">
+        <el-row>
+          <el-form-item label="课程名称(代码)" class="pull-left">
+            <el-select
+              v-model="paperTitleForm.courseNo"
+              class="dialog_input_width"
+              filterable
+              clearable
+              remote
+              placeholder="请输入课程名称(代码)"
+              :remote-method="queryCoursesByKeyword"
+              @change="searchGenPaper"
+              @focus="(e) => queryCoursesByKeyword(e.target.value)"
+            >
+              <el-option
+                v-for="item in courseInfoSelect"
+                :key="item.courseNo"
+                :label="item.courseInfo"
+                :value="item.courseNo"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row v-if="paperTitelDisable">
+          <el-form-item label="试卷名称" class="pull-left">
+            <el-select
+              v-model="paperTitleForm.paperId"
+              class="dialog_input_width"
+              filterable
+              placeholder="请选择"
+              sytle="width:220px;"
+              @change="searchPaperDetail"
+            >
+              <el-option
+                v-for="item in paperInfoSelect"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+            <span style="padding-left: 5px">
+              <el-button type="primary" @click="insertPaper"
+                ><i class="el-icon-plus"></i> 新增试卷</el-button
+              >
+            </span>
+          </el-form-item>
+        </el-row>
+        <el-row v-if="paperDetailDisable">
+          <el-form-item label="试卷大题" class="pull-left">
+            <el-select
+              v-model="paperTitleForm.paperDetailId"
+              class="dialog_input_width"
+              filterable
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in paperDetailInfoSelect"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="题型" class="pull-left">
+            <el-select
+              v-model="paperTitleForm.value"
+              class="dialog_input_width"
+              filterable
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in quesTypes"
+                :key="item.id"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label=" " class="pull-left">
+            <el-button
+              type="primary"
+              :disabled="nextDisabled"
+              @click="submitForm"
+              >下一步
+            </el-button>
+          </el-form-item>
+        </el-row>
+      </el-form>
+
+      <!-- 添加试卷弹出框 -->
+      <el-dialog title="新增试卷" :visible.sync="paperDialog" width="500px">
+        <el-form
+          ref="paperForm"
+          :model="paperForm"
+          :rules="rules"
+          label-position="right"
+          label-width="120px"
+          inline-message
+        >
+          <el-row>
+            <el-form-item label="试卷名称" label-width="120px" prop="name">
+              <el-input
+                v-model="paperForm.name"
+                class="dialog_input_width"
+                placeholder="请输入试卷名称"
+              ></el-input>
+            </el-form-item>
+          </el-row>
+          <el-row class="margin_top_10 margin_left_120">
+            <el-button type="primary" @click="submitPaperForm('paperForm')"
+              >保 存</el-button
+            >
+            <el-button @click="resetPaperForm('paperForm')"
+              ><i class="el-icon-refresh"></i> 重 置</el-button
+            >
+          </el-row>
+        </el-form>
+      </el-dialog>
+    </div>
+  </section>
+</template>
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import { mapState } from "vuex";
+export default {
+  data() {
+    return {
+      paperTitleForm: {
+        courseNo: "",
+        courseName: "",
+        paperId: "",
+        paperDetailId: "",
+        value: "",
+      },
+      paperForm: {
+        name: "",
+      },
+      paperDialog: false,
+      courseList: [],
+      tableData: [],
+      detailsData: [],
+      quesTypes: [
+        { value: "SINGLE_ANSWER_QUESTION", label: "单选" },
+        { value: "MULTIPLE_ANSWER_QUESTION", label: "多选" },
+        { value: "BOOL_ANSWER_QUESTION", label: "判断" },
+        { value: "FILL_BLANK_QUESTION", label: "填空" },
+        { value: "TEXT_ANSWER_QUESTION", label: "问答" },
+      ],
+      rules: {
+        name: [{ required: true, message: "请输入试卷名称", trigger: "blur" }],
+      },
+    };
+  },
+  computed: {
+    paperTitelDisable() {
+      if (this.paperTitleForm.courseNo) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    paperDetailDisable() {
+      console.log(
+        "this.paperTitleForm.courseNo:",
+        this.paperTitleForm.courseNo
+      );
+      console.log("this.paperTitleForm.paperId:", this.paperTitleForm.paperId);
+      console.log("this.detailsData.length:", this.detailsData.length);
+      if (
+        this.paperTitleForm.courseNo &&
+        this.paperTitleForm.paperId &&
+        this.detailsData.length > 0
+      ) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    nextDisabled() {
+      if (
+        this.paperTitleForm.courseNo &&
+        this.paperTitleForm.paperId &&
+        this.paperTitleForm.paperDetailId &&
+        this.paperTitleForm.value
+      ) {
+        return false;
+      } else {
+        return true;
+      }
+    },
+    //获取课程的集合
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+    //获取试卷的集合
+    paperInfoSelect() {
+      var paperList = [];
+      for (let paper of this.tableData) {
+        var paperId = paper.id;
+        var paperName = paper.name;
+        paperList.push({ id: paperId, name: paperName });
+      }
+      return paperList;
+    },
+    //获取大题的集合
+    paperDetailInfoSelect() {
+      var paperDetaliList = [];
+      for (let paperDetail of this.detailsData) {
+        var paperDetailId = paperDetail.id;
+        var paperDetailName = paperDetail.name;
+        paperDetaliList.push({ id: paperDetailId, name: paperDetailName });
+      }
+
+      return paperDetaliList;
+    },
+    routeType() {
+      return this.$route.params.type;
+    },
+    ...mapState({ user: (state) => state.user }),
+  },
+  methods: {
+    //远端查询课程
+    queryCoursesByKeyword(query) {
+      query = query.trim();
+      this.courseLoading = true;
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          this.courseLoading = false;
+        });
+    },
+    //根据课程查询试卷集合
+    searchGenPaper() {
+      this.$http
+        .get(
+          QUESTION_API +
+            "/importPaper/1/500/?courseNo=" +
+            this.paperTitleForm.courseNo
+        )
+        .then((response) => {
+          this.tableData = response.data.content;
+        });
+      this.paperTitleForm.paperId = "";
+      this.paperTitleForm.paperDetailId = "";
+    },
+    //根据试卷查询大题
+    searchPaperDetail() {
+      this.$http
+        .get(QUESTION_API + "/paperDetail/paper/" + this.paperTitleForm.paperId)
+        .then((response) => {
+          this.detailsData = response.data;
+          console.log("response.data:", response.data);
+          console.log("this.detailsData:", this.detailsData);
+          for (var i = 0; i < this.detailsData.length; i++) {
+            if (!this.detailsData[i].name) {
+              this.detailsData[i].name = "默认大题";
+            }
+          }
+        });
+      this.paperTitleForm.paperDetailId = "";
+    },
+    //新增试卷
+    insertPaper() {
+      this.paperDialog = true;
+    },
+    //重置
+    resetPaperForm(formData) {
+      this.$refs[formData].resetFields();
+    },
+    //提交新增试卷
+    submitPaperForm(formData) {
+      this.$refs[formData].validate((valid) => {
+        if (valid) {
+          for (let course of this.courseList) {
+            if (course.code == this.paperTitleForm.courseNo) {
+              this.paperTitleForm.courseName = course.name;
+            }
+          }
+          console.log("课程名称:" + this.paperTitleForm.courseName);
+          this.$http
+            .post(
+              QUESTION_API +
+                "/importPaper/saveBlankPaper/" +
+                this.paperTitleForm.courseNo +
+                "/" +
+                this.paperForm.name
+            )
+            .then((response) => {
+              var paperId = response.data.paper.id;
+              this.$http
+                .get(
+                  QUESTION_API +
+                    "/importPaper/1/500/?courseNo=" +
+                    this.paperTitleForm.courseNo
+                )
+                .then((response) => {
+                  this.tableData = response.data.content;
+                  this.paperTitleForm.paperDetailId = "";
+                  this.paperTitleForm.paperId = paperId;
+                  this.searchPaperDetail();
+                });
+              this.paperDialog = false;
+            })
+            .catch((error) => {
+              this.$notify({ type: "error", message: error.response.data.msg });
+              this.paperDialog = false;
+            });
+        } else {
+          return false;
+        }
+      });
+    },
+    //提交
+    submitForm() {
+      var path = "";
+      switch (this.paperTitleForm.value) {
+        case "SINGLE_ANSWER_QUESTION":
+        case "MULTIPLE_ANSWER_QUESTION":
+          path = "edit_select_question";
+          break;
+        case "TEXT_ANSWER_QUESTION":
+        case "FILL_BLANK_QUESTION":
+        case "BOOL_ANSWER_QUESTION":
+          path = "edit_other_question";
+          break;
+      }
+      for (let course of this.courseList) {
+        if (course.code == this.paperTitleForm.courseNo) {
+          this.paperTitleForm.courseName = course.name;
+        }
+      }
+      console.log(path);
+      this.$router.push({
+        path:
+          path +
+          "/" +
+          this.paperTitleForm.paperId +
+          "/" +
+          this.paperTitleForm.paperDetailId +
+          "/" +
+          this.paperTitleForm.value +
+          "/" +
+          this.paperTitleForm.courseNo +
+          "/" +
+          this.paperTitleForm.courseName,
+      });
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>
+<style scoped>
+.box_width_lg {
+  width: 600px;
+}
+</style>

+ 409 - 0
src/modules/questions/views/PaperStructure.vue

@@ -0,0 +1,409 @@
+<template>
+  <section class="content">
+    <div v-show="isClear == 1">
+      <LinkTitlesCustom :current-paths="['基础信息', '精确结构预设']" />
+    </div>
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="结构名称" class="pull-left">
+              <el-input
+                v-model="formSearch.name"
+                class="search_width"
+                placeholder="请输入结构名称"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="创建人" class="pull-left">
+              <el-input
+                v-model="formSearch.creator"
+                class="search_width"
+                placeholder="请输入创建人"
+                size="small"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="制定课程">
+              <el-select
+                v-model="formSearch.courseNo"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option key="ALL" label="请选择" value="ALL"></el-option>
+                <el-option key label="公用" value></el-option>
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseNo"
+                  :label="item.courseInfo"
+                  :value="item.courseNo"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" @click="resetForm">
+                <i class="el-icon-refresh"></i> 重 置</el-button
+              >
+              <el-button size="small" type="primary" @click="addPaperStruct"
+                ><i class="el-icon-plus"></i> 新增</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+        <div
+          style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+        ></div>
+        <el-row>
+          <el-form-item>
+            <span>批量操作:</span>
+            <el-button
+              size="small"
+              type="danger"
+              :disabled="noBatchSelected"
+              @click="deletePapers"
+            >
+              <i class="el-icon-delete"></i> 删除
+            </el-button>
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div style="width: 100%; margin-bottom: 10px"></div>
+      <!-- 页面列表 -->
+      <el-table
+        v-loading="loading"
+        :data="tableData"
+        element-loading-text="拼命加载中"
+        border
+        style="width: 100%; text-align: center"
+        @selection-change="selectionChange"
+      >
+        <el-table-column type="selection" width="40"></el-table-column>
+        <el-table-column label="预设精确结构名称">
+          <template slot-scope="scope">
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="关联课程" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程代码" width="90">
+          <template slot-scope="scope">
+            <span>{{ scope.row.courseNo }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="类型" width="80">
+          <template slot-scope="scope">
+            <span>{{ getType(scope.row.courseNo) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="大题数" width="88" sortable prop="detailCount">
+        </el-table-column>
+        <el-table-column
+          label="小题数"
+          width="88"
+          sortable
+          prop="detailUnitCount"
+        >
+        </el-table-column>
+        <el-table-column label="总分" width="78" sortable prop="totalScore">
+        </el-table-column>
+        <el-table-column label="创建人" width="100">
+          <template slot-scope="scope">
+            <span>{{ scope.row.creator }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="175" fixed="right">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                size="mini"
+                type="primary"
+                plain
+                @click="editPaperStruct(scope.row)"
+                ><i class="el-icon-edit"></i>编辑</el-button
+              >
+              <el-button
+                size="mini"
+                type="danger"
+                @click="deletePaper(scope.$index, scope.row)"
+                ><i class="el-icon-delete"></i> 删除</el-button
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        ></el-pagination>
+      </div>
+    </div>
+  </section>
+</template>
+
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      courseLoading: false,
+      formSearch: {
+        name: "",
+        creator: "",
+        courseNo: "ALL",
+        type: "EXACT",
+        courseName: "",
+      },
+      paperStructId: "",
+      loading: false,
+      tableData: [],
+      selectedList: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      courseList: [],
+      isClear: 0,
+    };
+  },
+  computed: {
+    selectedIds() {
+      var selectedIdsStr = "";
+      for (let id of this.selectedList) {
+        if (!selectedIdsStr) {
+          selectedIdsStr += id;
+        } else {
+          selectedIdsStr += "," + id;
+        }
+      }
+      return selectedIdsStr;
+    },
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+    noBatchSelected() {
+      return this.selectedList.length === 0;
+    },
+  },
+  watch: {
+    $route: "initVue",
+  },
+  created() {
+    this.initVue();
+    this.removeItem();
+  },
+  methods: {
+    //重置
+    resetForm() {
+      this.formSearch = {
+        name: "",
+        creator: "",
+        courseNo: "ALL",
+        type: "EXACT",
+      };
+    },
+    //查询
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchPaperStructs();
+    },
+    searchPaperStructs() {
+      var pageNo = Number(this.currentPage);
+      this.currentPage = 1;
+      this.loading = true;
+      var url = QUESTION_API + "/paperStruct/" + pageNo + "/" + this.pageSize;
+      this.$http.get(url, { params: this.formSearch }).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.currentPage = pageNo;
+      });
+      this.loading = false;
+    },
+    //全选
+    selectionChange(val) {
+      this.selectedList = [];
+      var selectedList = this.selectedList;
+      val.forEach((element) => {
+        selectedList.push(element.id);
+      });
+      this.selectedList = selectedList;
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.formSearch.courseName = course.name;
+        }
+      }
+    },
+    editPaperStruct(row) {
+      this.getCourseName(this.formSearch.courseNo);
+      sessionStorage.setItem("paper_stucture", JSON.stringify(this.formSearch));
+      sessionStorage.setItem("paper_stucture_currentPage", this.currentPage);
+      sessionStorage.setItem("paperStruct", JSON.stringify(row));
+      this.$router.push({
+        path: "/questions/insert_paper_structure/" + row.id,
+      });
+    },
+    //删除单条数据
+    deletePaper(index, row) {
+      this.$confirm("是否删除试卷结构?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        var url = QUESTION_API + "/paperStruct/" + row.id;
+        this.loading = true;
+        this.$http.delete(url).then(
+          () => {
+            this.$notify({
+              type: "success",
+              message: "删除成功!",
+            });
+            this.searchPaperStructs();
+          },
+          () => {
+            this.$notify({
+              type: "error",
+              message: "删除失败!",
+            });
+          }
+        );
+      });
+      this.loading = false;
+    },
+    //删除多条数据
+    deletePapers() {
+      this.$confirm("是否删除试卷结构?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "error",
+      }).then(() => {
+        this.loading = true;
+        var url = QUESTION_API + "/paperStruct/" + this.selectedIds;
+        this.$http
+          .delete(url)
+          .then(() => {
+            this.$notify({
+              type: "success",
+              message: "删除成功!",
+            });
+            this.searchPaperStructs();
+            this.selectedList = [];
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "删除失败!",
+            });
+            this.selectedList = [];
+          });
+      });
+      this.loading = false;
+    },
+
+    //新增
+    addPaperStruct() {
+      sessionStorage.setItem("paper_stucture", JSON.stringify(this.formSearch));
+      sessionStorage.setItem("paper_stucture_currentPage", this.currentPage);
+      this.$router.push({
+        path: "/questions/insert_paper_structure/add",
+      });
+    },
+
+    //分页跳转
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchPaperStructs();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchPaperStructs();
+    },
+    //查询所有课程
+    getCourses(query) {
+      query = query.trim();
+      if (query) {
+        if (!(query.indexOf("(") > -1 && query.indexOf(")") > -1)) {
+          this.courseLoading = true;
+          this.$http
+            .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+            .then((response) => {
+              this.courseList = response.data;
+              this.courseLoading = false;
+            });
+        }
+      } else {
+        this.courseList = [];
+      }
+    },
+    getType(val) {
+      if (val == "ENSEMBLE") {
+        return "宏观结构";
+      }
+      return "微观结构";
+    },
+    removeItem() {
+      sessionStorage.removeItem("paperStruct");
+    },
+    initVue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        sessionStorage.removeItem("paper_stucture");
+        sessionStorage.removeItem("paper_stucture_currentPage");
+        this.formSearch = {
+          name: "",
+          creator: "",
+          courseNo: "ALL",
+          type: "EXACT",
+          courseName: "",
+        };
+        this.currentPage = 1;
+      } else {
+        this.formSearch = JSON.parse(sessionStorage.getItem("paper_stucture"));
+        this.currentPage = parseInt(
+          sessionStorage.getItem("paper_stucture_currentPage")
+        );
+      }
+      if (this.formSearch.courseName) {
+        this.getCourses(this.formSearch.courseName);
+      }
+      this.searchPaperStructs();
+    },
+  },
+};
+</script>
+<style scoped src="../styles/Common.css"></style>

+ 321 - 0
src/modules/questions/views/PreviewPaper.vue

@@ -0,0 +1,321 @@
+<template>
+  <div v-loading="loading" class="paper">
+    <div class="center paper-top">
+      <small class="paper-title">
+        <el-button-group>
+          <el-button size="small">
+            <span class="titlefont">课程代码:</span>
+          </el-button>
+          <el-button size="small">
+            <span class="titlefont">{{ paper.course.code }}</span>
+          </el-button>
+        </el-button-group>
+      </small>
+      <small class="paper-title">
+        <el-button-group>
+          <el-button size="small">
+            <span class="titlefont">课程名称:</span>
+          </el-button>
+          <el-button size="small">
+            <span class="titlefont">{{ paper.course.name }}</span>
+          </el-button>
+        </el-button-group>
+      </small>
+      <small class="paper-title">
+        <el-button-group>
+          <el-button size="small">
+            <span class="titlefont">试卷名称:</span>
+          </el-button>
+          <input
+            v-model="paper.name"
+            placeholder="试卷名称"
+            class="paper-input"
+          />
+        </el-button-group>
+      </small>
+      <small class="paper-title">
+        <el-button-group>
+          <el-button v-if="isBack" size="small" class="titlefont" @click="back">
+            <i class="el-icon-arrow-left"></i>返回
+          </el-button>
+        </el-button-group>
+      </small>
+    </div>
+    <div class="center">
+      <div>
+        <br />
+        <h3 class="text-center">{{ paper.course.name }}&nbsp;试卷</h3>
+        <br />
+        <h4 class="text-center">(课程代码&nbsp;{{ paper.course.code }})</h4>
+        <br />
+        <div v-if="paper.examRemark">
+          <h1 class="text-left">考试说明:</h1>
+          <div v-html="paper.examRemark"></div>
+        </div>
+        <h1 class="text-left">本试卷满分{{ paper.totalScore }}分。</h1>
+        <br />
+      </div>
+    </div>
+    <div>
+      <div
+        v-for="(paperDetail, index) in paper.paperDetails"
+        :key="index"
+        class="mainQues"
+      >
+        <div class="mainQuesTitle">
+          <span>{{ paperDetail.cnNum }}</span>
+          <span>.</span>
+          <span>{{ paperDetail.name }}</span>
+          <span>
+            (本大题共{{ paperDetail.unitCount }}小题,满分{{
+              paperDetail.score
+            }}分)
+          </span>
+        </div>
+        <div
+          v-for="(paperDetailUnit, index2) in paperDetail.paperDetailUnits"
+          :key="index2"
+        >
+          <div>
+            <div class="quesBody">
+              <span class="ques-title">{{ paperDetailUnit.number }}.</span>
+              <span
+                v-question-audio
+                class="ques-body"
+                :hasAudio="paperDetailUnit.question.hasAudio"
+                :questionId="paperDetailUnit.question.id"
+                v-html="paperDetailUnit.question.quesBody"
+              ></span>
+              <span>({{ paperDetailUnit.score }}分)</span>
+            </div>
+            <div
+              v-for="(quesOption, index3) in paperDetailUnit.question
+                .quesOptions"
+              :key="index3"
+              class="quesOption"
+            >
+              <span class="ques-title"
+                >{{ index3 | optionOrderWordFilter }}.</span
+              >
+              <span
+                v-question-audio
+                class="ques-body"
+                :hasAudio="paperDetailUnit.question.hasAudio"
+                :questionId="paperDetailUnit.question.id"
+                v-html="quesOption.optionBody"
+              ></span>
+            </div>
+          </div>
+          <br />
+          <div
+            v-for="(subQuestion, index4) in paperDetailUnit.question
+              .subQuestions"
+            :key="index4"
+            class="subQues"
+          >
+            <div>
+              <div class="quesBody">
+                <span class="ques-title"
+                  >{{ subQuestion.quesParams.number }}.</span
+                >
+                <span v-html="subQuestion.quesBody"></span>
+                <span>({{ paperDetailUnit.subScoreList[index] }}分)</span>
+              </div>
+              <div
+                v-for="(subQuesOption, index5) in subQuestion.quesOptions"
+                :key="index5"
+                class="quesOption"
+              >
+                <span class="ques-title"
+                  >{{ index5 | optionOrderWordFilter }}.</span
+                >
+                <span v-html="subQuesOption.optionBody"></span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { mapState } from "vuex";
+export default {
+  data() {
+    return {
+      paperId: "",
+      paperDetailId: "",
+      editPaperDetailUnit: "",
+      quesDialog: false,
+      paperDatailDialog: false,
+      parentView: "",
+      paper: {
+        course: {
+          code: "",
+          name: "",
+        },
+      },
+      questionType: "",
+      reduplicateGroup: [],
+      singleRightAnswer: "", //接收单选答案
+      multipleRightAnswer: [], //接收多选答案
+      options: ["正确", "错误"],
+      loading: false,
+    };
+  },
+  computed: {
+    ...mapState({
+      user: (state) => state.user,
+    }),
+    isBack() {
+      var params = window.location.href.split("?")[1];
+      if (params && params.indexOf("isback") > -1) {
+        return true;
+      }
+      return false;
+    },
+  },
+  created() {
+    this.paperId = this.$route.params.paperId;
+    console.log("this.paperId:", this.paperId);
+    this.initPaper();
+  },
+  methods: {
+    //初始化试卷
+    initPaper() {
+      this.loading = true;
+      this.$httpWithoutAuth
+        .get(QUESTION_API + "/paper/" + this.paperId)
+        .then((response) => {
+          this.paper = response.data;
+          this.loading = false;
+        });
+    },
+    back() {
+      console.log("返回");
+      window.history.back();
+    },
+  },
+};
+</script>
+<style scoped>
+.red {
+  color: red;
+}
+.center {
+  margin: 0 auto 0 auto;
+  text-align: center;
+}
+h1 {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  font-weight: bold;
+}
+
+h3 {
+  font-size: 33px;
+  font-family: "微软雅黑", serif;
+  font-weight: bold;
+}
+h4 {
+  font-size: 21px;
+  font-family: "微软雅黑", serif;
+}
+span > p {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  text-align: left;
+  display: block;
+}
+.ques-title {
+  margin-right: 10px;
+}
+.mainQues {
+  margin-left: 3.5%;
+  text-align: left;
+}
+.mainQuesTitle {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  font-weight: bold;
+  margin-bottom: 20px;
+}
+.ques {
+  font-size: 16px;
+  font-family: "微软雅黑", serif;
+  text-align: left;
+  margin-bottom: 10px;
+  padding-left: 10px;
+  position: relative;
+}
+.subQues {
+  position: relative;
+  margin-bottom: 10px;
+  padding-left: 10px;
+}
+.ques.repeated {
+  /*border: 1px solid red;*/
+}
+.quesBody {
+  display: flex;
+}
+.quesOption {
+  margin-top: 10px;
+  display: flex;
+}
+.text-left {
+  margin-left: 3.5%;
+  text-align: left;
+}
+
+.paper-title {
+  margin-right: 10px;
+  margin-bottom: 10px;
+}
+.paper-top {
+  margin-top: 10px;
+}
+small {
+  font-size: 15px;
+  font-weight: bold;
+  margin-left: 10px;
+  margin-top: 20px;
+}
+.titlefont {
+  font-size: 14px;
+}
+.paper {
+  margin: 0 auto;
+  background-color: white;
+  padding-left: 10%;
+  padding-right: 10%;
+}
+.paper-input {
+  font-size: 14px;
+  margin-left: 7px;
+  margin-top: 5px;
+  width: 150px;
+  border-bottom: 1px solid black; /* 下划线效果 */
+  border-top: 0;
+  border-left: 0;
+  border-right: 0;
+  background-color: transparent; /* 背景色透明 */
+  outline: none;
+}
+.btnDiv {
+  height: 30px;
+  text-align: right;
+  position: relative;
+  right: 2px;
+  top: 1px;
+  visibility: hidden;
+}
+textarea {
+  width: 200px;
+}
+#app {
+  background-color: white !important;
+}
+</style>

+ 456 - 0
src/modules/questions/views/PropertyInfo.vue

@@ -0,0 +1,456 @@
+<template>
+  <section class="content">
+    <LinkTitlesCustom :current-paths="['属性结构']" />
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="courseProperty"
+        label-position="right"
+        label-width="90px"
+      >
+        <el-row :gutter="10">
+          <el-col :xs="7" :sm="7" :md="7" :lg="8">
+            <el-form-item label="属性名称" class="pull-left">
+              <el-input
+                v-model="courseProperty.name"
+                placeholder="请输入课程名称"
+                :disabled="true"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="7" :sm="7" :md="7" :lg="8">
+            <el-form-item label="课程名称">
+              <el-select
+                v-model="courseProperty.courseId"
+                class="form_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                :disabled="true"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseId"
+                  :label="item.courseInfo"
+                  :value="item.courseId"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :xs="7" :sm="7" :md="7" :lg="8">
+          <div style="margin-left: 20px">
+            <el-form-item>
+              <el-button type="primary" size="small" @click="insertParent"
+                ><i class="el-icon-plus"></i> 新增一级</el-button
+              >
+
+              <el-button
+                type="primary"
+                size="small"
+                :disabled="showSonButtton"
+                @click="insertSon"
+                ><i class="el-icon-plus"></i> 新增二级</el-button
+              >
+              <el-button
+                type="primary"
+                size="small"
+                :disabled="showButton"
+                @click="updateProperty"
+                ><i class="el-icon-edit"></i>编辑</el-button
+              >
+              <el-button
+                type="danger"
+                size="small"
+                :disabled="showButton"
+                @click="deleteProperty"
+                ><i class="el-icon-delete"></i> 删除</el-button
+              >
+              <el-button
+                type="info"
+                :disabled="showMoveButtton"
+                size="small"
+                @click="moveUp"
+                ><i class="el-icon-arrow-up"></i> 上移</el-button
+              >
+              <el-button
+                type="info"
+                :disabled="showMoveButtton"
+                size="small"
+                @click="moveDown"
+                ><i class="el-icon-arrow-down"></i> 下移</el-button
+              >
+              <el-button
+                type="primary"
+                icon="caret-left"
+                size="small"
+                @click="back"
+                ><i class="el-icon-arrow-left"></i> 返 回</el-button
+              >
+            </el-form-item>
+          </div>
+        </el-row>
+      </el-form>
+
+      <el-tree
+        :data="data"
+        node-key="id"
+        :props="defaultProps"
+        :default-expanded-keys="ids"
+        @node-click="handleNodeClick"
+      ></el-tree>
+    </div>
+    <el-dialog :title="title" :visible.sync="propertyDialog">
+      <el-form :model="propertyForm">
+        <el-row v-show="parentName">
+          <el-form-item label="一级名称">
+            <el-input
+              v-model="propertyForm.parentName"
+              auto-complete="off"
+              style="width: 220px"
+              @change="showTitle"
+            ></el-input>
+            <span v-show="showParentName" style="color: red">请输一级名称</span>
+          </el-form-item>
+        </el-row>
+        <el-row v-show="sonName">
+          <el-form-item label="二级名称">
+            <el-input
+              v-model="propertyForm.sonName"
+              auto-complete="off"
+              style="width: 220px"
+              @change="showTitle"
+            ></el-input>
+            <span v-show="showSonName" style="color: red">请输二级名称</span>
+          </el-form-item>
+        </el-row>
+        <el-row>
+          <el-form-item label="名称备注">
+            <el-input
+              v-model="propertyForm.remark"
+              auto-complete="off"
+              style="width: 220px"
+              @change="showTitle"
+            ></el-input>
+          </el-form-item>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="propertyDialog = false">取 消</el-button>
+        <el-button type="primary" @click="submit">确 定</el-button>
+      </div>
+    </el-dialog>
+  </section>
+</template>
+
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      courseProperty: {
+        name: "",
+        courseId: "",
+      },
+      courseList: [],
+      ids: [],
+      loading: false,
+      parentName: false,
+      sonName: false,
+      propertyDialog: false,
+      propertyForm: {
+        parentName: "",
+        sonName: "",
+        remark: "",
+      },
+      property: {
+        id: "",
+        name: "",
+        parentId: "",
+        number: "",
+        coursePropertyId: "",
+        remark: "",
+      },
+      showButton: true,
+      showSonButtton: true,
+      showParentName: false,
+      showSonName: false,
+      data: [],
+      defaultProps: {
+        children: "propertyList",
+        label: "name",
+      },
+      title: "新增属性",
+      showMoveButtton: true,
+    };
+  },
+  computed: {
+    courseInfoSelect() {
+      var courseList = [];
+      for (var i = 0; i < this.courseList.length; i++) {
+        var courseInfo = {
+          courseInfo:
+            this.courseList[i].name + "(" + this.courseList[i].code + ")",
+          courseId: this.courseList[i].id,
+        };
+        courseList.push(courseInfo);
+      }
+      return courseList;
+    },
+  },
+  created() {
+    this.coursePropertyId = this.$route.params.id;
+    this.searchProperty();
+  },
+  methods: {
+    //树形节点选中
+    handleNodeClick(object) {
+      //得到选中的节点
+      console.log(object);
+      this.showButton = false;
+      //判断选中节点,如果是父节点,可以新增二级
+      if (object.parentId == "0") {
+        this.showSonButtton = false;
+        this.showMoveButtton = false;
+      } else {
+        this.showSonButtton = true;
+        this.showMoveButtton = true;
+      }
+      this.property.id = object.id;
+      this.property.name = object.name;
+      this.property.parentId = object.parentId;
+      this.property.number = object.number;
+      this.property.coursePropertyId = object.coursePropertyId;
+      this.property.remark = object.remark;
+    },
+    //查询所有课程
+    getCourses(query) {
+      this.courseList = [];
+      if (query) {
+        this.courseLoading = true;
+        this.$http.get(CORE_API + "/course/" + query).then((response) => {
+          var courseBean = response.data;
+          this.courseList.push(courseBean);
+        });
+      } else {
+        this.courseList = [];
+      }
+    },
+    //查询
+    searchProperty() {
+      this.loading = true;
+      var coursePropertyStorge = sessionStorage.getItem("courseProperty");
+      if (typeof coursePropertyStorge == "string") {
+        this.courseProperty = JSON.parse(coursePropertyStorge);
+        this.getCourses(this.courseProperty.courseId);
+      }
+      this.$http
+        .get(QUESTION_API + "/property/all/" + this.coursePropertyId)
+        .then((response) => {
+          this.data = response.data;
+          for (var i = 0; i < this.data.length; i++) {
+            var property = this.data[i];
+            this.ids.push(property.id);
+          }
+          this.loading = false;
+        });
+    },
+    //新增一级
+    insertParent() {
+      this.title = "新增属性";
+      this.showParentName = false;
+      this.property = {
+        id: "",
+        name: "",
+        parentId: "0",
+        number: "",
+        coursePropertyId: this.coursePropertyId,
+        remark: "",
+      };
+      this.propertyForm.parentName = "";
+      this.propertyForm.remark = "";
+      this.sonName = false;
+      this.parentName = true;
+      this.propertyDialog = true;
+    },
+    //新增二级
+    insertSon() {
+      this.title = "新增属性";
+      this.showSonName = false;
+      //父对象id赋值
+      this.property.parentId = this.property.id;
+      this.property.id = "";
+      this.property.name = "";
+      this.property.remark = "";
+      this.property.coursePropertyId = this.coursePropertyId;
+      this.propertyForm.sonName = "";
+      this.propertyForm.remark = "";
+      this.parentName = false;
+      this.sonName = true;
+      this.propertyDialog = true;
+    },
+    //修改
+    updateProperty() {
+      this.title = "修改属性";
+      //判断是父节点还是子节点
+      if (this.property.parentId == "0") {
+        this.propertyForm.parentName = this.property.name;
+        this.propertyForm.remark = this.property.remark;
+        this.sonName = false;
+        this.parentName = true;
+      } else {
+        this.propertyForm.sonName = this.property.name;
+        this.propertyForm.remark = this.property.remark;
+        this.parentName = false;
+        this.sonName = true;
+      }
+      this.propertyDialog = true;
+    },
+    //保存
+    submit() {
+      if (this.property.parentId == "0") {
+        //非空判断
+        if (!this.propertyForm.parentName) {
+          this.showParentName = true;
+          return 0;
+        }
+        this.property.name = this.propertyForm.parentName;
+      } else {
+        //非空判断
+        if (!this.propertyForm.sonName) {
+          this.showSonName = true;
+          return 0;
+        }
+        this.property.name = this.propertyForm.sonName;
+      }
+      this.property.remark = this.propertyForm.remark;
+      if (this.property.id) {
+        this.$http
+          .put(QUESTION_API + "/property/save", this.property)
+          .then(() => {
+            this.$notify({
+              message: "修改成功",
+              type: "success",
+            });
+            this.propertyDialog = false;
+            this.searchProperty();
+          })
+          .catch((error) => {
+            this.$notify({
+              type: "error",
+              message: error.response.data.desc,
+            });
+          });
+      } else {
+        this.$http
+          .post(QUESTION_API + "/property/save", this.property)
+          .then(() => {
+            this.$notify({
+              message: "新增成功",
+              type: "success",
+            });
+            this.propertyDialog = false;
+            this.searchProperty();
+          })
+          .catch((error) => {
+            this.$notify({
+              type: "error",
+              message: error.response.data.desc,
+            });
+          });
+      }
+      this.showButton = true;
+      this.showSonButtton = true;
+    },
+    //删除
+    deleteProperty() {
+      this.$confirm("确认删除属性吗?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http
+          .delete(
+            QUESTION_API +
+              "/property/delete/" +
+              this.property.id +
+              "/" +
+              this.property.coursePropertyId
+          )
+          .then(() => {
+            this.$notify({
+              message: "删除成功",
+              type: "success",
+            });
+            this.searchProperty();
+          })
+          .catch(() => {
+            this.$notify({
+              type: "error",
+              message: "删除失败",
+            });
+          });
+      });
+      this.showButton = true;
+      this.showSonButtton = true;
+    },
+    //效验
+    showTitle() {
+      if (this.propertyForm.parentName) {
+        this.showParentName = false;
+      }
+      if (!this.propertyForm.parentName) {
+        this.showParentName = true;
+      }
+      if (this.propertyForm.sonName) {
+        this.showSonName = false;
+      }
+      if (!this.propertyForm.sonName) {
+        this.showSonName = true;
+      }
+    },
+    //返回
+    back() {
+      this.$router.push({
+        path: "/questions/course_property/1",
+      });
+    },
+    //上移
+    moveUp() {
+      this.$http
+        .put(QUESTION_API + "/property/moveUp", this.property)
+        .then(() => {
+          this.searchProperty();
+          this.showMoveButtton = true;
+        })
+        .catch(() => {
+          this.$notify({
+            type: "error",
+            message: "无法上移",
+          });
+          this.showMoveButtton = true;
+        });
+    },
+    //下移
+    moveDown() {
+      this.$http
+        .put(QUESTION_API + "/property/moveDown", this.property)
+        .then(() => {
+          this.searchProperty();
+          this.showMoveButtton = true;
+        })
+        .catch(() => {
+          this.$notify({
+            type: "error",
+            message: "无法下移",
+          });
+          this.showMoveButtton = true;
+        });
+    },
+  },
+};
+</script>

+ 772 - 0
src/modules/questions/views/Question.vue

@@ -0,0 +1,772 @@
+<template>
+  <section class="content">
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="70px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="课程名称">
+              <el-select
+                v-model="formSearch.courseNo"
+                class="search_width"
+                filterable
+                :remote-method="getCourses"
+                remote
+                clearable
+                placeholder="请选择"
+                size="small"
+                @change="initCourseProperty"
+                @focus="(e) => getCourses(e.target.value)"
+              >
+                <el-option
+                  v-for="item in courseInfoSelect"
+                  :key="item.courseNo"
+                  :label="item.courseInfo"
+                  :value="item.courseNo"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="层次">
+              <el-select
+                v-model="formSearch.courseLevel"
+                class="search_width"
+                filterable
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in levelList"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="题型">
+              <el-select
+                v-model="formSearch.questionType"
+                class="search_width"
+                filterable
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in questionTypes"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="属性名">
+              <el-select
+                v-model="formSearch.coursePropertyName"
+                class="search_width"
+                filterable
+                clearable
+                :disabled="updatePorperty"
+                placeholder="请选择"
+                size="small"
+                @change="searchFirst"
+              >
+                <el-option
+                  v-for="item in coursePropertyList"
+                  :key="item.name"
+                  :label="item.name"
+                  :value="item.name"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <!-- created by wwh -->
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="一级属性">
+              <el-select
+                v-model="formSearch.firstPropertyId"
+                class="search_width"
+                filterable
+                clearable
+                :disabled="updateFirst"
+                placeholder="请选择"
+                size="small"
+                @change="searchSecond"
+              >
+                <el-option
+                  v-for="item in firstPropertyList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="二级属性">
+              <el-select
+                v-model="formSearch.secondPropertyId"
+                class="search_width"
+                filterable
+                clearable
+                :disabled="updateSecond"
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in secondPropertyList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="公开度">
+              <el-select
+                v-model="formSearch.publicity"
+                class="search_width"
+                filterable
+                clearable
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in publicityList"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button size="small" type="primary" @click="searchFrom"
+                ><i class="el-icon-search"></i> 查询</el-button
+              >
+              <el-button size="small" @click="resetForm"
+                ><i class="el-icon-refresh"></i> 重 置</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div
+        style="width: 100%; border-bottom: 1px solid #ddd; margin: 10px 0"
+      ></div>
+      <!-- 页面列表 -->
+      <el-table v-loading="loading" :data="tableData" border>
+        <el-table-column label="课程" width="180">
+          <template slot-scope="scope">
+            <span>{{ scope.row.course.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="层次" width="100">
+          <template slot-scope="scope">
+            <span>{{ getLevel(scope.row.course.level) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题型" width="100">
+          <template slot-scope="scope">
+            <span>{{ scope.row.questionType | questionType }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题干">
+          <template slot-scope="scope">
+            <span
+              class="row_quesBody"
+              @click="prevViewQues(scope.row)"
+              v-html="scope.row.quesBody"
+            >
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="175">
+          <template slot-scope="scope">
+            <div class="operate_left">
+              <el-button
+                v-if="scope.row.questionType !== 'NESTED_ANSWER_QUESTION'"
+                size="mini"
+                type="primary"
+                plain
+                @click="updateRow(scope.row)"
+              >
+                <i class="el-icon-edit"></i>编辑
+              </el-button>
+              <el-button
+                size="mini"
+                type="danger"
+                @click="deleteRow(scope.row)"
+              >
+                <i class="el-icon-delete"></i> 删除
+              </el-button>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页栏 -->
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+
+    <div class="text-left">
+      <el-dialog
+        title="试题预览"
+        :visible.sync="quesDialog"
+        @close="closeQuesDialog"
+      >
+        <el-form :model="quesModel" label-position="right" label-width="80px">
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="题型">
+                <el-select
+                  v-model="quesModel.questionType"
+                  :disabled="true"
+                  placeholder="题型"
+                >
+                  <el-option
+                    v-for="item in questionTypes"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="10">
+            <el-col :xs="30" :sm="30" :md="30" :lg="30">
+              <el-form-item label="题干">
+                <span class="ques-body" v-html="quesModel.quesBody"></span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!-- 非套题 -->
+          <div v-if="quesModel.questionType !== 'NESTED_ANSWER_QUESTION'">
+            <el-row
+              v-for="(quesOption, index) in quesModel.quesOptions"
+              :key="index"
+              :gutter="10"
+            >
+              <el-col :xs="30" :sm="30" :md="30" :lg="30">
+                <el-form-item :label="index | optionOrderWordFilter">
+                  <span class="ques-body" v-html="quesOption.optionBody"></span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </div>
+          <!-- 套题 -->
+          <div v-if="quesModel.questionType === 'NESTED_ANSWER_QUESTION'">
+            <el-row
+              v-for="subQuestionModel in quesModel.subQuestions"
+              :key="subQuestionModel"
+              :gutter="10"
+            >
+              <el-col :xs="30" :sm="30" :md="30" :lg="30">
+                <el-row>
+                  <el-form-item label="题目">
+                    <span v-html="subQuestionModel.quesBody"></span>
+                  </el-form-item>
+                </el-row>
+                <el-row
+                  v-for="(
+                    subQuesOption, subIndex
+                  ) in subQuestionModel.quesOptions"
+                  :key="subIndex"
+                  :gutter="10"
+                >
+                  <el-col :xs="30" :sm="30" :md="30" :lg="30">
+                    <el-form-item :label="subIndex | optionOrderWordFilter">
+                      <span v-html="subQuesOption.optionBody"></span>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-form-item label="答案">
+                    <span v-html="subQuestionModel.quesAnswer"></span>
+                  </el-form-item>
+                </el-row>
+              </el-col>
+            </el-row>
+          </div>
+          <!-- 非套题答案 -->
+          <div v-if="quesModel.questionType !== 'NESTED_ANSWER_QUESTION'">
+            <el-form-item label="答案">
+              <span v-html="answer"></span>
+            </el-form-item>
+          </div>
+        </el-form>
+      </el-dialog>
+    </div>
+    <el-dialog title="提示" :visible.sync="deleteDialogVisible">
+      <span>{{ deleteInfo }}</span>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="deleteDialogVisible = false"
+          >确 定</el-button
+        >
+      </span>
+    </el-dialog>
+  </section>
+</template>
+<script>
+import { CORE_API, QUESTION_API } from "@/constants/constants";
+import { QUESTION_TYPES, LEVEL_TYPE } from "../constants/constants";
+export default {
+  data() {
+    return {
+      formSearch: {
+        questionType: "",
+        courseNo: "",
+        courseLevel: "",
+        courseName: "",
+        publicity: "",
+        coursePropertyName: "",
+        firstPropertyId: "",
+        secondPropertyId: "",
+      },
+      courseList: [], //课程list
+      levelList: LEVEL_TYPE, //层次list
+      questionTypeList: [], //题型
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      quesModel: {},
+      questionTypes: QUESTION_TYPES,
+      quesDialog: false,
+      loading: false,
+      deleteDialogVisible: false,
+      deleteInfo: "",
+      singleRightAnswer: "", //接收单选答案
+      multipleRightAnswer: [], //接收多选答案
+      publicityList: [
+        { label: "公开", value: true },
+        { label: "非公开", value: false },
+      ],
+      coursePropertyList: [], //课程属性集合
+      firstPropertyList: [], //一级属性集合
+      secondPropertyList: [], //二级属性集合
+      isClear: 0,
+    };
+  },
+  computed: {
+    courseInfoSelect() {
+      var courseList = [];
+      for (let course of this.courseList) {
+        var courseInfo = course.name + "(" + course.code + ")";
+        var courseNo = course.code;
+        courseList.push({ courseNo: courseNo, courseInfo: courseInfo });
+      }
+      return courseList;
+    },
+    answer() {
+      if (this.quesModel.questionType == "SINGLE_ANSWER_QUESTION") {
+        return this.singleRightAnswer;
+      } else if (this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION") {
+        var obj = this.multipleRightAnswer;
+        return obj.sort().toString();
+      } else {
+        return this.quesModel.quesAnswer;
+      }
+    },
+    updateFirst() {
+      if (this.formSearch.coursePropertyName) {
+        return false;
+      }
+      return true;
+    },
+    updateSecond() {
+      if (this.formSearch.firstPropertyId) {
+        return false;
+      }
+      return true;
+    },
+    updatePorperty() {
+      if (this.formSearch.courseNo) {
+        return false;
+      }
+      return true;
+    },
+  },
+  watch: {
+    $route: "initVue",
+  },
+  //钩子函数
+  created() {
+    this.initVue();
+  },
+  methods: {
+    resetForm() {
+      this.formSearch = {
+        questionType: "",
+        courseNo: "",
+        courseLevel: "",
+        courseName: "",
+        publicity: "",
+        coursePropertyName: "",
+        firstPropertyId: "",
+        secondPropertyId: "",
+      };
+    },
+    getLevel(level) {
+      if (level == "ZSB") {
+        return "专升本";
+      } else if (level == "GQZ") {
+        return "高起专";
+      } else if (level == "GQB") {
+        return "高起本";
+      } else if (level == "ALL") {
+        return "不限";
+      } else {
+        return "";
+      }
+    },
+    //查询列表
+    searchFrom() {
+      this.currentPage = 1;
+      this.searchQues();
+    },
+    searchQues() {
+      var pageNo = Number(this.currentPage | 1);
+      this.currentPage = 1;
+      this.tableData = [];
+      var url = QUESTION_API + "/question/" + pageNo + "/" + this.pageSize;
+      this.loading = true;
+      this.$http.get(url, { params: this.formSearch }).then((response) => {
+        this.tableData = response.data.content;
+        this.total = response.data.totalElements;
+        this.currentPage = Number(pageNo);
+        this.loading = false;
+      });
+    },
+    //分页
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchQues();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchQues();
+    },
+    getCourseName(courseNo) {
+      for (let course of this.courseList) {
+        if (course.code == courseNo) {
+          this.formSearch.courseName = course.name;
+        }
+      }
+    },
+    //修改
+    updateRow(row) {
+      this.getCourseName(this.formSearch.courseNo);
+      //选择题:单选、多选
+      if (
+        row.questionType === "SINGLE_ANSWER_QUESTION" ||
+        row.questionType === "MULTIPLE_ANSWER_QUESTION"
+      ) {
+        this.$router.push({
+          path: "/questions/edit_select_question/" + row.id,
+        });
+      }
+      //判断、填空、问答
+      if (
+        row.questionType === "BOOL_ANSWER_QUESTION" ||
+        row.questionType === "FILL_BLANK_QUESTION" ||
+        row.questionType === "TEXT_ANSWER_QUESTION"
+      ) {
+        this.$router.push({
+          path: "/questions/edit_other_question/" + row.id,
+        });
+      }
+      sessionStorage.setItem("question", JSON.stringify(this.formSearch));
+      sessionStorage.setItem("question_currentPage", this.currentPage);
+    },
+    //删除
+    deleteRow(row) {
+      this.$confirm("确认删除试题吗?", "提示", {
+        type: "warning",
+      }).then(() => {
+        this.loading = true;
+        this.$http
+          .delete(QUESTION_API + "/paper/deleteQuestion/" + row.id)
+          .then(
+            (response) => {
+              if (response.data.length > 0) {
+                this.loading = false;
+                this.deleteInfo =
+                  "该试题被试卷:" +
+                  response.data.join(" , ") +
+                  "使用,不能删除";
+                this.deleteDialogVisible = true;
+              } else {
+                this.$notify({
+                  message: "删除成功",
+                  type: "success",
+                });
+                this.searchQues();
+              }
+            },
+            () => {
+              this.$notify({
+                message: "删除失败",
+                type: "error",
+              });
+              this.loading = false;
+            }
+          );
+      });
+    },
+    //打开弹窗
+    openQuesDialog() {
+      this.quesDialog = true;
+    },
+    //关闭弹窗
+    closeQuesDialog() {
+      this.quesDialog = false;
+      this.quesModel = {};
+    },
+    //预览
+    prevViewQues(row) {
+      this.quesModel = row;
+      this.disposeSelectAnswer();
+      this.openQuesDialog();
+    },
+    //查询所有课程
+    getCourses(query) {
+      this.$http
+        .get(CORE_API + "/course/query?name=" + query + "&enable=true")
+        .then((response) => {
+          this.courseList = response.data;
+          if (this.formSearch.courseNo) {
+            this.$http
+              .get(
+                QUESTION_API +
+                  "/courseProperty/enable/" +
+                  this.formSearch.courseNo
+              )
+              .then((response) => {
+                this.coursePropertyList = response.data;
+                //查询一级属性
+                this.getFirst();
+              });
+          } else {
+            this.coursePropertyList = [];
+          }
+        });
+    },
+    //重置查询表单
+    resetSearchForm() {
+      this.formSearch = {
+        questionType: "",
+        courseNo: "",
+      };
+    },
+    /*处理选择题答案显示,处理套题下选择题答案显示*/
+    disposeSelectAnswer() {
+      this.singleRightAnswer = "";
+      this.multipleRightAnswer = [];
+      //处理选择题答案显示
+      if (
+        this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" ||
+        this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION"
+      ) {
+        for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+          var option = this.quesModel.quesOptions[i];
+          var orderNum = String.fromCharCode(65 + i);
+          if (
+            this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" &&
+            option.isCorrect == 1
+          ) {
+            this.singleRightAnswer = orderNum;
+          }
+          if (
+            this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION" &&
+            option.isCorrect == 1
+          ) {
+            this.multipleRightAnswer.push(orderNum);
+          }
+        }
+      }
+      //处理套题下选择题答案显示
+      if (this.quesModel.questionType == "NESTED_ANSWER_QUESTION") {
+        var subQuestions = this.quesModel.subQuestions;
+        for (var ii = 0; ii < subQuestions.length; ii++) {
+          var subQuestion = subQuestions[ii];
+          if (subQuestion.questionType == "SINGLE_ANSWER_QUESTION") {
+            for (var j = 0; j < subQuestion.quesOptions.length; j++) {
+              var option_j = subQuestion.quesOptions[j];
+              var orderNum_j = String.fromCharCode(65 + j);
+              if (option_j.isCorrect == 1) {
+                subQuestion["quesAnswer"] = orderNum_j;
+              }
+            }
+          }
+          if (subQuestion.questionType == "MULTIPLE_ANSWER_QUESTION") {
+            var subQuestionMultipleRightAnswer = [];
+            for (var k = 0; k < subQuestion.quesOptions.length; k++) {
+              var option2 = subQuestion.quesOptions[k];
+              var orderNum2 = String.fromCharCode(65 + k);
+              if (option2.isCorrect == 1) {
+                subQuestionMultipleRightAnswer.push(orderNum2);
+              }
+            }
+            subQuestion[
+              "quesAnswer"
+            ] = subQuestionMultipleRightAnswer.sort().toString();
+          }
+        }
+      }
+    },
+    //查询所有课程属性名
+    initCourseProperty() {
+      this.formSearch.coursePropertyName = "";
+      this.formSearch.firstPropertyId = "";
+      this.formSearch.secondPropertyId = "";
+      if (this.formSearch.courseNo) {
+        this.$http
+          .get(
+            QUESTION_API + "/courseProperty/enable/" + this.formSearch.courseNo
+          )
+          .then((response) => {
+            this.coursePropertyList = response.data;
+          });
+      } else {
+        this.coursePropertyList = [];
+      }
+    },
+    //查询一级属性
+    searchFirst() {
+      if (this.formSearch.coursePropertyName) {
+        this.formSearch.firstPropertyId = "";
+        this.formSearch.secondPropertyId = "";
+        for (let courseProperty of this.coursePropertyList) {
+          if (courseProperty.name == this.formSearch.coursePropertyName) {
+            this.$http
+              .get(QUESTION_API + "/property/first/" + courseProperty.id)
+              .then((response) => {
+                this.firstPropertyList = response.data;
+              });
+          }
+        }
+      }
+    },
+    //查询二级属性
+    searchSecond() {
+      if (this.formSearch.firstPropertyId) {
+        this.formSearch.secondPropertyId = "";
+        this.$http
+          .get(
+            QUESTION_API + "/property/second/" + this.formSearch.firstPropertyId
+          )
+          .then((response) => {
+            this.secondPropertyList = response.data;
+          });
+      }
+    },
+    getFirst() {
+      if (this.formSearch.coursePropertyName) {
+        for (let courseProperty of this.coursePropertyList) {
+          if (courseProperty.name == this.formSearch.coursePropertyName) {
+            this.$http
+              .get(QUESTION_API + "/property/first/" + courseProperty.id)
+              .then((response) => {
+                this.firstPropertyList = response.data;
+                //查询二级
+                this.getSecond();
+              });
+          }
+        }
+      }
+    },
+    getSecond() {
+      if (this.formSearch.firstPropertyId) {
+        this.$http
+          .get(
+            QUESTION_API + "/property/second/" + this.formSearch.firstPropertyId
+          )
+          .then((response) => {
+            this.secondPropertyList = response.data;
+          });
+      }
+    },
+    initVue() {
+      this.isClear = this.$route.params.isClear;
+      if (this.isClear == 0 || !this.isClear) {
+        sessionStorage.removeItem("question");
+        sessionStorage.removeItem("question_currentPage");
+        this.formSearch = {
+          questionType: "",
+          courseNo: "",
+          courseLevel: "",
+          courseName: "",
+          publicity: "",
+          coursePropertyName: "",
+          firstPropertyId: "",
+          secondPropertyId: "",
+        };
+        this.currentPage = 1;
+      } else {
+        this.formSearch = JSON.parse(sessionStorage.getItem("question"));
+        this.currentPage = parseInt(
+          sessionStorage.getItem("question_currentPage")
+        );
+      }
+      if (this.formSearch && this.formSearch.courseName) {
+        this.getCourses(this.formSearch.courseName);
+      } else {
+        this.formSearch = {
+          questionType: "",
+          courseNo: "",
+          courseLevel: "",
+          courseName: "",
+          publicity: "",
+          coursePropertyName: "",
+          firstPropertyId: "",
+          secondPropertyId: "",
+        };
+      }
+      this.searchQues();
+    },
+  },
+};
+</script>
+<style scoped>
+.select_width {
+  width: 150px;
+}
+.row_quesBody >>> p {
+  text-overflow: ellipsis;
+  overflow: hidden;
+}
+</style>
+<style scoped src="../styles/Common.css"></style>

+ 533 - 0
src/modules/questions/views/School.vue

@@ -0,0 +1,533 @@
+<template>
+  <section class="content">
+    <div
+      v-loading.fullscreen="loading"
+      class="box-body"
+      element-loading-text="请稍后..."
+    >
+      <!-- 表单 -->
+      <el-form :inline="true" :model="formSearch">
+        <el-form-item label="学校名称">
+          <el-input v-model="formSearch.name" placeholder="请输入学校名称" />
+        </el-form-item>
+        <el-form-item label="学校代码">
+          <el-input v-model="formSearch.code" placeholder="请输入学校代码" />
+        </el-form-item>
+        <el-form-item label="学校域名">
+          <el-input
+            v-model="formSearch.domainName"
+            placeholder="请输入学校域名"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button
+            size="small"
+            type="primary"
+            icon="el-icon-search"
+            @click="handleSearchBtn"
+            >查询</el-button
+          >
+          <el-button
+            size="small"
+            icon="el-icon-refresh"
+            @click="resetSearchForm"
+            >重置</el-button
+          >
+          <el-button
+            size="small"
+            type="primary"
+            icon="el-icon-plus"
+            @click="insert"
+            >新增</el-button
+          >
+        </el-form-item>
+      </el-form>
+
+      <div class="block-seperator"></div>
+
+      <span>操作:</span>
+      <el-button
+        size="small"
+        type="success"
+        :disabled="noBatchSelected"
+        icon="el-icon-check"
+        @click="enableOrg"
+        >启用</el-button
+      >
+      <el-button
+        size="small"
+        type="danger"
+        :disabled="noBatchSelected"
+        icon="el-icon-close"
+        @click="disableOrg"
+        >禁用</el-button
+      >
+
+      <div style="width: 100%; margin-bottom: 10px"></div>
+
+      <!-- 添加或修改学校弹出框 -->
+      <el-dialog
+        title="学校信息"
+        width="500px"
+        :visible.sync="schoolDialog"
+        @close="dialogBeforeClose"
+      >
+        <el-form
+          ref="schoolForm"
+          :inline="true"
+          inline-message
+          :model="schoolForm"
+          :rules="rules"
+          label-position="right"
+          label-width="90px"
+          class="editSchoolForm"
+        >
+          <el-row>
+            <el-form-item label="学校代码" label-width="120px" prop="code">
+              <el-input
+                v-model="schoolForm.code"
+                class="pull_length"
+                auto-complete="off"
+                placeholder="学校域名"
+                :disabled="null != schoolForm.id"
+              />
+            </el-form-item>
+          </el-row>
+          <el-row>
+            <el-form-item label="学校名称" label-width="120px" prop="name">
+              <el-input
+                v-model="schoolForm.name"
+                class="pull_length"
+                auto-complete="off"
+                placeholder="学校名称"
+              />
+            </el-form-item>
+          </el-row>
+          <el-row>
+            <el-form-item
+              label="学校域名"
+              label-width="120px"
+              prop="domainName"
+            >
+              <el-input
+                v-model="schoolForm.domainName"
+                class="pull_length"
+                auto-complete="off"
+                placeholder="学校域名"
+              />
+            </el-form-item>
+          </el-row>
+          <el-row>
+            <el-form-item label="负责人" label-width="120px" prop="contacts">
+              <el-input
+                v-model="schoolForm.contacts"
+                class="pull_length"
+                auto-complete="off"
+                placeholder="负责人"
+              />
+            </el-form-item>
+          </el-row>
+          <el-row>
+            <el-form-item label="联系方式" label-width="120px" prop="telephone">
+              <el-input
+                v-model="schoolForm.telephone"
+                class="pull_length"
+                auto-complete="off"
+                placeholder="联系方式"
+              />
+            </el-form-item>
+          </el-row>
+          <el-row>
+            <el-form-item label="状态" label-width="120px" prop="enable">
+              <el-radio-group v-model="schoolForm.enable" class="pull_right_sm">
+                <el-radio label="true">启用</el-radio>
+                <el-radio label="false">禁用</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-row>
+          <el-row class="d-flex justify-content-center">
+            <el-button type="primary" @click="submitForm">保 存</el-button>
+            <el-button @click="schoolDialog = false">取 消</el-button>
+          </el-row>
+        </el-form>
+      </el-dialog>
+
+      <!-- 页面列表 -->
+      <el-table
+        :data="tableData"
+        border
+        resizable
+        stripe
+        @selection-change="selectChange"
+      >
+        <el-table-column type="selection" width="40" />
+        <el-table-column prop="id" label="ID" width="60" />
+        <el-table-column label="学校名称">
+          <template slot-scope="scope">
+            <el-popover trigger="hover" placement="left">
+              <div style="font-size: 18px; font-family: 新宋体">
+                <tr>
+                  <td style="color: green">学校名称</td>
+                  <td style="color: purple; padding-left: 20px">
+                    {{ scope.row.name }}
+                  </td>
+                </tr>
+                <tr>
+                  <td style="color: green">学校代码</td>
+                  <td style="color: purple; padding-left: 20px">
+                    {{ scope.row.code }}
+                  </td>
+                </tr>
+                <tr>
+                  <td style="color: green">负责人</td>
+                  <td style="color: purple; padding-left: 20px">
+                    {{ scope.row.contacts }}
+                  </td>
+                </tr>
+                <tr>
+                  <td style="color: green">联系方式</td>
+                  <td style="color: purple; padding-left: 20px">
+                    {{ scope.row.telephone }}
+                  </td>
+                </tr>
+              </div>
+
+              <div slot="reference" class="name-wrapper">
+                <span>{{ scope.row.name }}</span>
+              </div>
+            </el-popover>
+          </template>
+        </el-table-column>
+        <el-table-column prop="code" width="190" label="学校代码" />
+        <el-table-column prop="domainName" width="190" label="学校域名" />
+        <el-table-column
+          sortable
+          prop="updateTime"
+          width="170"
+          label="更新时间"
+        />
+        <el-table-column width="50" label="状态">
+          <span slot-scope="scope">
+            <span v-if="scope.row.enable">
+              <el-tooltip
+                class="item"
+                effect="dark"
+                content="启用"
+                placement="left"
+              >
+                <i class="el-icon-success" style="color: green"></i>
+              </el-tooltip>
+            </span>
+            <span v-else>
+              <el-tooltip
+                class="item"
+                effect="dark"
+                content="禁用"
+                placement="left"
+              >
+                <i class="el-icon-error" style="color: red"></i>
+              </el-tooltip>
+            </span>
+          </span>
+        </el-table-column>
+        <el-table-column :context="_self" width="210" prop label="操作">
+          <div slot-scope="scope">
+            <el-button
+              size="mini"
+              type="primary"
+              plain
+              @click="edit(scope.row)"
+            >
+              <i class="el-icon-edit"></i> 编辑
+            </el-button>
+          </div>
+        </el-table-column>
+      </el-table>
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100, 200, 300]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        />
+      </div>
+    </div>
+  </section>
+</template>
+
+<script>
+import { QUESTION_API } from "@/constants/constants.js";
+import { mapState } from "vuex";
+
+export default {
+  name: "School",
+  data() {
+    return {
+      formSearch: {
+        code: "",
+        name: "",
+        domainName: "",
+      },
+      schoolForm: {
+        id: null,
+        name: "",
+        code: "",
+        domainName: "",
+        contacts: "",
+        telephone: "",
+        enable: "true",
+        remark: "",
+      },
+      schoolDialog: false,
+      logoDialog: false,
+      curSchool: {
+        name: null,
+        code: null,
+      },
+      uploadAction: "",
+      uploadHeaders: {},
+      uploadData: {},
+      fileList: [],
+      selectedOrgIds: [],
+      loading: true,
+      tableData: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      imp: "",
+      rules: {
+        code: [{ required: true, message: "请输入学校代码", trigger: "blur" }],
+        domainName: [
+          { required: true, message: "请输入学校域名", trigger: "blur" },
+        ],
+        name: [{ required: true, message: "请输入学校名称", trigger: "blur" }],
+        contacts: [
+          { required: true, message: "请输入负责人", trigger: "blur" },
+        ],
+        telephone: [
+          { required: true, message: "请输入联系方式", trigger: "blur" },
+        ],
+        enable: [{ required: true, message: "请选择状态", trigger: "change" }],
+      },
+    };
+  },
+  computed: {
+    ...mapState({ user: (state) => state.user }),
+    noBatchSelected() {
+      return this.selectedOrgIds.length === 0;
+    },
+    orgIds() {
+      var orgIds = "";
+      for (let orgId of this.selectedOrgIds) {
+        if (!orgIds) {
+          orgIds += orgId;
+        } else {
+          orgIds += "," + orgId;
+        }
+      }
+      return orgIds;
+    },
+  },
+  //初始化查询
+  created() {
+    this.searchForm();
+
+    this.uploadHeaders = {
+      key: this.user.key,
+      token: this.user.token,
+    };
+  },
+  methods: {
+    resetSearchForm() {
+      this.formSearch.code = "";
+      this.formSearch.name = "";
+      this.formSearch.domainName = "";
+    },
+    dialogBeforeClose() {
+      this.$refs.schoolForm.clearValidate();
+    },
+    handleSizeChange(val) {
+      this.pageSize = val;
+      this.currentPage = 1;
+      this.searchForm();
+    },
+    getTag(status) {
+      if (status == true) {
+        return "success";
+      } else if (status == false) {
+        return "danger";
+      }
+      return status;
+    },
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchForm();
+    },
+    handleSearchBtn() {
+      this.currentPage = 1;
+      this.searchForm();
+    },
+    //查询
+    searchForm() {
+      let searchLock = true;
+      setTimeout(() => {
+        if (searchLock) {
+          this.loading = true;
+        }
+      }, 500);
+      var param = new URLSearchParams(this.formSearch);
+      var url =
+        QUESTION_API +
+        "/org/rootOrgPage/" +
+        (this.currentPage - 1) +
+        "/" +
+        this.pageSize +
+        "?" +
+        param;
+      this.$httpWithMsg
+        .get(url)
+        .then((response) => {
+          this.tableData = response.data.list;
+          this.total = response.data.total;
+          this.loading = false;
+        })
+        .finally(() => {
+          searchLock = false;
+          this.loading = false;
+        });
+    },
+    //启用
+    enableOrg() {
+      if (this.selectedOrgIds.length === 0) {
+        this.$notify({
+          type: "warning",
+          message: "请选择要启用的机构",
+        });
+      } else {
+        this.$confirm("是否启用这些机构?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          var url = QUESTION_API + "/org/enable/" + this.orgIds;
+          this.$httpWithMsg.put(url).then(() => {
+            this.$notify({
+              type: "success",
+              message: "启用成功!",
+            });
+            this.searchForm();
+          });
+        });
+      }
+    },
+    disableOrg() {
+      if (this.selectedOrgIds.length === 0) {
+        this.$notify({
+          type: "warning",
+          message: "请选择要禁用的机构",
+        });
+      } else {
+        this.$confirm("是否禁用这些机构?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          var url = QUESTION_API + "/org/disable/" + this.orgIds;
+          this.$$httpWithMsg.put(url, {}).then(() => {
+            this.$notify({
+              type: "success",
+              message: "禁用成功!",
+            });
+            this.searchForm();
+          });
+        });
+      }
+    },
+    selectChange(row) {
+      this.selectedOrgIds = [];
+      row.forEach((element) => {
+        this.selectedOrgIds.push(element.id);
+      });
+      console.log(this.selectedOrgIds);
+    },
+    //重置
+    resetForm() {
+      this.$refs.schoolForm.resetFields();
+    },
+    //提交
+    submitForm() {
+      this.$refs.schoolForm.validate((valid) => {
+        if (valid) {
+          if (null != this.schoolForm.id) {
+            //修改
+            url = QUESTION_API + "/org/updateRootOrg";
+            this.$httpWithMsg.put(url, this.schoolForm).then(() => {
+              this.$notify({
+                type: "success",
+                message: "修改成功!",
+              });
+              this.searchForm();
+              this.schoolDialog = false;
+            });
+          } else {
+            //新增
+            var url = QUESTION_API + "/org/addRootOrg";
+            this.$httpWithMsg.post(url, this.schoolForm).then(() => {
+              this.$notify({
+                type: "success",
+                message: "新增成功!",
+              });
+              this.searchForm();
+              this.schoolDialog = false;
+            });
+          }
+        } else {
+          console.log("error submit!");
+          return false;
+        }
+      });
+    },
+    //新增
+    insert() {
+      this.schoolForm.id = null;
+      this.schoolForm.name = "";
+      this.schoolForm.code = "";
+      this.schoolForm.domainName = "";
+      this.schoolForm.telephone = "";
+      this.schoolForm.contacts = "";
+      this.schoolForm.enable = "true";
+
+      this.schoolDialog = true;
+    },
+    //修改
+    edit(row) {
+      this.schoolForm.id = row.id;
+      this.schoolForm.name = row.name;
+      this.schoolForm.code = row.code;
+      this.schoolForm.domainName = row.domainName;
+      this.schoolForm.telephone = row.telephone;
+      this.schoolForm.contacts = row.contacts;
+      this.schoolForm.enable = row.enable ? "true" : "false";
+
+      this.schoolDialog = true;
+    },
+  },
+};
+</script>
+
+<style scoped>
+.page {
+  margin-top: 10px;
+}
+
+.el-upload {
+  width: 80px;
+}
+
+.editSchoolForm .pull_length {
+  width: 200px;
+}
+</style>

+ 421 - 0
src/modules/questions/views/SelectQuestion.vue

@@ -0,0 +1,421 @@
+<template>
+  <section class="content">
+    <LinkTitlesCustom :current-paths="['添加试题']" />
+    <!-- 正文信息 -->
+    <div class="box-body">
+      <el-form
+        :inline="true"
+        :model="formSearch"
+        label-position="right"
+        label-width="90px"
+      >
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="课程">
+              <el-select
+                v-model="course"
+                class="search_width"
+                :disabled="true"
+                placeholder="请选择"
+                size="small"
+              >
+                <el-option
+                  v-for="item in courseList"
+                  :key="item.value"
+                  :label="item.name"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="题型">
+              <el-select
+                v-model="formSearch.questionType"
+                class="search_width"
+                placeholder="请选择题型"
+                size="small"
+                @change="searchQuestionPaper"
+              >
+                <el-option
+                  v-for="item in questionTypes"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                >
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="题干">
+              <el-input
+                v-model="formSearch.quesBody"
+                class="search_width"
+                placeholder="请输入题干"
+                size="small"
+                @blur="searchQuestionPaper"
+              >
+              </el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <div class="search_down">
+              <el-button
+                size="small"
+                type="primary"
+                icon="el-icon-circle-check"
+                @click="submitQues()"
+                >确定</el-button
+              >
+              <el-button
+                size="small"
+                type="primary"
+                icon="el-icon-arrow-left"
+                @click="back()"
+                >返回</el-button
+              >
+            </div>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <!-- 页面列表 -->
+      <el-table
+        :data="tableData"
+        border
+        style="width: 100%"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="45"> </el-table-column>
+        <el-table-column label="课程">
+          <template slot-scope="scope">
+            <span v-if="scope.row.course != null">{{
+              scope.row.course.name
+            }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题型">
+          <template slot-scope="scope">
+            <span>{{ scope.row.questionType | questionType }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="题干">
+          <template slot-scope="scope">
+            <span class="small">
+              <a
+                href="javascript:;"
+                @click="prevViewQues(scope.row)"
+                v-html="scope.row.quesBody"
+              >
+              </a>
+            </span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!--分页栏-->
+      <div class="page pull-right">
+        <el-pagination
+          :current-page="currentPage"
+          :page-size="pageSize"
+          layout="total, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+        >
+        </el-pagination>
+      </div>
+    </div>
+
+    <div class="text-left">
+      <el-dialog
+        v-loading.body="dialogLoading"
+        title="试题预览"
+        :visible.sync="quesDialog"
+        @close="closeQuesDialog"
+      >
+        <el-form :model="quesModel" label-position="right" label-width="80px">
+          <el-row :gutter="10">
+            <el-col :xs="10" :sm="10" :md="10" :lg="10">
+              <el-form-item label="题型">
+                <el-select
+                  v-model="quesModel.questionType"
+                  :disabled="true"
+                  placeholder="题型"
+                >
+                  <el-option
+                    v-for="item in questionTypes"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="10">
+            <el-col :xs="30" :sm="30" :md="30" :lg="30">
+              <el-form-item label="题干">
+                <span v-html="quesModel.quesBody"></span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <!--非套题-->
+          <div v-if="quesModel.questionType !== 'NESTED_ANSWER_QUESTION'">
+            <el-row
+              v-for="(quesOption, index) in quesModel.quesOptions"
+              :key="quesOption.number"
+              :gutter="10"
+            >
+              <el-col :xs="30" :sm="30" :md="30" :lg="30">
+                <el-form-item :label="index | optionOrderWordFilter">
+                  <span v-html="quesOption.optionBody"></span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </div>
+          <!--套题-->
+          <div v-if="quesModel.questionType === 'NESTED_ANSWER_QUESTION'">
+            <el-row
+              v-for="(subQuestionModel, index) in quesModel.subQuestions"
+              :key="index"
+              :gutter="10"
+            >
+              <el-col :xs="30" :sm="30" :md="30" :lg="30">
+                <el-row>
+                  <el-form-item label="题目">
+                    <span v-html="subQuestionModel.quesBody"></span>
+                  </el-form-item>
+                </el-row>
+                <el-row
+                  v-for="(
+                    subQuesOption, index2
+                  ) in subQuestionModel.quesOptions"
+                  :key="index2"
+                  :gutter="10"
+                >
+                  <el-col :xs="30" :sm="30" :md="30" :lg="30">
+                    <el-form-item :label="index2 | optionOrderWordFilter">
+                      <span v-html="subQuesOption.optionBody"></span>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-form-item label="答案">
+                    <span v-html="subQuestionModel.quesAnswer"></span>
+                  </el-form-item>
+                </el-row>
+              </el-col>
+            </el-row>
+          </div>
+
+          <!--非套题答案-->
+          <div v-if="quesModel.questionType !== 'NESTED_ANSWER_QUESTION'">
+            <el-form-item label="答案">
+              <span v-html="quesModel.quesAnswer"></span>
+            </el-form-item>
+          </div>
+        </el-form>
+      </el-dialog>
+    </div>
+  </section>
+</template>
+<script>
+import { QUESTION_API } from "@/constants/constants";
+import { QUESTION_TYPES } from "../constants/constants";
+import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+export default {
+  components: { LinkTitlesCustom },
+  data() {
+    return {
+      formSearch: {
+        questionType: "",
+        quesBody: "",
+      },
+      course: "",
+      paperId: "",
+      paperDetailId: "",
+      parentView: "",
+      courseList: [],
+      tableData: [],
+      multipleSelection: [],
+      currentPage: 1,
+      pageSize: 10,
+      total: 10,
+      courseName: "",
+      quesModel: {},
+      questionTypes: QUESTION_TYPES,
+      quesDialog: false,
+      loading: false,
+      dialogLoading: false,
+      singleRightAnswer: "", //接收单选答案
+      multipleRightAnswer: [], //接收多选答案
+    };
+  },
+  //钩子函数
+  created() {
+    this.paperId = this.$route.params.id;
+    this.courseName = this.$route.params.courseName;
+    var courseNo = this.$route.params.courseNo;
+    this.paperDetailId = this.$route.params.paperDetailId;
+    this.parentView = this.$route.params.parentView;
+    this.courseList.push({ name: this.courseName, value: courseNo });
+    this.course = this.courseList[0].value;
+    this.searchQuestionPaper();
+  },
+  methods: {
+    //查询列表
+    searchQuestionPaper() {
+      this.loading = true;
+      this.$http
+        .get(
+          QUESTION_API +
+            "/paper/listQuestion/" +
+            this.paperId +
+            "/" +
+            this.currentPage +
+            "/" +
+            this.pageSize +
+            "?quesType=" +
+            this.formSearch.questionType +
+            "&quesBody=" +
+            this.formSearch.quesBody
+        )
+        .then((response) => {
+          this.tableData = response.data.content;
+          this.total = response.data.totalElements;
+          this.loading = false;
+        });
+    },
+    //分页
+    handleCurrentChange(val) {
+      this.currentPage = val;
+      this.searchQuestionPaper();
+    },
+    openQuesDialog() {
+      this.quesDialog = true;
+    },
+    closeQuesDialog() {
+      this.quesDialog = false;
+      this.quesModel = {};
+    },
+    //预览
+    prevViewQues(row) {
+      this.quesModel = row;
+      this.disposeSelectAnswer();
+      this.openQuesDialog();
+    },
+    //添加试题
+    submitQues() {
+      if (this.multipleSelection.length === 0) {
+        this.$notify({
+          type: "error",
+          message: "请选择要添加的试题",
+        });
+      } else {
+        this.$confirm("是否添加这些试题?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          this.$http
+            .post(
+              QUESTION_API +
+                "/paper/selectQuestions/" +
+                this.paperId +
+                "/" +
+                this.paperDetailId,
+              this.multipleSelection
+            )
+            .then(() => {
+              this.$notify({
+                type: "success",
+                message: "添加成功!",
+              });
+              this.$router.push({
+                path: "/edit_paper/" + this.paperId + "/" + this.parentView,
+              });
+            })
+            .catch(() => {
+              this.$notify({
+                type: "error",
+                message: "添加失败!",
+              });
+            });
+        });
+      }
+    },
+    //返回
+    back() {
+      this.$router.push({
+        path: "/edit_paper/" + this.paperId + "/" + this.parentView,
+      });
+    },
+    //全选
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    /**
+     * 处理选择题答案显示
+     * 处理套题下选择题答案显示
+     */
+    disposeSelectAnswer() {
+      this.singleRightAnswer = "";
+      this.multipleRightAnswer = [];
+      //处理选择题答案显示
+      if (
+        this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" ||
+        this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION"
+      ) {
+        for (var i = 0; i < this.quesModel.quesOptions.length; i++) {
+          var option_i = this.quesModel.quesOptions[i];
+          var orderNum_i = String.fromCharCode(65 + i);
+          if (
+            this.quesModel.questionType == "SINGLE_ANSWER_QUESTION" &&
+            option_i.isCorrect == 1
+          ) {
+            this.singleRightAnswer = orderNum_i;
+          }
+          if (
+            this.quesModel.questionType == "MULTIPLE_ANSWER_QUESTION" &&
+            option_i.isCorrect == 1
+          ) {
+            this.multipleRightAnswer.push(orderNum_i);
+          }
+        }
+      }
+      //处理套题下选择题答案显示
+      if (this.quesModel.questionType == "NESTED_ANSWER_QUESTION") {
+        var subQuestions = this.quesModel.subQuestions;
+        for (var k = 0; k < subQuestions.length; k++) {
+          var subQuestion = subQuestions[k];
+          if (subQuestion.questionType == "SINGLE_ANSWER_QUESTION") {
+            for (var j = 0; j < subQuestion.quesOptions.length; j++) {
+              var option_j = subQuestion.quesOptions[j];
+              var orderNum_j = String.fromCharCode(65 + j);
+              if (option_j.isCorrect == 1) {
+                subQuestion["quesAnswer"] = orderNum_j;
+              }
+            }
+          }
+          if (subQuestion.questionType == "MULTIPLE_ANSWER_QUESTION") {
+            var subQuestionMultipleRightAnswer = [];
+            for (var z = 0; z < subQuestion.quesOptions.length; z++) {
+              var option_k = subQuestion.quesOptions[z];
+              var orderNum_k = String.fromCharCode(65 + z);
+              if (option_k.isCorrect == 1) {
+                subQuestionMultipleRightAnswer.push(orderNum_k);
+              }
+            }
+            subQuestion[
+              "quesAnswer"
+            ] = subQuestionMultipleRightAnswer.sort().toString();
+          }
+        }
+      }
+    },
+  },
+};
+</script>
+<style></style>

+ 472 - 0
src/plugins/axios.js

@@ -0,0 +1,472 @@
+import Vue from "vue";
+import axios from "axios";
+import router from "../router";
+import { loadProgressBar } from "axios-progress-bar";
+import networkInformationHint from "./networkInformationHint.js";
+
+const ERROR_MSG_CONFIG = require("./errorMsgConfig").default;
+
+// Full config:  https://github.com/axios/axios#request-config
+// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
+// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
+// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
+
+let config = {
+  // baseURL: process.env.baseURL || process.env.apiUrl || ""
+  timeout: 60 * 1000, // Timeout
+  withCredentials: true, // Check cross-site Access-Control
+};
+
+const _$httpWith500Msg = axios.create(config);
+const _$http = axios.create(config); // no auto 500 error UI
+const _$httpWithoutBar = axios.create(config);
+
+/**
+ * A. token lifecycle
+ * 1. /login UI => localStorage.removeItem('token') && localStorage.setItem('token')
+ * 2. non /login UI => axios if(!wk_token) wk_token = window.sessionStorage.getItem("token"), send request
+ * 3. if axios request fail with 401/403, wk_token = null, redirect to /login removeItem('token')
+ * 4. logout to /login, before send request, invalidate wk_token
+ * */
+
+let wk_token, wk_key;
+let wk_orgId;
+
+function getRootOrgId() {
+  if (location.hostname.includes("qmth.com.cn")) {
+    return "";
+  } else {
+    return "?orgId=" + (wk_orgId === undefined ? "" : wk_orgId);
+  }
+}
+
+_$httpWith500Msg.interceptors.request.use(
+  function (config) {
+    networkInformationHint();
+    // Do something before request is sent
+    if (
+      config.url.includes("/login") === false &&
+      config.url.includes("/auth/thirdPartyAccess") === false
+    ) {
+      if (!wk_token) {
+        const user = JSON.parse(window.sessionStorage.getItem("user"));
+        if (!user) {
+          if (
+            window.___lastInvalidDate === undefined ||
+            window.___lastInvalidDate < Date.now() - 300
+          ) {
+            Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                // if (process.env.NODE_ENV !== "production") {
+                //   router.push("/login/" + "?orgId=" + wk_orgId);
+                // } else {
+                //   router.push("/login");
+                // }
+                router.push("/login/" + getRootOrgId());
+              },
+            });
+            window.___lastInvalidDate = Date.now();
+          }
+          return;
+        }
+        wk_token = user.token;
+        wk_key = user.key;
+        wk_orgId = user.rootOrgId;
+      }
+      if (wk_token && config.headers.common["token"] == null) {
+        config.headers.common["token"] = wk_token;
+        config.headers.common["key"] = wk_key;
+      }
+    } else {
+      wk_token = null;
+    }
+    return config;
+  },
+  function (error) {
+    // Do something with request error
+    Vue.prototype.$notify({
+      showClose: true,
+      message: error,
+      type: "error",
+    });
+    return Promise.reject(error);
+  }
+);
+
+_$http.interceptors.request.use(
+  // no auto 500 error UI
+  function (config) {
+    networkInformationHint();
+    // Do something before request is sent
+    if (config.url.includes("/login") === false) {
+      if (!wk_token) {
+        const user = JSON.parse(window.sessionStorage.getItem("user"));
+        if (!user) {
+          if (
+            window.___lastInvalidDate === undefined ||
+            window.___lastInvalidDate < Date.now() - 300
+          ) {
+            Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                router.push("/login/" + getRootOrgId());
+              },
+            });
+            window.___lastInvalidDate = Date.now();
+          }
+          return;
+        }
+        wk_token = user.token;
+        wk_key = user.key;
+        wk_orgId = user.rootOrgId;
+      }
+      if (wk_token && config.headers.common["token"] == null) {
+        config.headers.common["token"] = wk_token;
+        config.headers.common["key"] = wk_key;
+      }
+    } else {
+      wk_token = null;
+    }
+    return config;
+  },
+  function (error) {
+    // Do something with request error
+    Vue.prototype.$notify({
+      showClose: true,
+      message: error,
+      type: "error",
+    });
+    return Promise.reject(error);
+  }
+);
+
+const recordRequest = () => {
+  let matchedRoutePath;
+  try {
+    const matched = router.resolve(location).route.matched;
+    const exactMatched = matched[matched.length - 1];
+    matchedRoutePath = exactMatched.path;
+  } catch (error) {
+    console.log(error);
+    window._hmt.push([
+      "_trackEvent",
+      `页面-${location.pathname}`,
+      "网络请求-响应",
+      "解析出错",
+    ]);
+  }
+  window._hmt.push([
+    "_trackEvent",
+    `页面-${matchedRoutePath || location.pathname}`,
+    "网络请求-响应",
+  ]);
+};
+// Add a response interceptor
+_$httpWith500Msg.interceptors.response.use(
+  (response) => {
+    recordRequest(response);
+    return response;
+  },
+  (error) => {
+    console.log(error);
+    if (!error.response) {
+      // "Network Error" 网络不通,直接返回
+
+      Vue.prototype.$notify({
+        showClose: true,
+        message: "网络连接异常,请检查网络设置。",
+        type: "error",
+      });
+      return Promise.reject(error);
+    }
+    // 这里是返回状态码不为200时候的错误处理
+    let status = error.response.status;
+
+    // 登录失效 跳转登录页面
+    if (status == 403 || status == 401) {
+      if (
+        window.___lastInvalidDate === undefined ||
+        window.___lastInvalidDate < Date.now() - 300
+      ) {
+        Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
+          confirmButtonText: "确定",
+          callback: () => {
+            router.push("/login/" + getRootOrgId());
+          },
+        });
+        window.___lastInvalidDate = Date.now();
+      }
+      return Promise.reject(error);
+    } else if (status == 405) {
+      Vue.prototype.$alert("没有权限!", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          router.push("/login/" + getRootOrgId());
+        },
+      });
+      return Promise.reject(error);
+    } else if (status == 502) {
+      Vue.prototype.$alert("服务器异常!", "提示", {
+        confirmButtonText: "确定",
+      });
+      return Promise.reject(error);
+    }
+
+    if (status != 200) {
+      const data = error.response.data;
+      if (ERROR_MSG_CONFIG.map((v) => v.code).includes(data.code)) {
+        const MSG = ERROR_MSG_CONFIG.find((v) => v.code === data.code);
+        if (MSG.display) {
+          Vue.prototype.$notify({
+            showClose: true,
+            message: MSG.message,
+            type: "error",
+          });
+        }
+      } else {
+        if (data && data.desc) {
+          Vue.prototype.$notify({
+            showClose: true,
+            message: data.desc,
+            type: "error",
+          });
+        } else {
+          Vue.prototype.$notify({
+            showClose: true,
+            message: "未定义异常: " + JSON.stringify(data, 2),
+            type: "error",
+          });
+        }
+      }
+      return Promise.reject(error);
+    }
+  }
+);
+
+// Add a response interceptor
+_$http.interceptors.response.use(
+  // no auto 500 error UI
+  (response) => {
+    recordRequest(response);
+    return response;
+  },
+  (error) => {
+    if (!error.response) {
+      Vue.prototype.$notify({
+        showClose: true,
+        message: "网络连接异常,请检查网络设置。",
+        type: "error",
+      });
+      return Promise.reject(error);
+    }
+    // 这里是返回状态码不为200时候的错误处理
+    let status = error.response.status;
+
+    // 登录失效 跳转登录页面
+    if (status == 403 || status == 401) {
+      if (
+        window.___lastInvalidDate === undefined ||
+        window.___lastInvalidDate < Date.now() - 300
+      ) {
+        Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
+          confirmButtonText: "确定",
+          callback: () => {
+            router.push("/login/" + getRootOrgId());
+          },
+        });
+        window.___lastInvalidDate = Date.now();
+      }
+      return Promise.reject(error);
+    } else if (status == 405) {
+      Vue.prototype.$alert("没有权限!", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          router.push("/login/" + getRootOrgId());
+        },
+      });
+      return Promise.reject(error);
+    } else if (status == 502) {
+      Vue.prototype.$alert("服务器异常!", "提示", {
+        confirmButtonText: "确定",
+      });
+      return Promise.reject(error);
+    } else if (status == 503) {
+      Vue.prototype.$alert("服务器繁忙,请稍后重试!", "提示", {
+        confirmButtonText: "确定",
+      });
+      return Promise.reject(error);
+    }
+
+    if (status != 200) {
+      return Promise.reject(error);
+    }
+  }
+);
+
+_$httpWithoutBar.interceptors.request.use(
+  // no auto 500 error UI
+  function (config) {
+    networkInformationHint();
+    // Do something before request is sent
+    if (config.url.includes("/login") === false) {
+      if (!wk_token) {
+        const user = JSON.parse(window.sessionStorage.getItem("user"));
+        if (!user) {
+          if (
+            window.___lastInvalidDate === undefined ||
+            window.___lastInvalidDate < Date.now() - 300
+          ) {
+            Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
+              confirmButtonText: "确定",
+              callback: () => {
+                router.push("/login/" + getRootOrgId());
+              },
+            });
+            window.___lastInvalidDate = Date.now();
+          }
+          return;
+        }
+        wk_token = user.token;
+        wk_key = user.key;
+        wk_orgId = user.rootOrgId;
+      }
+      if (wk_token && config.headers.common["token"] == null) {
+        config.headers.common["token"] = wk_token;
+        config.headers.common["key"] = wk_key;
+      }
+    } else {
+      wk_token = null;
+    }
+    return config;
+  },
+  function (error) {
+    // Do something with request error
+    Vue.prototype.$notify({
+      showClose: true,
+      message: error,
+      type: "error",
+    });
+    return Promise.reject(error);
+  }
+);
+_$httpWithoutBar.interceptors.response.use(
+  // no auto 500 error UI
+  (response) => {
+    recordRequest(response);
+    return response;
+  },
+  (error) => {
+    if (!error.response) {
+      Vue.prototype.$notify({
+        showClose: true,
+        message: "网络连接异常,请检查网络设置。",
+        type: "error",
+      });
+      return Promise.reject(error);
+    }
+    // 这里是返回状态码不为200时候的错误处理
+    let status = error.response.status;
+
+    // 登录失效 跳转登录页面
+    if (status == 403 || status == 401) {
+      if (
+        window.___lastInvalidDate === undefined ||
+        window.___lastInvalidDate < Date.now() - 300
+      ) {
+        window.___lastInvalidDate = Date.now();
+      }
+      return Promise.reject(error);
+    } else if (status == 405) {
+      Vue.prototype.$alert("没有权限!", "提示", {
+        confirmButtonText: "确定",
+        callback: () => {
+          router.push("/login/" + getRootOrgId());
+        },
+      });
+      return Promise.reject(error);
+    } else if (status == 502) {
+      Vue.prototype.$alert("服务器异常!", "提示", {
+        confirmButtonText: "确定",
+      });
+      return Promise.reject(error);
+    }
+
+    if (status != 200) {
+      return Promise.reject(error);
+    }
+  }
+);
+
+Plugin.install = function (Vue) {
+  Vue.$http = _$http; // no auto 500 error UI
+  Object.defineProperties(Vue.prototype, {
+    $http: {
+      get() {
+        return _$http; // no auto 500 error UI
+      },
+    },
+  });
+
+  Vue.$httpWithMsg = _$httpWith500Msg;
+  Object.defineProperties(Vue.prototype, {
+    $httpWithMsg: {
+      get() {
+        return _$httpWith500Msg;
+      },
+    },
+  });
+
+  // for below request
+  // config.url.includes("/api/ecs_ques/paper/") === false &&
+  // config.url.includes("/api/ecs_ques/questionAudio") === false
+  const _a = axios.create(config);
+  Vue.$httpWithoutAuth = _a;
+  Object.defineProperties(Vue.prototype, {
+    $httpWithoutAuth: {
+      get() {
+        return _a;
+      },
+    },
+  });
+
+  Vue.$httpWithoutBar = _$httpWithoutBar;
+  Object.defineProperties(Vue.prototype, {
+    $httpWithoutBar: {
+      get() {
+        return _$httpWithoutBar;
+      },
+    },
+  });
+};
+
+Vue.use(Plugin);
+
+loadProgressBar({}, Vue.$http);
+loadProgressBar({}, Vue.$httpWithMsg);
+loadProgressBar({}, Vue.$httpWithoutAuth);
+
+// const update = (type, e) => {
+//   // debugger;
+//   console.log(type);
+//   console.log(
+//     "e.target.url: ",
+//     e.target.responseURL,
+//     " timeStamp: ",
+//     e.timeStamp.toFixed(2),
+//     " loaded:",
+//     e.loaded,
+//     " total: ",
+//     e.total
+//   );
+//   console.log(e);
+// };
+// Vue.$httpWithMsg.defaults.onDownloadProgress = e => {
+//   update("下载", e);
+// };
+// Vue.$httpWithMsg.defaults.onUploadProgress = e => {
+//   update("上传", e);
+// };
+
+import "axios-progress-bar/dist/nprogress.css";
+export default Plugin;

+ 5 - 0
src/plugins/element.js

@@ -0,0 +1,5 @@
+import Vue from "vue";
+import Element from "element-ui";
+import "element-ui/lib/theme-chalk/index.css";
+
+Vue.use(Element);

+ 7 - 0
src/plugins/errorMsgConfig.js

@@ -0,0 +1,7 @@
+export default [
+  {
+    code: "B-001-example",
+    display: false,
+    message: "",
+  },
+];

+ 13 - 0
src/plugins/helpers.js

@@ -0,0 +1,13 @@
+import Vue from "vue";
+import queryHandlers from "@/utils/queryHandlers";
+
+const helpers = { ...queryHandlers };
+
+const plugin = {
+  install() {
+    Vue.helpers = helpers;
+    Vue.prototype.$helpers = helpers;
+  },
+};
+
+Vue.use(plugin);

+ 40 - 0
src/plugins/networkInformationHint.js

@@ -0,0 +1,40 @@
+import Vue from "vue";
+
+let ___networkInformationHintLastTime = Date.now();
+const sessionNoNetworkHint = "networkInformationHint-nohint";
+
+export default function networkInformationHint() {
+  if (navigator.connection && navigator.connection.downlink) {
+    if (navigator.connection.downlink < 1 || navigator.connection.rtt > 1000) {
+      window._hmt.push([
+        "_trackEvent",
+        "网络状况不佳",
+        "下载小于1Mb,延迟大于1000ms",
+      ]);
+
+      if (
+        Date.now() - ___networkInformationHintLastTime > 3000 &&
+        !sessionStorage.getItem(sessionNoNetworkHint)
+      ) {
+        Vue.prototype.$notify({
+          showClose: true,
+          duration: 2000,
+          title: "当前网络状况不佳!",
+          message: `当前网速:下载(${navigator.connection.downlink}Mb) 网络延时(${navigator.connection.rtt}ms) (点击此行可不再提示)`,
+          type: "warning",
+          onClick: () => {
+            Vue.prototype
+              .$alert("不在提示网络状况?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+              })
+              .then(() => {
+                sessionStorage.setItem(sessionNoNetworkHint, true);
+              });
+          },
+        });
+        ___networkInformationHintLastTime = Date.now();
+      }
+    }
+  }
+}

+ 13 - 0
src/plugins/vueAwesome.js

@@ -0,0 +1,13 @@
+import Vue from "vue";
+import Icon from "vue-awesome/components/Icon";
+
+import "vue-awesome/icons/flag";
+import "vue-awesome/icons/print";
+import "vue-awesome/icons/user";
+import "vue-awesome/icons/users";
+import "vue-awesome/icons/sign-out-alt";
+import "vue-awesome/icons/lock";
+import "vue-awesome/icons/bars";
+import "vue-awesome/icons/user-check";
+
+Vue.component("VIcon", Icon);

+ 139 - 0
src/plugins/vueLifecylceLogs.js

@@ -0,0 +1,139 @@
+import Vue from "vue";
+
+// const ignoreComponents = [undefined, "transition", "router-link", "Bar"];
+
+// const ignoreComponentsNameRegexArray = [/^El[A-Z].*/, /^fa-.*/];
+
+// const groupCollapsed = true;
+
+// const RENDER_DURATION = 1 * 1000;
+
+export default function ({
+  ignoreComponents = [undefined, "transition", "router-link", "Bar"],
+  ignoreComponentsNameRegexArray = [/^El[A-Z].*/, /^fa-.*/],
+  groupCollapsed = true,
+  RENDER_DURATION = 1 * 1000,
+}) {
+  function getParentNumber(that) {
+    let parentNumber = 0;
+    while (that) {
+      if (that.$parent) {
+        parentNumber++;
+        that = that.$parent;
+      } else {
+        break;
+      }
+    }
+    return parentNumber;
+  }
+
+  function componentNameMatch(name) {
+    if (ignoreComponents.includes(name)) {
+      return true;
+    } else {
+      let match = false;
+      for (const regex of ignoreComponentsNameRegexArray) {
+        if (regex.test(name)) {
+          match = true;
+          break;
+        }
+      }
+      return match;
+    }
+  }
+
+  const injectMethods = [
+    { name: "beforeCreate", style: "color: green" },
+    { name: "created", style: "color: green" },
+    { name: "beforeMount", style: "color: green" },
+    { name: "mounted", style: "color: green" },
+    { name: "beforeUpdate", style: "color: #0000aa" },
+    { name: "updated", style: "color: #0000aa" },
+    { name: "beforeDestroy", style: "color: red; font-weight: bold" },
+    { name: "destroyed", style: "color: red; font-weight: bold" },
+  ];
+
+  const injectRouterMethods = [
+    {
+      name: "beforeRouteEnter",
+      style: ["background: #aaa", "color: #0000aa;"],
+    },
+    {
+      name: "beforeRouteUpdate",
+      style: ["background: #aaa", "color: #0000aa;"],
+    },
+    {
+      name: "beforeRouteLeave",
+      style: ["background: #aaa", "color: #0000aa;"],
+    },
+  ];
+
+  let lifeCycleMixins = {};
+
+  let startLogging = false;
+  let loggingTime = Date.now();
+
+  for (const m of injectMethods) {
+    lifeCycleMixins[m.name] = function () {
+      const parentNumber = getParentNumber(this);
+      if (!componentNameMatch(this.$options.name)) {
+        if (!startLogging) {
+          startLogging = true;
+          if (groupCollapsed) {
+            console.groupCollapsed("Vue lifecyle logs");
+          } else {
+            console.group("Vue lifecyle logs");
+          }
+
+          loggingTime = Date.now();
+        }
+        console.log(
+          "--".repeat(parentNumber) + `${this.$options.name} %c ${m.name}`,
+          m.style
+        );
+      }
+    };
+  }
+
+  for (const m of injectRouterMethods) {
+    lifeCycleMixins[m.name] = function (to, from, next) {
+      if (!startLogging) {
+        startLogging = true;
+        if (groupCollapsed) {
+          console.groupCollapsed("Vue lifecyle logs");
+        } else {
+          console.group("Vue lifecyle logs");
+        }
+
+        loggingTime = Date.now();
+      }
+
+      console.log(
+        "%c " +
+          from.fullPath +
+          " --> " +
+          to.fullPath +
+          (this ? ` (${this.$options.name}) ` : " ") +
+          `%c ${m.name}`,
+        ...m.style
+      );
+
+      next();
+    };
+  }
+
+  // setInterval(
+  //   () => console.log("----------------------Vue lifecyle logs----------------"),
+  //   10 * 1000
+  // );
+  setInterval(() => {
+    if (startLogging && Date.now() - loggingTime > RENDER_DURATION) {
+      console.groupEnd();
+      startLogging = false;
+    }
+  }, 1 * 100);
+
+  Vue.mixin({
+    ...lifeCycleMixins,
+  });
+}

+ 55 - 0
src/router/index.js

@@ -0,0 +1,55 @@
+import Vue from "vue";
+import Router from "vue-router";
+import PortalRoutes from "../modules/portal/routes/routes";
+import QuestionsRoutes from "../modules/questions/routes/routes";
+import { QUESTION_API } from "@/constants/constants.js";
+
+// ignore NavigationDuplicated. https://github.com/vuejs/vue-router/issues/2881
+const originalPush = Router.prototype.push;
+Router.prototype.push = function push(location, onResolve, onReject) {
+  if (onResolve || onReject)
+    return originalPush.call(this, location, onResolve, onReject);
+  try {
+    return originalPush.call(this, location).catch((err) => err);
+  } catch (error) {
+    console.log(error);
+  }
+};
+// end ignore
+
+Vue.use(Router);
+
+let router = new Router({
+  mode: "history",
+  base: "/admin",
+  routes: [...PortalRoutes, ...QuestionsRoutes],
+});
+
+router.beforeEach((to, from, next) => {
+  if (to.path) {
+    window._hmt.push(["_trackPageview", "/admin" + to.fullPath]);
+  }
+
+  if (to.path.includes("/preview_paper/")) {
+    next();
+    return;
+  }
+  if (!to.meta.privilegeCodes) {
+    next();
+  } else {
+    let params = new URLSearchParams();
+    params.append("privilegeCodes", to.meta.privilegeCodes);
+    Vue.prototype.$httpWithMsg
+      .post(QUESTION_API + "/rolePrivilege/checkPrivileges?" + params)
+      .then((response) => {
+        if (Object.values(response.data).includes(true)) {
+          next();
+        } else {
+          Vue.prototype.$alert("没有权限访问!", "提示");
+          next(false);
+        }
+      });
+  }
+});
+
+export default router;

+ 18 - 0
src/store/index.js

@@ -0,0 +1,18 @@
+import Vue from "vue";
+import Vuex from "vuex";
+import user from "../modules/portal/store/user";
+import currentPaths from "../modules/portal/store/currentPaths";
+import menuList from "../modules/portal/store/menuList";
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+  state: {},
+  mutations: {},
+  actions: {},
+  modules: {
+    user,
+    currentPaths,
+    menuList,
+  },
+});

+ 42 - 0
src/styles/bootstrap.scss

@@ -0,0 +1,42 @@
+/*!
+ * Bootstrap v4.2.1 (https://getbootstrap.com/)
+ * 启明泰和根据bootstrap定制UI规范
+ */
+
+@import "bootstrap/scss/_functions.scss";
+@import "bootstrap/scss/_variables.scss";
+@import "bootstrap/scss/_mixins.scss";
+@import "bootstrap/scss/_root.scss";
+@import "bootstrap/scss/_reboot.scss";
+@import "bootstrap/scss/_type.scss";
+@import "bootstrap/scss/_images.scss";
+@import "bootstrap/scss/_code.scss";
+@import "bootstrap/scss/_grid.scss";
+@import "bootstrap/scss/_tables.scss";
+@import "bootstrap/scss/_forms.scss";
+@import "bootstrap/scss/_buttons.scss";
+// @import "transitions";
+// @import "dropdown";
+@import "bootstrap/scss/_button-group.scss";
+@import "bootstrap/scss/_input-group.scss";
+// @import "custom-forms";
+@import "bootstrap/scss/_nav.scss";
+@import "bootstrap/scss/_navbar.scss";
+@import "bootstrap/scss/_card.scss";
+// @import "breadcrumb";
+// @import "pagination";
+@import "bootstrap/scss/_badge.scss";
+// @import "jumbotron";
+// @import "alert";
+// @import "progress";
+@import "bootstrap/scss/_media.scss";
+@import "bootstrap/scss/_list-group.scss";
+@import "bootstrap/scss/_close.scss";
+// @import "toasts";
+// @import "modal";
+// @import "tooltip";
+// @import "popover";
+// @import "carousel";
+// @import "spinners";
+@import "bootstrap/scss/_utilities.scss";
+// @import "print";

+ 18 - 0
src/styles/elementuiCustomize.css

@@ -0,0 +1,18 @@
+.el-table td {
+  padding: 4px 0;
+}
+
+.el-table label {
+  margin-bottom: 0;
+}
+.el-form label.el-radio {
+  margin-bottom: 0;
+}
+
+.el-form .el-form-item {
+  margin-bottom: 0px;
+}
+
+/* .el-form--inline .el-form-item__content {
+  line-height: inherit !important;
+} */

+ 124 - 0
src/styles/global.css

@@ -0,0 +1,124 @@
+@import "./elementuiCustomize.css";
+@import "./nprogress.css";
+
+body {
+  margin: 0;
+  min-width: 1260px;
+}
+
+.qm-primary-text {
+  font-size: 14px;
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: 22px;
+  letter-spacing: 0px;
+  color: #999999;
+}
+
+.qm-primary-strong-text {
+  font-size: 14px;
+  font-weight: bold;
+  font-stretch: normal;
+  line-height: 14px;
+  letter-spacing: 0px;
+  color: #444444;
+}
+
+.qm-secondary-text {
+  font-size: 12px;
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: 20px;
+  letter-spacing: 0px;
+  color: #999999;
+}
+
+.qm-big-text {
+  font-size: 16px;
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: 22px;
+  letter-spacing: 0px;
+  color: #999999;
+}
+
+.qm-title-text {
+  font-size: 18px;
+  font-weight: bold;
+  font-stretch: normal;
+  line-height: 24px;
+  letter-spacing: 0px;
+  color: #444444;
+}
+
+.qm-tip-text {
+  color: #cccccc;
+}
+
+.qm-primary-button {
+  /* font-size: 36px; */
+  display: inline-block;
+  height: 36px;
+  font-size: 14px;
+  color: #ffffff;
+  background-color: #13bb8a;
+  border-radius: 6px;
+  padding: 0 30px;
+  line-height: 36px;
+  overflow: hidden;
+}
+
+.qm-primary-button:hover {
+  color: #ffffff;
+  background-color: #13bb8a;
+}
+
+.qm-secondary-button {
+  height: 36px;
+  font-size: 14px;
+  color: #999999;
+  background-color: #ffffff;
+  border-radius: 6px;
+  padding: 0 30px;
+  line-height: 36px;
+  overflow: hidden;
+}
+
+.qm-secondary-button:hover {
+  color: #999999;
+  background-color: #ffffff;
+}
+
+.qm-icon-button {
+  height: 36px;
+  font-size: 36px;
+  color: #999999;
+  background-color: #ffffff;
+  line-height: 36px;
+  overflow: hidden;
+}
+
+.qm-icon-button:hover {
+  color: #444444;
+  background-color: #ffffff;
+}
+
+.center-block {
+  display: block;
+  margin-right: auto;
+  margin-left: auto;
+}
+
+.pull-right {
+  float: right !important;
+}
+
+.pull-left {
+  float: left !important;
+}
+
+.block-seperator {
+  width: 100%;
+  border-bottom: 1px solid #ddd;
+  margin: 10px 0;
+}

+ 12 - 0
src/styles/nprogress.css

@@ -0,0 +1,12 @@
+#nprogress .bar {
+  background: yellow !important;
+}
+
+#nprogress .peg {
+  box-shadow: 0 0 10px yellow, 0 0 5px yellow !important;
+}
+
+#nprogress .spinner-icon {
+  border-top-color: yellow !important;
+  border-left-color: yellow !important;
+}

+ 5 - 0
src/utils/utils.js

@@ -0,0 +1,5 @@
+import queryString from "query-string";
+
+export function object2QueryString(obj) {
+  return queryString.stringify(obj);
+}