123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- <template>
- <div>
- <el-tabs v-model="activeName" type="card">
- <el-tab-pane label="考试规则设置" name="first">
- <el-form
- ref="form1"
- :model="form"
- :rules="rules"
- label-width="170px"
- inline
- >
- <el-row>
- <el-form-item label="考试模式" prop="mode">
- <ExamTypeSelect
- :disabled="isEdit"
- v-model="form.mode"
- ></ExamTypeSelect>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="批次编码" prop="code">
- <el-input :disabled="isEdit" v-model.trim="form.code"></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="批次名称" prop="name">
- <el-input v-model.trim="form.name"></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考试时间" prop="startEndTimeProxy">
- <el-date-picker
- v-model="form.startEndTimeProxy"
- type="datetimerange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- >
- </el-date-picker>
- </el-form-item>
- </el-row>
- <el-row v-if="!isModeAnytime">
- <el-form-item label="候考时长(分钟)">
- <MinuteInput v-model.trim="form.prepareSeconds"> </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考试次数限制">
- <el-input v-model.number.trim="form.examCount"></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考试时长(分钟)">
- <MinuteInput v-model.trim="form.maxDurationSeconds">
- </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row v-if="!isModeAnytime">
- <el-form-item label="迟到时长(分钟)">
- <MinuteInput v-model.trim="form.openingSeconds"> </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="冻结时间(分钟)">
- <MinuteInput v-model.trim="form.minDurationSeconds">
- </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row v-if="!isModeAnytime">
- <el-form-item label="启用集中收卷">
- <el-radio v-model="form.forceFinish" :label="1">是</el-radio>
- <el-radio v-model="form.forceFinish" :label="0">否</el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="启用开考口令">
- <el-radio v-model="enableShortCodeProxy" :label="true"
- >是</el-radio
- >
- <el-radio v-model="enableShortCodeProxy" :label="false"
- >否</el-radio
- >
- <el-input
- v-if="enableShortCodeProxy"
- v-model.trim="form.shortCode"
- ></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否允许断点续考">
- <el-radio v-model="enableBreakProxy" :label="true">是</el-radio>
- <el-radio v-model="enableBreakProxy" :label="false">否</el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item v-if="enableBreakProxy" label="断点次数">
- <el-input v-model.trim="form.breakResumeCount"></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item v-if="enableBreakProxy" label="断点时长(分钟)">
- <MinuteInput v-model.trim="form.breakExpireSeconds">
- </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="重考是否需要审核">
- <el-radio v-model="form.reexamAuditing" :label="1">是</el-radio>
- <el-radio v-model="form.reexamAuditing" :label="0">否</el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="取分策略">
- <el-radio
- v-model="form.recordSelectStrategy"
- :disabled="isEdit"
- label="LATEST"
- >
- 最后一次提交
- </el-radio>
- <el-radio
- v-model="form.recordSelectStrategy"
- label="HIGHEST_OBJECTIVE_SCORE"
- :disabled="isEdit"
- >
- 客观分最高
- </el-radio>
- <el-radio
- v-model="form.recordSelectStrategy"
- label="HIGHEST_TOTAL_SCORE"
- :disabled="isEdit"
- >
- 总分最高
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="多选题给分规则">
- <el-radio v-model="form.objectiveScorePolicy" label="EQUAL">
- 全对给分
- </el-radio>
- <el-radio v-model="form.objectiveScorePolicy" label="PARTIAL">
- 漏选给一半分
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否显示客观分">
- <el-radio v-model="form.showObjectiveScore" :label="1">
- 是
- </el-radio>
- <el-radio v-model="form.showObjectiveScore" :label="0">
- 否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否允许摄像头拍照作答">
- <el-radio v-model="form.cameraPhotoUpload" :label="1">
- 是
- </el-radio>
- <el-radio v-model="form.cameraPhotoUpload" :label="0">
- 否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否允许小程序作答">
- <el-radio v-model="form.mobilePhotoUpload" :label="1">
- 是
- </el-radio>
- <el-radio v-model="form.mobilePhotoUpload" :label="0">
- 否
- </el-radio>
- </el-form-item>
- </el-row>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="监考设置" name="second">
- <el-form
- ref="form2"
- :model="form"
- :rules="rules"
- label-width="180px"
- inline
- >
- <el-row class="tab-invililation">
- <h2>开考检测</h2>
- <el-form-item label="">
- <div class="d-flex flex-column tab-invililation-radio">
- <el-radio v-model="form.entryAuthenticationPolicy" label="OFF">
- 安全级别:<span style="color: #202b4b; font-size: 20px;">
- 无
- </span>
- <span class="desc-txt">
- 开考无检测
- </span>
- </el-radio>
- <el-radio
- v-model="form.entryAuthenticationPolicy"
- label="FACE_VERIFY_OPTIONAL"
- >
- 安全级别:<span style="color: #fe5863; font-size: 20px;">
- 低
- </span>
- <span class="desc-txt"> 开考人脸比对,不强制比对 </span>
- </el-radio>
- <el-radio
- v-model="form.entryAuthenticationPolicy"
- label="FACE_VERIFY_FORCE"
- >
- 安全级别:<span style="color: #ff9f43; font-size: 20px;">
- 中
- </span>
- <span class="desc-txt"> 开考人脸强制比对 </span>
- </el-radio>
- <el-radio
- v-model="form.entryAuthenticationPolicy"
- label="LIVENESS_VERIFY"
- >
- 安全级别:<span style="color: #1cd1a1; font-size: 20px;">
- 高
- </span>
- <span class="desc-txt"> 开考人脸比对及活体验证 </span>
- </el-radio>
- </div>
- </el-form-item>
- </el-row>
- <el-row class="tab-invililation">
- <h2>过程监控</h2>
- <el-form-item label="是否考中人脸识别">
- <el-radio v-model="form.inProcessFaceVerify" :label="1"
- >是
- </el-radio>
- <el-radio v-model="form.inProcessFaceVerify" :label="0"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否考中陌生人脸识别">
- <el-radio v-model="form.inProcessFaceStrangerIgnore" :label="0"
- >是
- </el-radio>
- <el-radio v-model="form.inProcessFaceStrangerIgnore" :label="1"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否考中活体检测">
- <el-radio v-model="form.inProcessLivenessVerify" :label="1"
- >是
- </el-radio>
- <el-radio v-model="form.inProcessLivenessVerify" :label="0"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row v-if="form.inProcessLivenessVerify">
- <el-form-item
- label="活体验证弹出时间段"
- prop="inProcessLivenessFixedRange"
- >
- <MinuteInput
- v-model.trim="form.inProcessLivenessFixedRange[0]"
- style="width: 150px;"
- />
- ~
- <MinuteInput
- v-model.trim="form.inProcessLivenessFixedRange[1]"
- style="width: 150px;"
- />
- <span style="color: red;">
- *活体验证将该时间段内随机出现,且时间段设置不能超过冻结时间
- </span>
- </el-form-item>
- </el-row>
- <el-row v-if="form.inProcessLivenessVerify">
- <el-form-item label="活体验证结果的判定方案">
- <el-radio v-model="form.inProcessLivenessJudgePolicy" label="ANY">
- 单条成功则通过
- </el-radio>
- <el-radio v-model="form.inProcessLivenessJudgePolicy" label="ALL">
- 所有验证成功则通过
- </el-radio>
- <el-radio
- v-model="form.inProcessLivenessJudgePolicy"
- label="MORE"
- >
- 成功次数大于失败则通过
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row class="tab-invililation">
- <h2>监考直播</h2>
- <el-form-item label="是否开启考生端监考直播">
- <el-radio v-model="form.monitorProxy" :label="true">是 </el-radio>
- <el-radio v-model="form.monitorProxy" :label="false"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item v-if="form.monitorProxy" label="是否需要视频转录">
- <el-radio v-model="form.monitorRecord" :label="1">是 </el-radio>
- <el-radio v-model="form.monitorRecord" :label="0">否 </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item v-if="form.monitorProxy" label="电脑&手机监控方案">
- <el-checkbox-group v-model="form.monitorVideoSource">
- <el-checkbox label="CLIENT_CAMERA"
- >电脑摄像头为主机位</el-checkbox
- >
- <el-checkbox label="CLIENT_SCREEN">电脑开启录频</el-checkbox>
- <el-checkbox label="MOBILE_FIRST">手机主机位</el-checkbox>
- <el-checkbox
- :disabled="!form.monitorVideoSource.includes('MOBILE_FIRST')"
- label="MOBILE_SECOND"
- >手机辅机位</el-checkbox
- >
- </el-checkbox-group>
- <span style="color: red;">*主机位设备负责收音及播放监考提示</span>
- </el-form-item>
- </el-row>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="其他设置" name="third">
- <el-form :model="form" label-width="170px" inline>
- <el-row>
- <el-form-item label="考试须知">
- <el-input v-model.trim="form.preNotice"></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="须知强制阅读时长">
- <el-input v-model.trim="form.preNoticeStaySeconds">
- <template slot="append">秒</template>
- </el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考后说明">
- <el-input v-model.trim="form.postNotice"></el-input>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="IP限制">
- <el-radio v-model="form.enableIpLimit" :label="1">是</el-radio>
- <el-radio v-model="form.enableIpLimit" :label="0">否</el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="IP段(*表示任意):">
- <el-input v-model.trim="form.ipAllow"></el-input>
- </el-form-item>
- </el-row>
- </el-form>
- </el-tab-pane>
- </el-tabs>
- <div class="tab-footer">
- <el-button type="primary" @click="save" :loading="loading"
- >保存</el-button
- >
- <el-button @click="cancel">取消</el-button>
- </div>
- </div>
- </template>
- <script>
- import ExamTypeSelect from "@/components/ExamTypeSelect";
- import MinuteInput from "@/components/MinuteInput";
- import { saveExam, getExamDetail } from "@/api/examwork-exam";
- import { isNumber } from "lodash-es";
- export default {
- name: "ExamEdit",
- components: { ExamTypeSelect, MinuteInput },
- computed: {
- examId() {
- return this.$route.params.id;
- },
- isEdit() {
- return !!this.examId;
- },
- isModeAnytime() {
- return this.form.mode === "ANYTIME";
- },
- enableShortCodeProxy: {
- get() {
- return !!this.form.shortCode;
- },
- set(v) {
- if (v) {
- this.form.shortCode = "123";
- } else {
- this.form.shortCode = null;
- }
- },
- },
- enableBreakProxy: {
- get() {
- return !!(this.form.breakResumeCount || this.form.breakExpireSeconds);
- },
- set(v) {
- if (v) {
- this.form.breakResumeCount = 0;
- this.form.breakExpireSeconds = 0;
- } else {
- this.form.breakResumeCount = null;
- this.form.breakExpireSeconds = null;
- }
- },
- },
- },
- watch: {
- "form.startEndTimeProxy": {
- immediate: true,
- handler(v) {
- this.form.startTime = v[0];
- this.form.endTime = v[1];
- },
- },
- "form.monitorProxy": {
- immediate: true,
- handler(v) {
- if (!v) {
- this.form.monitorVideoSource = [];
- this.form.monitorRecord = 0;
- }
- if (this.form.monitorVideoSource === null) {
- this.form.monitorVideoSource = [];
- }
- },
- },
- "form.monitorVideoSource": {
- immediate: true,
- handler(v, ov) {
- if (!v) {
- this.form.monitorVideoSource = [];
- }
- if (
- // 没动静,不修改,避免死循环
- (v || []).includes("MOBILE_FIRST") !==
- (ov || []).includes("MOBILE_FIRST") &&
- !this.form.monitorVideoSource.includes("MOBILE_FIRST")
- ) {
- this.form.monitorVideoSource = this.form.monitorVideoSource.filter(
- (v) => v !== "MOBILE_SECOND"
- );
- }
- },
- },
- },
- async created() {
- if (this.isEdit) {
- const res = await getExamDetail({ id: this.examId });
- this.form = { ...this.form, ...res.data.data };
- if (!this.form.inProcessLivenessFixedRange) {
- this.form.inProcessLivenessFixedRange = [0, 0];
- }
- this.form.startEndTimeProxy = [this.form.startTime, this.form.endTime];
- this.form.monitorProxy = !!this.form.monitorVideoSource;
- }
- },
- data() {
- return {
- activeName: "first",
- form: {
- mode: "",
- code: "",
- name: "",
- startEndTimeProxy: [],
- startTime: 0,
- endTime: 0,
- prepareSeconds: 0,
- maxDurationSeconds: 0,
- openingSeconds: 0,
- minDurationSeconds: 0,
- forceFinish: 1,
- shortCode: "",
- breakResumeCount: 0,
- breakExpireSeconds: 0,
- reexamAuditing: 0,
- recordSelectStrategy: "HIGHEST_OBJECTIVE_SCORE",
- objectiveScorePolicy: "EQUAL",
- showObjectiveScore: 0,
- cameraPhotoUpload: 0,
- mobilePhotoUpload: 0,
- entryAuthenticationPolicy: "OFF",
- inProcessFaceVerify: 0,
- inProcessFaceStrangerIgnore: 0,
- inProcessLivenessVerify: 0,
- inProcessLivenessFixedRange: [0, 0],
- inProcessLivenessJudgePolicy: "ALL",
- monitorProxy: false,
- monitorRecord: 0,
- monitorVideoSource: [],
- ipAllow: "",
- },
- rules: {
- mode: { required: true, message: "必填" },
- name: { required: true, message: "必填" },
- code: { required: true, message: "必填" },
- startEndTimeProxy: { required: true, message: "必填" },
- inProcessLivenessFixedRange: {
- validator: (rule, value) => {
- return new Promise((resolve, reject) => {
- const isNull = value === null;
- if (
- (this.form.inProcessLivenessVerify === 0 && isNull) ||
- (value.length === 2 &&
- isNumber(value[0]) &&
- isNumber(value[1]) &&
- value[0] < value[1] &&
- value[0] > 0 &&
- value[1] < this.form.minDurationSeconds)
- ) {
- resolve();
- return;
- }
- if (this.form.inProcessLivenessVerify && value[0] <= 0) {
- reject("开始时间必须大于0");
- } else if (
- this.form.inProcessLivenessVerify &&
- value[1] >= this.form.minDurationSeconds
- ) {
- reject("结束时间必须小于冻结时间");
- } else {
- reject("格式错误");
- }
- });
- },
- // message: "格式错误",
- },
- },
- loading: false,
- };
- },
- methods: {
- async save() {
- try {
- await this.$refs.form1.validate();
- await this.$refs.form2.validate();
- // await this.$refs.form3.validate();
- } catch (error) {
- console.log(error);
- return;
- }
- try {
- this.loading = true;
- await saveExam(this.form);
- this.$notify({ title: "保存成功", type: "success" });
- this.$router.back();
- } finally {
- this.loading = false;
- }
- },
- cancel() {
- this.$router.back();
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .tab-invililation {
- > h2 {
- font-size: 20px;
- font-weight: 500;
- color: #8c94ac;
- line-height: 28px;
- margin-bottom: 26px;
- }
- &-radio {
- padding-left: 30px;
- .el-radio {
- margin-bottom: 18px;
- height: 30px;
- }
- }
- .desc-txt {
- color: #fe5863;
- border: 1px solid #fe5863;
- border-radius: 0 6px 6px 0;
- padding: 6px 10px 6px 16px;
- margin-left: 20px;
- border-left: 0;
- position: relative;
- &::before {
- content: "";
- display: block;
- position: absolute;
- width: 12px;
- height: 30px;
- left: -11px;
- top: -1px;
- background-image: url(../../../assets/bg-angle.png);
- background-repeat: no-repeat;
- background-size: 100% 100%;
- }
- }
- }
- </style>
|