12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238 |
- <template>
- <div v-loading="!orgSetting">
- <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
- :disabled="disableEdit"
- >
- <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="结束日期"
- value-format="timestamp"
- >
- </el-date-picker>
- </el-form-item>
- </el-row>
- <el-row v-if="!isModeAnytime">
- <el-form-item label="候考时长(分钟)">
- <MinuteInput
- v-model.trim="form.prepareSeconds"
- :min="0"
- :max="10000"
- >
- </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考试次数限制" prop="examCount">
- <el-input-number
- v-model.number.trim="form.examCount"
- :min="1"
- :max="10000"
- ></el-input-number>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考试时长(分钟)" prop="maxDurationSeconds">
- <MinuteInput v-model.trim="form.maxDurationSeconds">
- </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row v-if="!isModeAnytime">
- <el-form-item label="迟到时长(分钟)" prop="openingSeconds">
- <MinuteInput v-model.trim="form.openingSeconds"> </MinuteInput>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="冻结时间(分钟)" prop="minDurationSeconds">
- <MinuteInput v-model.trim="form.minDurationSeconds" />
- </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-number
- v-model.trim="form.breakResumeCount"
- :min="0"
- :max="10000"
- ></el-input-number>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item
- v-if="enableBreakProxy"
- prop="breakExpireSeconds"
- 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"
- :disabled="disableEdit"
- >
- <el-row class="tab-invililation">
- <h2>开考检测</h2>
- <el-form-item label="" label-width="0px">
- <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: #1cd1a1; 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"
- :disabled="orgSettingDisableLiveness"
- :title="orgSettingDisableLiveness && '原因:机构禁用活体'"
- label="LIVENESS_VERIFY"
- >
- 安全级别:<span style="color: red; 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
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.inProcessFaceVerify"
- :label="1"
- >是
- </el-radio>
- <el-radio
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.inProcessFaceVerify"
- :label="0"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row v-if="form.inProcessFaceVerify">
- <el-form-item label="是否考中陌生人脸识别">
- <el-radio
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.inProcessFaceStrangerIgnore"
- :label="0"
- >是
- </el-radio>
- <el-radio
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.inProcessFaceStrangerIgnore"
- :label="1"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row v-if="form.inProcessFaceVerify">
- <el-form-item label="是否考中开启真实性检测">
- <el-radio
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.inProcessRealnessVerify"
- :label="1"
- >是
- </el-radio>
- <el-radio
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.inProcessRealnessVerify"
- :label="0"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="是否考中活体检测">
- <el-radio
- :disabled="
- !!form.cameraPhotoUpload || orgSettingDisableLiveness
- "
- :title="
- (!!form.cameraPhotoUpload || orgSettingDisableLiveness) &&
- '原因:机构禁用活体或选择拍照作答'
- "
- v-model="form.inProcessLivenessVerify"
- :label="1"
- >是
- </el-radio>
- <el-radio
- :disabled="
- !!form.cameraPhotoUpload || orgSettingDisableLiveness
- "
- :title="
- (!!form.cameraPhotoUpload || orgSettingDisableLiveness) &&
- '原因:机构禁用活体或选择拍照作答'
- "
- 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
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.monitorProxy"
- :label="true"
- >是
- </el-radio>
- <el-radio
- :disabled="!!form.cameraPhotoUpload"
- :title="!!form.cameraPhotoUpload && '原因:拍照作答'"
- v-model="form.monitorProxy"
- :label="false"
- >否
- </el-radio>
- </el-form-item>
- </el-row>
- <div
- class="monitor-config"
- v-if="form.monitorProxy && enablePrevilleges"
- >
- <div class="monitor-config-options">
- <h3 class="monitor-title">监考直播方案配置:</h3>
- <el-form-item label="电脑摄像头主机位">
- <el-radio-group
- v-model="monitorVideoSource.CLIENT_CAMERA"
- @change="monitorClientCameraChange"
- >
- <el-radio
- v-for="(val, key) in monitorTypes"
- :key="key"
- :label="key"
- >{{ val }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="电脑操作录屏">
- <el-radio-group
- v-model="monitorVideoSource.CLIENT_SCREEN"
- :disabled="monitorVideoSource.CLIENT_CAMERA !== '1'"
- :title="
- monitorVideoSource.CLIENT_CAMERA === '0' &&
- '原因:先选择电脑摄像头主机位'
- "
- @change="monitorClientScreenChange"
- >
- <el-radio
- v-for="(val, key) in monitorTinyTypes"
- :key="key"
- :label="key"
- >{{ val }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="手机监考主机位">
- <el-radio-group
- v-model="monitorVideoSource.MOBILE_FIRST"
- @change="monitorMobileFirstChange"
- >
- <el-radio
- v-for="(val, key) in monitorTypes"
- :key="key"
- :label="key"
- >{{ val }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="手机监考辅机位">
- <el-radio-group
- v-model="monitorVideoSource.MOBILE_SECOND"
- :disabled="monitorVideoSource.MOBILE_FIRST === '0'"
- :title="
- monitorVideoSource.MOBILE_FIRST === '0' &&
- '原因:先选择手机监考主机位'
- "
- @change="monitorVideoSourceChange"
- >
- <el-radio
- v-for="(val, key) in monitorTypes"
- :key="key"
- :label="key"
- >{{ val }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <p class="tips-info tips-error">
- *主机位设备负责收音及播放监考提示
- </p>
- </div>
- <div class="monitor-config-view">
- <div class="monitor-item monitor-client-camera">
- <div
- :class="['monitor-item-img', monitorImgSrc('CLIENT_CAMERA')]"
- />
- <p class="monitor-item-desc">电脑摄像头主机位</p>
- </div>
- <div class="monitor-item monitor-client-screen">
- <div
- :class="['monitor-item-img', monitorImgSrc('CLIENT_SCREEN')]"
- />
- <p class="monitor-item-desc">电脑开启录屏</p>
- </div>
- <div class="monitor-item monitor-mobile-first">
- <div
- :class="['monitor-item-img', monitorImgSrc('MOBILE_FIRST')]"
- />
- <p class="monitor-item-desc">手机监考主机位</p>
- </div>
- <div class="monitor-item monitor-mobile-second">
- <div
- :class="['monitor-item-img', monitorImgSrc('MOBILE_SECOND')]"
- />
- <p class="monitor-item-desc">手机监考辅机位</p>
- </div>
- <div
- v-if="monitorVideoSource.MOBILE_SECOND !== '0'"
- class="monitor-line-mobile-second"
- >
- <span></span>
- <span></span>
- </div>
- <div
- v-if="monitorVideoSource.MOBILE_FIRST !== '0'"
- class="monitor-line-mobile-first"
- ></div>
- <div
- v-if="monitorVideoSource.CLIENT_CAMERA !== '0'"
- class="monitor-line-client-camera"
- ></div>
- </div>
- </div>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="其他设置" name="third">
- <el-form
- ref="form3"
- :model="form"
- label-width="170px"
- :rules="rules"
- inline
- :disabled="disableEdit"
- >
- <el-row>
- <el-form-item label="考试须知">
- <VEditor
- :value="form.preNoticeClone"
- style="width: 300px;"
- @result="(v) => (form.preNotice = v)"
- />
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="须知强制阅读时长">
- <el-input-number
- :min="1"
- :max="24 * 60"
- step-strictly
- :controls="false"
- v-model.trim="form.preNoticeStaySeconds"
- />(秒)
- </el-form-item>
- </el-row>
- <el-row>
- <el-form-item label="考后说明">
- <VEditor
- :value="form.postNoticeClone"
- style="width: 300px;"
- @result="(v) => (form.postNotice = v)"
- />
- </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 v-if="form.enableIpLimit">
- <el-form-item label="IP段(*表示任意):" prop="ipAllow">
- <el-input
- v-model.trim="form.ipAllow"
- placeholder="192.168.10.1, 192.168.11.*, 172.10.*.*"
- ></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"
- :disabled="disableEdit"
- >保存</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";
- import { searchOrg } from "@/api/system-org";
- 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 = 1;
- this.form.breakExpireSeconds = 0;
- } else {
- this.form.breakResumeCount = null;
- this.form.breakExpireSeconds = null;
- }
- },
- },
- orgSettingDisableLiveness() {
- return !this.orgSetting?.enableLiveness;
- },
- disableEdit() {
- return this.form.monitorStatus === "FINISHED";
- },
- enablePrevilleges() {
- return !!this.$store.state.user.orgInfo.enableMonitorRecord;
- },
- },
- watch: {
- "form.mode": {
- immediate: true,
- handler(v) {
- if (v === "ANYTIME") {
- this.form.forceFinish = 0;
- this.form.prepareSeconds = 0;
- this.form.openingSeconds = 0;
- }
- },
- },
- "form.startEndTimeProxy": {
- immediate: true,
- handler(v) {
- this.form.startTime = new Date(v[0]).valueOf();
- this.form.endTime = new Date(v[1]).valueOf();
- },
- },
- "form.monitorProxy": {
- immediate: true,
- handler(v) {
- if (!v) {
- this.form.monitorVideoSource = [];
- this.form.monitorRecord = [];
- }
- if (this.form.monitorVideoSource === null) {
- this.form.monitorVideoSource = [];
- }
- },
- },
- "form.cameraPhotoUpload": {
- handler(v) {
- if (v) {
- this.form.mobilePhotoUpload = 0;
- this.form.inProcessFaceVerify = 0;
- this.form.inProcessFaceStrangerIgnore = 0;
- this.form.inProcessRealnessVerify = 0;
- this.form.inProcessLivenessVerify = 0;
- this.form.monitorProxy = false;
- }
- },
- },
- "form.mobilePhotoUpload": {
- handler(v) {
- if (v) {
- this.form.cameraPhotoUpload = 0;
- }
- },
- },
- },
- 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.monitorRecord = this.form.monitorRecord || [];
- this.form.monitorVideoSource = this.form.monitorVideoSource || [];
- this.form.monitorProxy = !!this.form.monitorVideoSource;
- this.form.preNoticeClone = this.form.preNotice;
- this.form.postNoticeClone = this.form.postNotice;
- this.parseMonitorVideoSource();
- }
- // sleep
- // await new Promise((res) =>
- // setTimeout(() => {
- // res();
- // }, 3000)
- // );
- const res = await searchOrg(this.$store.state.user.orgId);
- this.orgSetting = res.data.data[0];
- if (!this.orgSetting.enableLiveness) {
- if (this.form.entryAuthenticationPolicy === "LIVENESS_VERIFY") {
- this.form.entryAuthenticationPolicy = "FACE_VERIFY_FORCE";
- }
- this.form.inProcessLivenessVerify = 0;
- }
- },
- 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,
- inProcessRealnessVerify: 0,
- inProcessLivenessVerify: 0,
- inProcessLivenessFixedRange: [0, 0],
- inProcessLivenessJudgePolicy: "ALL",
- monitorProxy: false,
- monitorRecord: [],
- monitorVideoSource: [],
- ipAllow: "",
- },
- rules: {
- mode: { required: true, message: "必填" },
- name: [
- { required: true, message: "必填" },
- { type: "string", min: 1, max: 30, message: "长度必须在1到30之间" },
- ],
- code: [
- { required: true, message: "必填" },
- { type: "string", min: 1, max: 30, message: "长度必须在1到30之间" },
- ],
- startEndTimeProxy: { required: true, message: "必填" },
- examCount: [
- { required: true, message: "必填" },
- { type: "number", min: 1, message: "必须大于0" },
- ],
- maxDurationSeconds: [
- { required: true, message: "必填" },
- { type: "number", min: 1, message: "必须大于0" },
- ],
- openingSeconds: {
- validator: (rule, value) => {
- // console.log(value);
- return new Promise((resolve, reject) => {
- if (
- !this.form.isModeAnytime &&
- this.form.maxDurationSeconds < value
- ) {
- reject("迟到时长必须小于考试时长");
- } else {
- resolve();
- }
- });
- },
- },
- minDurationSeconds: {
- trigger: "change",
- validator: (rule, value) => {
- // console.log(value);
- return new Promise((resolve, reject) => {
- if (this.form.maxDurationSeconds < value) {
- reject("冻结时长必须小于考试时长");
- } else {
- resolve();
- }
- });
- },
- },
- breakExpireSeconds: {
- trigger: "change",
- validator: (rule, value) => {
- // console.log(value);
- return new Promise((resolve, reject) => {
- if (
- this.enableBreakProxy &&
- this.form.monitorRecord.length &&
- value > 25 * 60
- ) {
- reject("开启回放情况下,断点时长不得超过25分钟");
- } else {
- resolve();
- }
- });
- },
- },
- 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: "格式错误",
- },
- ipAllow: {
- validator: (rule, value) => {
- // console.log(value);
- return new Promise((resolve, reject) => {
- if (!this.form.enableIpLimit) {
- resolve();
- return;
- }
- const ips = value.split(",").map((v) => v.trim());
- // console.log(ips);
- const checkIpSeg = (seg, idx) => {
- if (idx < 2) {
- if (!/^\d+$/.test(seg)) {
- return false;
- }
- } else if (idx >= 2 && idx < 4) {
- if (!(/^\d+$/.test(seg) || /^\*$/.test(seg))) {
- return false;
- }
- }
- return true;
- };
- if (
- ips.every(
- (v) =>
- v.split(".").length === 4 && v.split(".").every(checkIpSeg)
- )
- ) {
- resolve();
- } else {
- reject("格式不匹配");
- }
- });
- },
- },
- },
- sources: [
- "MOBILE_FIRST",
- "MOBILE_SECOND",
- "CLIENT_CAMERA",
- "CLIENT_SCREEN",
- ],
- monitorTypes: {
- 0: "关闭",
- 1: "直播",
- 2: "直播+回放",
- },
- monitorTinyTypes: {
- 0: "关闭",
- 1: "直播",
- },
- monitorVideoSource: {
- CLIENT_CAMERA: "0",
- CLIENT_SCREEN: "0",
- MOBILE_FIRST: "0",
- MOBILE_SECOND: "0",
- },
- orgSetting: null,
- loading: false,
- };
- },
- methods: {
- monitorImgSrc(monitorSource) {
- const selected = this.monitorVideoSource[monitorSource] !== "0";
- const selectedStr = selected ? "-selected" : "";
- if (monitorSource === "MOBILE_FIRST") {
- return `mobile-first-img${selectedStr}`;
- } else if (monitorSource === "MOBILE_SECOND") {
- return `mobile-second-img${selectedStr}`;
- // return `./imgs/手机监考辅机位${selectedStr}.png`;
- } else if (monitorSource === "CLIENT_CAMERA") {
- // return `./imgs/手机监考辅机位${selectedStr}.png`;
- return `client-camera-img${selectedStr}`;
- } else if (monitorSource === "CLIENT_SCREEN") {
- // return `./imgs/手机监考辅机位${selectedStr}.png`;
- return `client-screen-img${selectedStr}`;
- }
- return "";
- },
- monitorClientCameraChange(val) {
- if (val === "0" || val === "2") {
- this.monitorVideoSource.CLIENT_SCREEN = "0";
- }
- this.monitorVideoSourceChange();
- },
- monitorClientScreenChange() {
- this.monitorVideoSourceChange();
- },
- monitorMobileFirstChange(val) {
- if (val === "0") this.monitorVideoSource.MOBILE_SECOND = "0";
- this.monitorVideoSourceChange();
- },
- monitorVideoSourceChange() {
- let monitorVideoSource = [],
- monitorRecord = [];
- this.sources.forEach((source) => {
- if (this.monitorVideoSource[source] === "0") return;
- if (this.monitorVideoSource[source] === "2") {
- monitorRecord.push(source);
- }
- monitorVideoSource.push(source);
- });
- this.form.monitorVideoSource = monitorVideoSource;
- this.form.monitorRecord = monitorRecord;
- },
- parseMonitorVideoSource() {
- this.sources.forEach((source) => {
- const hasRecord = this.form.monitorRecord.includes(source);
- const hasSource = this.form.monitorVideoSource.includes(source);
- if (hasRecord) {
- this.monitorVideoSource[source] = "2";
- } else if (hasSource) {
- this.monitorVideoSource[source] = "1";
- } else {
- this.monitorVideoSource[source] = "0";
- }
- });
- },
- async save() {
- try {
- await this.$refs.form1.validate();
- await this.$refs.form2.validate();
- await this.$refs.form3.validate();
- } catch (error) {
- console.log("校验失败", error);
- this.$notify({
- title: "保存失败",
- message: "请检查不合法的输入项",
- type: "waring",
- });
- 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%;
- }
- }
- }
- .monitor-config {
- &-options {
- display: inline-block;
- vertical-align: top;
- width: 470px;
- .el-form-item {
- margin-bottom: 10px;
- }
- }
- &-view {
- position: relative;
- display: inline-block;
- vertical-align: top;
- width: 498px;
- height: 257px;
- background: #f0f4f9;
- border-radius: 6px;
- border: 1px solid #e8edf3;
- }
- }
- .monitor-title {
- height: 20px;
- font-size: 14px;
- font-weight: 600;
- color: #202b4b;
- line-height: 20px;
- margin-bottom: 20px;
- padding-left: 20px;
- &::before {
- content: "";
- display: inline-block;
- vertical-align: middle;
- width: 4px;
- height: 10px;
- background: #1886fe;
- border-radius: 2px;
- margin-right: 10px;
- }
- }
- .monitor-item {
- position: absolute;
- text-align: center;
- &-img {
- margin: 0 auto;
- }
- &-desc {
- font-size: 12px;
- line-height: 1;
- margin: 5px 0 0;
- }
- }
- .monitor-client-camera {
- left: 171px;
- top: 21px;
- }
- .monitor-client-screen {
- left: 124px;
- bottom: 20px;
- }
- .monitor-mobile-first {
- left: 20px;
- top: 20px;
- }
- .monitor-mobile-second {
- bottom: 20px;
- right: 20px;
- }
- .mobile-first-img {
- background-image: url(./imgs/手机监考主机位.png);
- background-repeat: no-repeat;
- background-size: 52px 80px;
- width: 52px;
- height: 80px;
- &-selected {
- @extend .mobile-first-img;
- background-image: url(./imgs/手机监考主机位-选中.png);
- }
- }
- .mobile-second-img {
- background-image: url(./imgs/手机监考辅机位.png);
- background-repeat: no-repeat;
- background-size: 52px 80px;
- width: 52px;
- height: 80px;
- &-selected {
- @extend .mobile-second-img;
- background-image: url(./imgs/手机监考辅机位-选中.png);
- }
- }
- .client-camera-img {
- background-image: url(./imgs/电脑摄像头主机位.png);
- background-repeat: no-repeat;
- background-size: 33px 40px;
- width: 33px;
- height: 40px;
- &-selected {
- @extend .client-camera-img;
- background-image: url(./imgs/电脑摄像头主机位-选中.png);
- }
- }
- .client-screen-img {
- background-image: url(./imgs/电脑操作录屏.png);
- background-repeat: no-repeat;
- background-size: 190px 110px;
- width: 190px;
- height: 110px;
- &-selected {
- @extend .client-screen-img;
- background-image: url(./imgs/电脑操作录屏-选中.png);
- }
- }
- .monitor-line-point {
- content: "";
- display: block;
- position: absolute;
- width: 5px;
- height: 5px;
- border: 1px solid #c1cbdb;
- border-radius: 50%;
- background: #f0f4f9;
- }
- .monitor-line-client-camera {
- position: absolute;
- width: 0;
- height: 23px;
- border-left: 1px solid #c1cbdb;
- top: 89px;
- left: 219px;
- &::before {
- @extend .monitor-line-point;
- left: -3px;
- top: 0;
- }
- &::after {
- @extend .monitor-line-point;
- left: -3px;
- bottom: 0;
- }
- }
- .monitor-line-mobile-first {
- position: absolute;
- width: 56px;
- height: 28px;
- bottom: 96px;
- left: 63px;
- border-left: 1px solid #c1cbdb;
- border-bottom: 1px solid #c1cbdb;
- &::before {
- @extend .monitor-line-point;
- left: -3px;
- top: 0;
- }
- &::after {
- @extend .monitor-line-point;
- right: 0;
- bottom: -3px;
- }
- }
- .monitor-line-mobile-second {
- span:nth-of-type(1) {
- display: block;
- position: absolute;
- width: 35px;
- height: 63px;
- bottom: 92px;
- right: 139px;
- border-right: 1px solid #c1cbdb;
- border-bottom: 1px solid #c1cbdb;
- &::before {
- @extend .monitor-line-point;
- left: 0;
- bottom: -3px;
- }
- }
- span:nth-of-type(2) {
- display: block;
- position: absolute;
- width: 78px;
- height: 23px;
- top: 100px;
- right: 61px;
- border-right: 1px solid #c1cbdb;
- border-top: 1px solid #c1cbdb;
- &::after {
- @extend .monitor-line-point;
- right: -3px;
- bottom: 0;
- }
- }
- }
- </style>
|