Ver código fonte

Merge remote-tracking branch 'remotes/origin/release_v4.1.1'

# Conflicts:
#	src/modules/basic/view/clientConfig.vue
#	src/modules/basic/view/school_config.vue
#	src/modules/oe/views/examDetail.vue
deason 3 anos atrás
pai
commit
2c7fa61a27

+ 8 - 0
src/constants/constants.js

@@ -102,6 +102,7 @@ export const PREVENT_CHEATING_CONFIG = [
   // { code: "DISABLE_VIRTUAL_MACHINE", name: "禁用虚拟机" },
   { code: "FULL_SCREEN_TOP", name: "强制全屏置顶" },
   { code: "DISABLE_MULTISCREEN", name: "禁用双屏" },
+  { code: "RECORD_SWITCH_SCREEN", name: "计算切屏次数" },
 ];
 //学生端版本
 export const STUDENT_CLIENT_VERSION = [
@@ -113,3 +114,10 @@ export const LOGIN_SUPPORT = [
   { code: "NATIVE", name: "考生端登录" },
   { code: "BROWSER", name: "浏览器登录" },
 ];
+
+//活体检查 - 动作选项
+export const ACTION_OPTION_LIST = [
+  { code: "NOD", name: "点头" },
+  { code: "SHAKE", name: "摇头" },
+  { code: "BLINK", name: "眨眼" },
+];

+ 147 - 4
src/modules/basic/view/clientConfig.vue

@@ -41,6 +41,50 @@
                 class="input-width"
               ></el-input>
             </el-form-item>
+            <el-form-item label="考生端登录页图片">
+              <el-upload
+                ref="uploadClientBgPicture"
+                class="upload-width"
+                accept=".jpg,.png,.jpeg"
+                :action="uploadActionClientBgPicture"
+                :headers="uploadHeaders"
+                :data="uploadDataClientBgPicture"
+                :before-upload="beforeUploadClientBgPicture"
+                :on-success="uploadSuccessClientBgPicture"
+                :on-error="uploadError"
+                :on-remove="handleRemoveClientBgPicture"
+                :file-list="fileListClientBgPicture"
+                :auto-upload="false"
+                :multiple="false"
+                :limit="1"
+                :on-exceed="handleExceed"
+                list-type="picture"
+              >
+                <el-button slot="trigger" size="small" type="primary"
+                  >选择文件</el-button
+                >&nbsp;
+                <el-button
+                  size="small"
+                  type="success"
+                  @click="submitUploadClientBgPicture"
+                  >确认上传</el-button
+                >
+                <el-button
+                  size="small"
+                  type="danger"
+                  @click="handleRemoveClientBgPicture"
+                  >清空文件</el-button
+                >
+                <div slot="tip" class="el-upload__tip">
+                  图片宽高比3:2;宽至少为1200px,不超过2000px;小于1MB;.jpg
+                  .jpeg .png 文件
+                </div>
+              </el-upload>
+              <el-input
+                v-show="false"
+                v-model="ruleForm.STUDENT_CLIENT_BG_PICTURE_URL"
+              ></el-input>
+            </el-form-item>
             <el-form-item label="学校logo" prop="LOGO_FILE_URL">
               <el-upload
                 ref="upload"
@@ -453,6 +497,7 @@ export default {
         LOGIN_SUPPORT: "",
         IS_CUSTOM_MENU_LOGO: "false",
         CUS_MENU_LOGO_FILE_URL: "",
+        STUDENT_CLIENT_BG_PICTURE_URL: "",
         properties: {
           OE_STUDENT_SYS_NAME: "",
           LOGO_FILE_URL: "",
@@ -467,6 +512,7 @@ export default {
           LOGIN_SUPPORT: "",
           IS_CUSTOM_MENU_LOGO: "false",
           CUS_MENU_LOGO_FILE_URL: "",
+          STUDENT_CLIENT_BG_PICTURE_URL: "",
         },
         loginType: [],
         preventCheatingConfig: [],
@@ -481,6 +527,10 @@ export default {
       uploadHeaders: {},
       uploadData: {},
       fileList: [],
+
+      uploadActionClientBgPicture: "",
+      uploadDataClientBgPicture: {},
+      fileListClientBgPicture: [],
       menuRules: {
         name: [
           {
@@ -663,6 +713,8 @@ export default {
           this.formDataChanged = !(
             newForm.OE_STUDENT_SYS_NAME ==
               this.originalRuleForm.OE_STUDENT_SYS_NAME &&
+            newForm.STUDENT_CLIENT_BG_PICTURE_URL ==
+              this.originalRuleForm.STUDENT_CLIENT_BG_PICTURE_URL &&
             newForm.LOGO_FILE_URL == this.originalRuleForm.LOGO_FILE_URL &&
             this.equalArrayIgnoreSequence(
               newForm.loginType,
@@ -726,6 +778,74 @@ export default {
   },
 
   methods: {
+    beforeUploadClientBgPicture() {
+      if (!this.checkUploadClientBgPicture()) {
+        return false;
+      }
+    },
+    checkUploadClientBgPicture() {
+      let fileList = this.$refs.uploadClientBgPicture.uploadFiles;
+      if (fileList.length == 0) {
+        this.$notify({
+          message: "上传文件不能为空",
+          type: "error",
+        });
+        return false;
+      }
+      if (fileList.length > 1) {
+        this.$notify({
+          message: "每次只能上传一个文件",
+          type: "error",
+        });
+        return false;
+      }
+      for (let file of fileList) {
+        let fileName = file.name;
+        if (
+          !fileName.endsWith(".jpg") &&
+          !fileName.endsWith(".jpeg") &&
+          !fileName.endsWith(".png")
+        ) {
+          this.$notify({
+            message: "上传文件格式必须为jpg jpeg png",
+            type: "error",
+          });
+          this.fileListClientBgPicture = [];
+          return false;
+        }
+      }
+      return true;
+    },
+    uploadSuccessClientBgPicture(response) {
+      if (response && response.length > 0) {
+        this.$notify({
+          message: "上传成功",
+          type: "success",
+        });
+        let fileUrl = response;
+
+        this.ruleForm.STUDENT_CLIENT_BG_PICTURE_URL =
+          this.ruleForm.properties.STUDENT_CLIENT_BG_PICTURE_URL = fileUrl;
+      } else {
+        this.errDialog = true;
+      }
+      this.fileLoading = false;
+    },
+    handleRemoveClientBgPicture() {
+      this.fileListClientBgPicture = [];
+      this.ruleForm.STUDENT_CLIENT_BG_PICTURE_URL =
+        this.ruleForm.properties.STUDENT_CLIENT_BG_PICTURE_URL = "";
+      if (this.$refs.uploadClientBgPicture) {
+        this.$refs.uploadClientBgPicture.clearFiles();
+      }
+    },
+    submitUploadClientBgPicture() {
+      if (!this.checkUploadClientBgPicture()) {
+        return false;
+      }
+      this.$refs.uploadClientBgPicture.submit();
+      this.fileLoading = true;
+    },
     submitForm(formName) {
       this.$refs[formName].validate((valid) => {
         if (valid) {
@@ -742,6 +862,8 @@ export default {
 
           this.ruleForm.properties.OE_STUDENT_SYS_NAME =
             this.ruleForm.OE_STUDENT_SYS_NAME;
+          this.ruleForm.properties.STUDENT_CLIENT_BG_PICTURE_URL =
+            this.ruleForm.STUDENT_CLIENT_BG_PICTURE_URL;
           this.ruleForm.properties.LOGO_FILE_URL = this.ruleForm.LOGO_FILE_URL;
           this.ruleForm.properties.STUDENT_CLIENT_DEFAULT_SIZE =
             this.ruleForm.STUDENT_CLIENT_DEFAULT_SIZE;
@@ -862,6 +984,8 @@ export default {
 
     async initForm() {
       this.uploadAction = CORE_API + "/org/importLogo/" + this.ruleForm.orgId;
+      this.uploadActionClientBgPicture =
+        CORE_API + "/org/importClientBgPicture/" + this.ruleForm.orgId;
       this.uploadAnswer =
         CORE_API + "/org/importAnswers/" + this.ruleForm.orgId;
       this.ruleForm.STUDENT_CLIENT_DEFAULT_SIZE = "1400*900";
@@ -1031,7 +1155,9 @@ export default {
             this.ruleForm.properties.OE_STUDENT_SYS_NAME ||
             this.ruleForm.OE_STUDENT_SYS_NAME;
           // }
-
+          this.ruleForm.STUDENT_CLIENT_BG_PICTURE_URL =
+            this.ruleForm.properties.STUDENT_CLIENT_BG_PICTURE_URL ||
+            this.ruleForm.STUDENT_CLIENT_BG_PICTURE_URL;
           this.ruleForm.LOGO_FILE_URL =
             this.ruleForm.properties.LOGO_FILE_URL ||
             this.ruleForm.LOGO_FILE_URL;
@@ -1052,9 +1178,10 @@ export default {
           this.ruleForm.STUDENT_CLIENT_DEFAULT_SIZE =
             this.ruleForm.properties.STUDENT_CLIENT_DEFAULT_SIZE ||
             this.ruleForm.STUDENT_CLIENT_DEFAULT_SIZE;
-          this.ruleForm.STUDENT_CLIENT_CONSOLE_CONFIG =
-            this.ruleForm.properties.STUDENT_CLIENT_CONSOLE_CONFIG ||
-            this.ruleForm.STUDENT_CLIENT_CONSOLE_CONFIG;
+          this.ruleForm.STUDENT_CLIENT_CONSOLE_CONFIG = this.ruleForm.properties
+            .STUDENT_CLIENT_CONSOLE_CONFIG
+            ? this.ruleForm.properties.STUDENT_CLIENT_CONSOLE_CONFIG
+            : "";
           this.ruleForm.STUDENT_CODE_LOGIN_ALIAS =
             this.ruleForm.properties.STUDENT_CODE_LOGIN_ALIAS ||
             this.ruleForm.STUDENT_CODE_LOGIN_ALIAS;
@@ -1111,6 +1238,22 @@ export default {
             // console.log(defaultValue);
           }
 
+          let fileUrlClientBgPicture =
+            response.data.STUDENT_CLIENT_BG_PICTURE_URL;
+          if (fileUrlClientBgPicture) {
+            let lastIndex = fileUrlClientBgPicture.lastIndexOf("/");
+            let len = fileUrlClientBgPicture.length;
+            let fname = fileUrlClientBgPicture.substr(
+              lastIndex + 1,
+              len - lastIndex
+            );
+            this.fileListClientBgPicture = [
+              { name: fname, url: this.ruleForm.STUDENT_CLIENT_BG_PICTURE_URL },
+            ];
+          } else {
+            this.fileListClientBgPicture = [];
+          }
+
           let fileUrl = response.data.LOGO_FILE_URL;
           let fname = "";
           if (fileUrl) {

+ 1 - 1
src/modules/basic/view/data_previllege.vue

@@ -205,7 +205,7 @@
                       <el-table-column width="200" label="中心代码">
                         <span slot-scope="scope">{{ scope.row.orgCode }}</span>
                       </el-table-column>
-                      <el-table-column label="课程名称">
+                      <el-table-column label="中心名称">
                         <span slot-scope="scope">{{ scope.row.orgName }}</span>
                       </el-table-column>
                       <el-table-column width="100" label="负责人">

+ 3 - 3
src/modules/basic/view/data_previllege_add_course.vue

@@ -2,7 +2,7 @@
   <el-dialog
     ref="dialog"
     title="添加课程"
-    width="700px"
+    width="900px"
     :visible.sync="visible"
     @close="closeDialog"
   >
@@ -19,7 +19,7 @@
           <el-input v-model="form.name" placeholder="请输入课程名称" />
         </el-form-item>
         <el-form-item label="课程层次">
-          <LevelTypeSelect v-model="form.levelType"></LevelTypeSelect>
+          <LevelTypeSelect v-model="form.level"></LevelTypeSelect>
         </el-form-item>
         <el-form-item>
           <el-button
@@ -97,7 +97,7 @@ export default {
       visible: false,
       form: {
         name: "",
-        levelType: "",
+        level: "",
       },
       rules: {},
       loading: false,

+ 1 - 1
src/modules/basic/view/data_previllege_add_exam.vue

@@ -2,7 +2,7 @@
   <el-dialog
     ref="dialog"
     title="添加考试"
-    width="700px"
+    width="900px"
     :visible.sync="visible"
     @close="closeDialog"
   >

+ 1 - 1
src/modules/basic/view/data_previllege_add_org.vue

@@ -2,7 +2,7 @@
   <el-dialog
     ref="dialog"
     title="添加中心"
-    width="700px"
+    width="900px"
     :visible.sync="visible"
     @close="closeDialog"
   >

+ 213 - 24
src/modules/basic/view/school_config.vue

@@ -91,10 +91,75 @@
             v-model="ruleForm.IDENTIFICATION_OF_LIVING_BODY_SCHEME"
             class="input"
           >
-            <el-radio label="S1">faceID</el-radio>
-            <el-radio label="S2">自研活体</el-radio>
+            <el-radio label="S1" :disabled="ruleForm.PC_CLIENT_ENABLED"
+              >FaceID</el-radio
+            >
+            <el-radio label="S2" :disabled="ruleForm.PC_CLIENT_ENABLED"
+              >自研活体</el-radio
+            >
+            <el-radio label="S3" :disabled="!ruleForm.PC_CLIENT_ENABLED"
+              >C端活体</el-radio
+            >
           </el-radio-group>
         </el-form-item>
+
+        <el-form-item label="启用C端考生端" prop="PC_CLIENT_ENABLED">
+          <el-switch
+            v-model="ruleForm.PC_CLIENT_ENABLED"
+            on-text="是"
+            off-text="否"
+            :disabled="false"
+          ></el-switch>
+        </el-form-item>
+
+        <div v-if="ruleForm.PC_CLIENT_ENABLED">
+          <el-form-item label="指定动作检测提醒" prop="ACTION_ALERT">
+            <el-input
+              v-model="ruleForm.ACTION_ALERT"
+              style="width: 180px"
+            ></el-input>
+            <span style="font-size: 14px; line-height: 44px">
+              秒后开始检测</span
+            >
+          </el-form-item>
+
+          <el-form-item label="动作个数" prop="ACTION_NUM">
+            <el-select v-model="ruleForm.ACTION_NUM" style="width: 180px">
+              <el-option label="1" value="1"></el-option>
+              <el-option label="2" value="2"></el-option>
+              <el-option label="3" value="3"></el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="动作选项" prop="ACTION_OPTIONS">
+            <el-checkbox-group v-model="ruleForm.ACTION_OPTIONS">
+              <el-checkbox
+                v-for="opt in actionOptions"
+                :key="opt.code"
+                v-model="opt.code"
+                name="ACTION_OPTION"
+                :label="opt.code"
+                >{{ opt.name }}</el-checkbox
+              >
+            </el-checkbox-group>
+          </el-form-item>
+
+          <el-form-item label="动作顺序" prop="ACTION_ORDER">
+            <el-radio-group v-model="ruleForm.ACTION_ORDER" class="input">
+              <el-radio label="RANDOM">随机</el-radio>
+              <el-radio label="FIXED">固定</el-radio>
+            </el-radio-group>
+          </el-form-item>
+
+          <el-form-item label="单个动作最大时长" prop="ACTION_DURATION">
+            <el-input
+              v-model="ruleForm.ACTION_DURATION"
+              style="width: 180px"
+            ></el-input>
+            <span style="font-size: 14px; line-height: 44px"> 秒</span>
+          </el-form-item>
+        </div>
+
         <el-form-item>
           <el-button
             :disabled="btnSaveDiabled"
@@ -109,7 +174,7 @@
 </template>
 <script>
 import { mapState } from "vuex";
-import { CORE_API } from "@/constants/constants.js";
+import { ACTION_OPTION_LIST, CORE_API } from "@/constants/constants.js";
 
 export default {
   data() {
@@ -124,7 +189,43 @@ export default {
         callback();
       }
     };
+
+    let checkFaceType = (rule, value, callback) => {
+      if (this.ruleForm.PC_CLIENT_ENABLED) {
+        if (value != "S3") {
+          return callback(new Error("请选择可用的 “活体检测方案”"));
+        }
+      } else {
+        if (value == "S3") {
+          return callback(new Error("请选择可用的 “活体检测方案”"));
+        }
+      }
+      callback();
+    };
+
+    let checkActionOptions = (rule, value, callback) => {
+      let pcClientEnabled = this.ruleForm.PC_CLIENT_ENABLED;
+      if (pcClientEnabled) {
+        if (this.ruleForm.ACTION_NUM != this.ruleForm.ACTION_OPTIONS.length) {
+          return callback(new Error("动作个数与动作选项数量不一致"));
+        }
+      }
+      callback();
+    };
+
+    let checkDuration = (rule, value, callback) => {
+      let pcClientEnabled = this.ruleForm.PC_CLIENT_ENABLED;
+      if (pcClientEnabled) {
+        let reg = /^[1-9][0-9]*$/;
+        if (!value.match(reg) || value < 5 || value > 20) {
+          return callback(new Error("范围5至20之间"));
+        }
+      }
+      callback();
+    };
+
     return {
+      actionOptions: ACTION_OPTION_LIST,
       rootOrgList: [],
       propertyGroupId: "",
       formDataChanged: false,
@@ -140,16 +241,12 @@ export default {
         APP_ENABLED: false,
         WEIXIN_ANSWER_ENABLED: false,
         IDENTIFICATION_OF_LIVING_BODY_SCHEME: "",
-        properties: {
-          STUDENT_CLIENT_ACCESS_FROM_THIRD_PARTY: false,
-          SHOW_STUDENT_CLIENT_APP_QRCODE: false,
-          STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL: "",
-          SHOW_QMTH_LOGO: false,
-          ID_NUMBER_PRIVATE_MODE: false,
-          APP_ENABLED: false,
-          WEIXIN_ANSWER_ENABLED: false,
-          IDENTIFICATION_OF_LIVING_BODY_SCHEME: "",
-        },
+        PC_CLIENT_ENABLED: false,
+        ACTION_ALERT: "",
+        ACTION_NUM: "",
+        ACTION_OPTIONS: [],
+        ACTION_ORDER: "",
+        ACTION_DURATION: "",
       },
       rules: {
         STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL: [
@@ -158,6 +255,32 @@ export default {
             trigger: "change",
           },
         ],
+        IDENTIFICATION_OF_LIVING_BODY_SCHEME: [
+          {
+            validator: checkFaceType,
+            trigger: "change",
+          },
+        ],
+        ACTION_OPTIONS: [
+          {
+            validator: checkActionOptions,
+            trigger: "change",
+          },
+        ],
+        ACTION_ALERT: [
+          { required: true, message: " ", trigger: "blur" },
+          {
+            validator: checkDuration,
+            trigger: "blur",
+          },
+        ],
+        ACTION_DURATION: [
+          { required: true, message: " ", trigger: "blur" },
+          {
+            validator: checkDuration,
+            trigger: "blur",
+          },
+        ],
       },
     };
   },
@@ -214,7 +337,13 @@ export default {
             newForm.SHOW_STUDENT_CLIENT_APP_QRCODE ==
               this.originalRuleForm.SHOW_STUDENT_CLIENT_APP_QRCODE &&
             newForm.IDENTIFICATION_OF_LIVING_BODY_SCHEME ==
-              this.originalRuleForm.IDENTIFICATION_OF_LIVING_BODY_SCHEME
+              this.originalRuleForm.IDENTIFICATION_OF_LIVING_BODY_SCHEME &&
+            newForm.PC_CLIENT_ENABLED ==
+              this.originalRuleForm.PC_CLIENT_ENABLED &&
+            newForm.ACTION_ALERT == this.originalRuleForm.ACTION_ALERT &&
+            newForm.ACTION_OPTIONS == this.originalRuleForm.ACTION_OPTIONS &&
+            newForm.ACTION_ORDER == this.originalRuleForm.ACTION_ORDER &&
+            newForm.ACTION_DURATION == this.originalRuleForm.ACTION_DURATION
           );
         } else {
           this.formDataChanged = false;
@@ -238,24 +367,42 @@ export default {
     submitForm(formName) {
       this.$refs[formName].validate((valid) => {
         if (valid) {
-          this.ruleForm.properties.STUDENT_CLIENT_ACCESS_FROM_THIRD_PARTY =
+          let params = {
+            orgId: null,
+            relatedPropertyGroupIdList: [],
+            properties: {},
+          };
+          params.orgId = this.ruleForm.orgId;
+          params.relatedPropertyGroupIdList =
+            this.ruleForm.relatedPropertyGroupIdList;
+
+          params.properties.STUDENT_CLIENT_ACCESS_FROM_THIRD_PARTY =
             this.ruleForm.STUDENT_CLIENT_ACCESS_FROM_THIRD_PARTY;
-          this.ruleForm.properties.STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL =
+          params.properties.STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL =
             this.ruleForm.STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL;
-          this.ruleForm.properties.SHOW_QMTH_LOGO =
-            this.ruleForm.SHOW_QMTH_LOGO;
-          this.ruleForm.properties.ID_NUMBER_PRIVATE_MODE =
+          params.properties.SHOW_QMTH_LOGO = this.ruleForm.SHOW_QMTH_LOGO;
+          params.properties.ID_NUMBER_PRIVATE_MODE =
             this.ruleForm.ID_NUMBER_PRIVATE_MODE;
-          this.ruleForm.properties.APP_ENABLED = this.ruleForm.APP_ENABLED;
-          this.ruleForm.properties.WEIXIN_ANSWER_ENABLED =
+          params.properties.APP_ENABLED = this.ruleForm.APP_ENABLED;
+          params.properties.WEIXIN_ANSWER_ENABLED =
             this.ruleForm.WEIXIN_ANSWER_ENABLED;
-          this.ruleForm.properties.SHOW_STUDENT_CLIENT_APP_QRCODE =
+          params.properties.SHOW_STUDENT_CLIENT_APP_QRCODE =
             this.ruleForm.SHOW_STUDENT_CLIENT_APP_QRCODE;
-          this.ruleForm.properties.IDENTIFICATION_OF_LIVING_BODY_SCHEME =
+          params.properties.IDENTIFICATION_OF_LIVING_BODY_SCHEME =
             this.ruleForm.IDENTIFICATION_OF_LIVING_BODY_SCHEME;
+          params.properties.PC_CLIENT_ENABLED = this.ruleForm.PC_CLIENT_ENABLED;
+
+          if (params.properties.PC_CLIENT_ENABLED) {
+            params.properties.ACTION_ALERT = this.ruleForm.ACTION_ALERT;
+            params.properties.ACTION_NUM = this.ruleForm.ACTION_NUM;
+            params.properties.ACTION_OPTIONS =
+              this.ruleForm.ACTION_OPTIONS.join(",");
+            params.properties.ACTION_ORDER = this.ruleForm.ACTION_ORDER;
+            params.properties.ACTION_DURATION = this.ruleForm.ACTION_DURATION;
+          }
 
           this.$httpWithMsg
-            .put(CORE_API + "/org/saveOrgProperties", this.ruleForm)
+            .put(CORE_API + "/org/saveOrgProperties", params)
             .then(
               () => {
                 this.$notify({
@@ -281,24 +428,66 @@ export default {
         this.ruleForm.orgId +
         "/" +
         this.propertyGroupId;
+
       this.$httpWithMsg.get(url).then((response) => {
         if (response) {
           this.ruleForm.STUDENT_CLIENT_ACCESS_FROM_THIRD_PARTY =
             response.data.STUDENT_CLIENT_ACCESS_FROM_THIRD_PARTY == "true";
+
           this.ruleForm.STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL =
             response.data.STUDENT_CLIENT_THIRD_PARTY_LOGIN_URL;
+
           this.ruleForm.SHOW_QMTH_LOGO = response.data.SHOW_QMTH_LOGO == "true";
+
           this.ruleForm.ID_NUMBER_PRIVATE_MODE =
             response.data.ID_NUMBER_PRIVATE_MODE == "true";
 
           this.ruleForm.APP_ENABLED = response.data.APP_ENABLED == "true";
+
           this.ruleForm.WEIXIN_ANSWER_ENABLED =
             response.data.WEIXIN_ANSWER_ENABLED == "true";
+
           this.ruleForm.SHOW_STUDENT_CLIENT_APP_QRCODE =
             response.data.SHOW_STUDENT_CLIENT_APP_QRCODE == "true";
+
           this.ruleForm.IDENTIFICATION_OF_LIVING_BODY_SCHEME =
             response.data.IDENTIFICATION_OF_LIVING_BODY_SCHEME;
 
+          // 未配置时,赋默认值
+          this.ruleForm.PC_CLIENT_ENABLED =
+            "true" == response.data.PC_CLIENT_ENABLED;
+
+          if (response.data.ACTION_ALERT) {
+            this.ruleForm.ACTION_ALERT = response.data.ACTION_ALERT;
+          } else {
+            this.ruleForm.ACTION_ALERT = 15;
+          }
+
+          if (response.data.ACTION_NUM) {
+            this.ruleForm.ACTION_NUM = response.data.ACTION_NUM;
+          } else {
+            this.ruleForm.ACTION_NUM = 2;
+          }
+
+          if (response.data.ACTION_OPTIONS) {
+            this.ruleForm.ACTION_OPTIONS =
+              response.data.ACTION_OPTIONS.split(",");
+          } else {
+            this.ruleForm.ACTION_OPTIONS = ["NOD", "SHAKE"];
+          }
+
+          if (response.data.ACTION_ORDER) {
+            this.ruleForm.ACTION_ORDER = response.data.ACTION_ORDER;
+          } else {
+            this.ruleForm.ACTION_ORDER = "RANDOM";
+          }
+
+          if (response.data.ACTION_DURATION) {
+            this.ruleForm.ACTION_DURATION = response.data.ACTION_DURATION;
+          } else {
+            this.ruleForm.ACTION_DURATION = 15;
+          }
+
           this.originalRuleForm = Object.assign({}, this.ruleForm);
         } else {
           this.$notify({

+ 54 - 0
src/modules/examwork/view/onlineExam.vue

@@ -415,6 +415,32 @@
                     </el-input>
                   </el-form-item>
                 </el-row>
+                <el-row>
+                  <el-form-item
+                    label="切屏次数限制"
+                    prop="MAX_SWITCH_SCREEN_COUNT"
+                    :label-width="style.label_width_tab2"
+                    ><el-tooltip
+                      :disabled="!maxSwitchScreenCountDisabled"
+                      placement="top"
+                    >
+                      <div slot="content">
+                        此设置不可用。考生端配置-防作弊设置中计算切屏次数未开启
+                      </div>
+                      <el-input
+                        v-model.trim.number="
+                          form.properties.MAX_SWITCH_SCREEN_COUNT
+                        "
+                        maxlength="5"
+                        auto-complete="off"
+                        class="input"
+                        :disabled="maxSwitchScreenCountDisabled"
+                      >
+                        <template slot="append">次</template>
+                      </el-input></el-tooltip
+                    >
+                  </el-form-item>
+                </el-row>
               </el-tab-pane>
               <el-tab-pane label="显示设置" name="tab3">
                 <el-row>
@@ -1044,6 +1070,20 @@ let validateMaxInterruptNum = (rule, value, callback) => {
     callback();
   }
 };
+let validateMaxSwitchScreenCount = (rule, value, callback) => {
+  let examReconnectTime = _this.form.properties.MAX_SWITCH_SCREEN_COUNT;
+  if (examReconnectTime === "") {
+    callback();
+  } else if (!examReconnectTime.toString().match(/^[0-9]\d*$/)) {
+    callback(new Error("只能是非负整数"));
+    if (!_this.toActiveName) {
+      _this.toActiveName = "tab2";
+      _this.activeName = "tab2";
+    }
+  } else {
+    callback();
+  }
+};
 
 let validateSnapshotInterval = (rule, value, callback) => {
   let isFaceEnable = _this.form.properties.IS_FACE_ENABLE;
@@ -1318,6 +1358,7 @@ export default {
       rootOrgWenXinAnswerEnabled: false,
       IDENTIFICATION_OF_LIVING_BODY_SCHEME: "S1",
       APP_ENABLED: false,
+      maxSwitchScreenCountDisabled: true,
       form: {
         started: false,
         name: "",
@@ -1335,6 +1376,7 @@ export default {
           IS_OBJ_SCORE_VIEW: "true",
           IS_STRANGER_ENABLE: "false",
           MAX_INTERRUPT_NUM: "",
+          MAX_SWITCH_SCREEN_COUNT: "",
           EXAM_RECONNECT_TIME: 30,
           FREEZE_TIME: 0,
           BEFORE_EXAM_REMARK: "",
@@ -1412,6 +1454,13 @@ export default {
             trigger: "blur",
           },
         ],
+        MAX_SWITCH_SCREEN_COUNT: [
+          {
+            required: false,
+            validator: validateMaxSwitchScreenCount,
+            trigger: "blur",
+          },
+        ],
         SNAPSHOT_INTERVAL: [
           {
             required: true,
@@ -1655,6 +1704,11 @@ export default {
       this.getOrgProperty("APP_ENABLED", function (res) {
         that.APP_ENABLED = res;
       });
+      this.getOrgProperty("PREVENT_CHEATING_CONFIG", function (res) {
+        if (res && res.indexOf("RECORD_SWITCH_SCREEN") != -1) {
+          that.maxSwitchScreenCountDisabled = false;
+        }
+      });
     },
     getOrgProperty: function (propkey, callback) {
       let url =

+ 56 - 0
src/modules/examwork/view/onlineHomework.vue

@@ -386,6 +386,33 @@
                     </el-input>
                   </el-form-item>
                 </el-row>
+                <el-row>
+                  <el-form-item
+                    label="切屏次数限制"
+                    prop="MAX_SWITCH_SCREEN_COUNT"
+                    :label-width="style.label_width_tab2"
+                  >
+                    <el-tooltip
+                      :disabled="!maxSwitchScreenCountDisabled"
+                      placement="top"
+                    >
+                      <div slot="content">
+                        此设置不可用。考生端配置-防作弊设置中计算切屏次数未开启
+                      </div>
+                      <el-input
+                        v-model.trim.number="
+                          form.properties.MAX_SWITCH_SCREEN_COUNT
+                        "
+                        maxlength="5"
+                        auto-complete="off"
+                        class="input"
+                        :disabled="maxSwitchScreenCountDisabled"
+                      >
+                        <template slot="append">次</template>
+                      </el-input>
+                    </el-tooltip>
+                  </el-form-item>
+                </el-row>
               </el-tab-pane>
               <el-tab-pane label="显示设置" name="tab3">
                 <el-row>
@@ -795,6 +822,21 @@ let validateMaxInterruptNum = (rule, value, callback) => {
   }
 };
 
+let validateMaxSwitchScreenCount = (rule, value, callback) => {
+  let examReconnectTime = _this.form.properties.MAX_SWITCH_SCREEN_COUNT;
+  if (examReconnectTime === "") {
+    callback();
+  } else if (!examReconnectTime.toString().match(/^[0-9]\d*$/)) {
+    callback(new Error("只能是非负整数"));
+    if (!_this.toActiveName) {
+      _this.toActiveName = "tab2";
+      _this.activeName = "tab2";
+    }
+  } else {
+    callback();
+  }
+};
+
 export default {
   components: {
     ckeditor,
@@ -821,6 +863,7 @@ export default {
       is_face_enable_diabled: true,
       rootOrgWenXinAnswerEnabled: false,
       IDENTIFICATION_OF_LIVING_BODY_SCHEME: "S1",
+      maxSwitchScreenCountDisabled: true,
       form: {
         started: false,
         name: "",
@@ -838,6 +881,7 @@ export default {
           IS_OBJ_SCORE_VIEW: "true",
           IS_STRANGER_ENABLE: "false",
           MAX_INTERRUPT_NUM: "",
+          MAX_SWITCH_SCREEN_COUNT: "",
           EXAM_RECONNECT_TIME: 30,
           FREEZE_TIME: 0,
           BEFORE_EXAM_REMARK: "",
@@ -913,6 +957,13 @@ export default {
             trigger: "blur",
           },
         ],
+        MAX_SWITCH_SCREEN_COUNT: [
+          {
+            required: false,
+            validator: validateMaxSwitchScreenCount,
+            trigger: "blur",
+          },
+        ],
         examCycleWeekArr: [
           {
             required: true,
@@ -1098,6 +1149,11 @@ export default {
           that.form.IDENTIFICATION_OF_LIVING_BODY_SCHEME = res;
         }
       );
+      this.getOrgProperty("PREVENT_CHEATING_CONFIG", function (res) {
+        if (res && res.indexOf("RECORD_SWITCH_SCREEN") != -1) {
+          that.maxSwitchScreenCountDisabled = false;
+        }
+      });
     },
     getOrgProperty: function (propkey, callback) {
       let url =

+ 114 - 2
src/modules/marking/views/MarkerDetail.vue

@@ -61,9 +61,14 @@
           >
             <el-table-column label="课程名称" width="200" prop="name" />
             <el-table-column label="课程代码" min-width="100" prop="code" />
+            <el-table-column
+              label="设置任务数"
+              min-width="100"
+              prop="limitCount"
+            />
             <el-table-column
               label="已评数量"
-              min-width="200"
+              min-width="100"
               prop="markedCount"
               sortable
             />
@@ -81,6 +86,19 @@
                 </div>
               </template>
             </el-table-column>
+            <el-table-column :context="_self" width="250" label="操作">
+              <template slot-scope="scope">
+                <div class="pull-left">
+                  <el-button
+                    type="primary"
+                    size="mini"
+                    plain
+                    @click="openStepModel(scope.row)"
+                    >设置任务数</el-button
+                  >
+                </div>
+              </template>
+            </el-table-column>
           </el-table>
           <div class="page pull-right">
             <el-pagination
@@ -95,6 +113,37 @@
             ></el-pagination>
           </div>
         </div>
+        <el-dialog
+          title="设置任务数"
+          width="500px"
+          :visible.sync="stepModel"
+          :close-on-click-modal="false"
+          @close="closeStepModel"
+        >
+          <el-form
+            ref="stepForm"
+            :key="stepModelKey"
+            :inline="true"
+            :model="stepForm"
+            :rules="stepRules"
+          >
+            <el-row>
+              <el-form-item label="设置任务数" prop="limitCount">
+                <el-input v-model="stepForm.limitCount" size="mini"></el-input>
+              </el-form-item>
+            </el-row>
+            <div style="margin-bottom: 20px"></div>
+            <el-row class="pull-center">
+              <el-button
+                type="primary"
+                :loading="stepForm.loading"
+                @click="subStep"
+                >确定</el-button
+              >
+              <el-button @click="closeStepModel">取消</el-button>
+            </el-row>
+          </el-form>
+        </el-dialog>
       </div>
     </section>
   </div>
@@ -104,6 +153,15 @@
 import { MARKING_API } from "@/constants/constants";
 import { mapState } from "vuex";
 import LinkTitlesCustom from "@/components/LinkTitlesCustom.vue";
+
+let reg = /^[1-9][0-9]*$/;
+let validateLimitCount = (rule, value, callback) => {
+  if (value && !reg.test(value)) {
+    callback(new Error("设置任务数必须是正整数"));
+  } else {
+    callback();
+  }
+};
 export default {
   components: {
     LinkTitlesCustom,
@@ -122,6 +180,23 @@ export default {
       loading: true,
       workId: "",
       courseCode: "",
+      stepModel: false,
+      stepModelKey: Math.random(),
+      stepForm: {
+        workId: null,
+        courseCode: null,
+        limitCount: null,
+        loading: false,
+      },
+      stepRules: {
+        limitCount: [
+          {
+            required: false,
+            validator: validateLimitCount,
+            trigger: "change",
+          },
+        ],
+      },
     };
   },
   computed: {
@@ -129,11 +204,48 @@ export default {
   },
   created() {
     this.workId = this.$route.params.workId;
-    this.userName = this.$route.params.userName;
     this.userId = this.$route.params.markerId;
     this.initSetting();
   },
   methods: {
+    openStepModel(row) {
+      this.stepForm.limitCount = row.limitCount;
+      this.stepForm.workId = row.workId;
+      this.stepForm.courseCode = row.code;
+      this.stepModel = true;
+    },
+    closeStepModel() {
+      this.stepModel = false;
+      this.stepModelKey = Math.random();
+    },
+    async subStep() {
+      let res = await this.$refs.stepForm.validate();
+      if (!res) {
+        return;
+      }
+      this.stepForm.loading = true;
+      var url =
+        MARKING_API +
+        "/markCourses/task-limit?workId=" +
+        this.stepForm.workId +
+        "&courseCode=" +
+        this.stepForm.courseCode +
+        "&userId=" +
+        this.userId +
+        "&limitCount=" +
+        this.stepForm.limitCount;
+      this.$httpWithMsg
+        .put(url)
+        .then(() => {
+          this.$notify({
+            type: "success",
+            message: "设置成功!",
+          });
+          this.closeStepModel();
+          this.initSetting();
+        })
+        .finally(() => (this.stepForm.loading = false));
+    },
     exportIt() {
       var key = this.user.key;
       var token = this.user.token;

+ 24 - 13
src/modules/marking/views/Marking.vue

@@ -344,7 +344,7 @@ export default {
         return 0;
       } else {
         for (let [index, task] of this.tasks.entries()) {
-          if (task.leftCount > 0) {
+          if (task.leftCount > 0 && !task.exceedLimit) {
             return index;
           }
         }
@@ -468,7 +468,10 @@ export default {
           //切换任务提交试卷后,继续维持该任务
           if (self.changeTaskCur != self.taskCur) {
             //切换任务评完后,继续跳转原来的任务
-            if (self.tasks[self.changeTaskCur].leftCount == 0) {
+            if (
+              self.tasks[self.changeTaskCur].leftCount == 0 ||
+              self.tasks[self.changeTaskCur].exceedLimit
+            ) {
               if (self.tasks[self.taskCur]) {
                 self.task = self.tasks[self.taskCur];
               }
@@ -545,20 +548,28 @@ export default {
       await self.$http
         .get(DATA_PROCESS_API + "/studentPapers?markTaskId=" + self.task.id)
         .then((response) => {
-          if (!response.data) {
-            self.$notify({
-              message: "该任务下试卷已评完,如有剩余任务将自动切换任务",
-              type: "warning",
-            });
+          if (response.data.resultCode == "0") {
+            self.studentPaper = response.data.data;
+            self.examType = self.studentPaper.examType;
+            self.paperMark = true;
+            console.log("paper", self.studentPaper);
+          } else {
+            if (response.data.resultCode == "1") {
+              self.$notify({
+                message: "该任务下试卷已评完,如有剩余任务将自动切换任务",
+                type: "warning",
+              });
+            } else if (response.data.resultCode == "2") {
+              self.$notify({
+                message: "您设置的阅卷任务数已完成",
+                type: "warning",
+              });
+            }
+
             this.resultItems.splice(0, this.resultItems.length);
             self.studentPaper = { id: "" };
             self.paperMark = false;
             return false;
-          } else {
-            self.studentPaper = response.data;
-            self.examType = response.data.examType;
-            self.paperMark = true;
-            console.log("paper", self.studentPaper);
           }
         });
       return true;
@@ -589,7 +600,7 @@ export default {
         .get(DATA_PROCESS_API + "/studentPapers/" + studentPaperId)
         .then((response) => {
           self.studentPaper = response.data;
-          self.examType = response.data.examType;
+          self.examType = self.studentPaper.examType;
         });
     },
     async getMarkedResultItems(markResultId) {

+ 9 - 1
src/modules/oe/component/commonExport.vue

@@ -18,6 +18,7 @@
   </span>
 </template>
 <script>
+import { mapState } from "vuex";
 export default {
   props: {
     form: {
@@ -36,8 +37,15 @@ export default {
   data() {
     return { exportLoading: false };
   },
+  computed: {
+    ...mapState({
+      user: (state) => state.user,
+    }),
+  },
   methods: {
     exportData() {
+      var key = this.user.key;
+      var token = this.user.token;
       if (!this.form.examId) {
         this.$notify({
           title: "警告",
@@ -54,7 +62,7 @@ export default {
       }).then(() => {
         this.exportLoading = true;
         this.$http
-          .get(this.exportUrl, {
+          .get(this.exportUrl + "?$key=" + key + "&$token=" + token, {
             params: {
               query: this.form,
             },

+ 18 - 19
src/modules/oe/component/commonForm.vue

@@ -1,6 +1,6 @@
 <template>
   <!-- eslint-disable vue/no-mutating-props -->
-  <el-form :model="form" label-width="70px">
+  <el-form :model="form" label-width="85px">
     <el-col :span="6">
       <el-form-item label="考试">
         <el-select
@@ -50,7 +50,6 @@
     <el-col :span="6">
       <el-form-item label="学习中心">
         <el-select
-          v-if="currentPagePrivileges.ORG_FIND_ALL"
           v-model="form.orgId"
           class="form_search_width"
           filterable
@@ -69,14 +68,14 @@
             :value="item.id"
           ></el-option>
         </el-select>
-        <el-input
+        <!-- <el-input
           v-if="!currentPagePrivileges.ORG_FIND_ALL"
           v-model="orgName"
           class="form_search_width"
           size="small"
           :disabled="true"
         ></el-input>
-        <el-radio v-show="false" v-model="form.ORG_FIND_ALL"></el-radio>
+        <el-radio v-show="false" v-model="form.ORG_FIND_ALL"></el-radio> -->
       </el-form-item>
     </el-col>
     <el-col :span="6">
@@ -186,21 +185,21 @@ export default {
       "privilegeCodes",
       Object.keys(this.currentPagePrivileges).toString()
     );
-    this.$http
-      .post("/api/ecs_core/rolePrivilege/checkPrivileges?" + params)
-      .then((response) => {
-        this.currentPagePrivileges = response.data;
-        this.form.ORG_FIND_ALL = this.currentPagePrivileges.ORG_FIND_ALL;
-        if (!this.currentPagePrivileges.ORG_FIND_ALL) {
-          var userId = this.user.userId;
-          this.$http.get("/api/ecs_core/user/" + userId).then((response) => {
-            this.form.orgId = response.data.orgId;
-            this.orgName = response.data.orgName;
-          });
-        } else {
-          this.getOrgs("");
-        }
-      });
+    // this.$http
+    //   .post("/api/ecs_core/rolePrivilege/checkPrivileges?" + params)
+    //   .then((response) => {
+    //     this.currentPagePrivileges = response.data;
+    //     this.form.ORG_FIND_ALL = this.currentPagePrivileges.ORG_FIND_ALL;
+    //     if (!this.currentPagePrivileges.ORG_FIND_ALL) {
+    //       var userId = this.user.userId;
+    //       this.$http.get("/api/ecs_core/user/" + userId).then((response) => {
+    //         this.form.orgId = response.data.orgId;
+    //         this.orgName = response.data.orgName;
+    //       });
+    //     } else {
+    this.getOrgs("");
+    //     }
+    //   });
   },
   methods: {
     getExams(examName) {

+ 2 - 8
src/modules/oe/views/captureDetail.vue

@@ -131,7 +131,7 @@
                   sortable
                   label="切屏次数"
                   prop="switchScreenCount"
-                  width="100"
+                  width="110"
                 ></el-table-column>
                 <el-table-column
                   sortable
@@ -200,13 +200,7 @@
             >
               <div v-show="item.pass" class="photo-pass">通过</div>
               <div v-show="!item.pass" class="photo-nopass">不通过</div>
-              <img
-                class="photo"
-                :title="item.virtualCameraNames"
-                :src="item.fileUrl"
-                alt
-                width="200"
-              />
+              <img class="photo" :src="item.fileUrl" alt width="200" />
               <div v-show="item.stranger" class="photo-stranger">陌生人</div>
               <div
                 v-show="!item.isFacelivenessPass"

+ 43 - 15
src/modules/oe/views/examDetail.vue

@@ -27,20 +27,24 @@
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="开考时间">
-              <el-date-picker
-                v-model="startExamDatetimeRange"
-                class="input"
-                type="datetimerange"
-                start-placeholder="开始日期"
-                range-separator="至"
-                end-placeholder="结束日期"
-                value-format="yyyy/MM/dd HH:mm:ss"
-                :clearable="false"
+          <el-col :span="6">
+            <el-form-item label="虚拟设备名">
+              <el-input
+                v-model="form.virtualName"
+                class="form_search_width"
                 size="small"
-                @change="changeStartExamDatetimeRange"
-              ></el-date-picker>
+                placeholder="虚拟设备名"
+              ></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="IP">
+              <el-input
+                v-model="form.ip"
+                class="form_search_width"
+                size="small"
+                placeholder="IP"
+              ></el-input>
             </el-form-item>
           </el-col>
         </el-row>
@@ -85,6 +89,24 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row v-show="showAllCondition">
+          <el-col :span="12">
+            <el-form-item label="开考时间">
+              <el-date-picker
+                v-model="startExamDatetimeRange"
+                class="input"
+                type="datetimerange"
+                start-placeholder="开始日期"
+                range-separator="至"
+                end-placeholder="结束日期"
+                value-format="yyyy/MM/dd HH:mm:ss"
+                :clearable="false"
+                size="small"
+                @change="changeStartExamDatetimeRange"
+              ></el-date-picker>
+            </el-form-item>
+          </el-col>
+        </el-row>
       </commonFormVue>
       <el-col :span="24">
         <el-button
@@ -321,6 +343,12 @@
               prop="virtualCameraNames"
               width="120"
             ></el-table-column>
+            <el-table-column
+              sortable
+              label="IP"
+              prop="ip"
+              width="120"
+            ></el-table-column>
             <el-table-column
               sortable
               :sort-method="sortByPaperTotalScore"
@@ -572,6 +600,8 @@ export default {
       dialogVisible: false,
       // uploadDisabled: true,
       form: {
+        virtualName: null,
+        ip: null,
         switchScreenCountStart: null,
         switchScreenCountEnd: null,
         rootOrgId: null,
@@ -1032,8 +1062,6 @@ export default {
 .el-date-editor.el-input__inner {
   width: auto !important;
 }
-.upload-demo {
-}
 .disabled .el-upload--picture-card {
   display: none;
 }

+ 7 - 1
src/modules/portal/views/home/main/HomeMain.vue

@@ -16,7 +16,13 @@
             <div class="cover"></div>
           </div>
           <div
-            class="align-self-left d-flex d-flex flex-column align-items-start module-desc"
+            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">

+ 1 - 1
src/modules/questions/views/ExportTemplate.vue

@@ -94,7 +94,7 @@
         </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 width="180" prop="creationTime" label="上传时间">
         </el-table-column>
         <el-table-column width="150" prop="createUser" label="上传人">
         </el-table-column>