|
@@ -0,0 +1,291 @@
|
|
|
+<template>
|
|
|
+ <div class="question-panel">
|
|
|
+ <a-descriptions v-if="info" class="panel-info" :column="10">
|
|
|
+ <a-descriptions-item label="准考证号" :span="6">
|
|
|
+ {{ info.examNumber }}
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item label="姓名" :span="4">
|
|
|
+ {{ info.name }}
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item label="座位号" :span="6">
|
|
|
+ {{ info.seatNumber }}
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item label="考点" :span="4">
|
|
|
+ {{ info.examSite }}
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item label="卷型号" :span="6">
|
|
|
+ <a-button class="ant-gray m-r-4px">{{ info.paperType }}</a-button>
|
|
|
+ <a-button>
|
|
|
+ <template #icon><SwapOutlined /></template>
|
|
|
+ </a-button>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item label="缺考" :span="4">
|
|
|
+ <a-radio-group
|
|
|
+ v-model:value="incomplete"
|
|
|
+ name="incomplete"
|
|
|
+ :options="booleanOptions"
|
|
|
+ >
|
|
|
+ </a-radio-group>
|
|
|
+ </a-descriptions-item>
|
|
|
+ </a-descriptions>
|
|
|
+ <div ref="panelBodyRef" class="panel-body">
|
|
|
+ <div class="panel-body-title">
|
|
|
+ <h4>客观题</h4>
|
|
|
+ <p>多于一个填涂显示>号,未填涂显示#号</p>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in questionList"
|
|
|
+ :key="index"
|
|
|
+ :class="['question-item', `question-item-${index}`]"
|
|
|
+ >
|
|
|
+ <span>{{ getQuestionNo(index) }}:</span>
|
|
|
+ <a-button
|
|
|
+ :class="['ant-gray', { 'is-active': curQuestionIndex === index }]"
|
|
|
+ @click="onEditQuestion(index)"
|
|
|
+ >{{ getQuesionCont(item) }}</a-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-if="quesionEditShow"
|
|
|
+ class="queston-edit"
|
|
|
+ :style="quesionEditStyle"
|
|
|
+ v-ele-click-outside-directive="hideEditQuestion"
|
|
|
+ @keyup.enter="onSaveQuesion"
|
|
|
+ >
|
|
|
+ <a-input v-model:value="curQuestion" style="width: 64px"></a-input>
|
|
|
+ <a-button class="ant-simple m-l-8px" type="link" @click="onSaveQuesion"
|
|
|
+ >保存(Enter)</a-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, ref, watch } from "vue";
|
|
|
+import { message } from "ant-design-vue";
|
|
|
+import { SwapOutlined } from "@ant-design/icons-vue";
|
|
|
+import { booleanOptionList } from "@/constants/enumerate";
|
|
|
+
|
|
|
+import { vEleClickOutsideDirective } from "@/directives/eleClickOutside";
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: "QuestionPanel",
|
|
|
+});
|
|
|
+
|
|
|
+interface QuestionInfo {
|
|
|
+ examNumber: string;
|
|
|
+ name: string;
|
|
|
+ examSite: string;
|
|
|
+ seatNumber: number;
|
|
|
+ paperType: string;
|
|
|
+}
|
|
|
+
|
|
|
+const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ questions: string[];
|
|
|
+ info: QuestionInfo;
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ questions: () => [],
|
|
|
+ }
|
|
|
+);
|
|
|
+const emit = defineEmits(["update:questions", "change"]);
|
|
|
+
|
|
|
+const booleanOptions = ref(booleanOptionList);
|
|
|
+const incomplete = ref();
|
|
|
+const questionList = ref([]);
|
|
|
+const curQuestion = ref("");
|
|
|
+const curQuestionIndex = ref(-1);
|
|
|
+
|
|
|
+function getQuestionNo(index: number) {
|
|
|
+ const no = index + 1;
|
|
|
+ return no < 10 ? `0${no}` : `${no}`;
|
|
|
+}
|
|
|
+
|
|
|
+function getQuesionCont(cont: string) {
|
|
|
+ if (!cont) return "#";
|
|
|
+ if (cont.length > 1) return ">";
|
|
|
+ return cont;
|
|
|
+}
|
|
|
+
|
|
|
+// question edit
|
|
|
+const quesionEditShow = ref(false);
|
|
|
+const quesionEditPos = ref({
|
|
|
+ left: 0,
|
|
|
+ top: 0,
|
|
|
+});
|
|
|
+const quesionEditStyle = computed(() => {
|
|
|
+ return {
|
|
|
+ top: `${quesionEditPos.value.top}px`,
|
|
|
+ left: `${quesionEditPos.value.left}px`,
|
|
|
+ };
|
|
|
+});
|
|
|
+const panelBodyRef = ref();
|
|
|
+function onEditQuestion(index: number) {
|
|
|
+ curQuestionIndex.value = index;
|
|
|
+ const qcont = questionList.value[curQuestionIndex.value];
|
|
|
+ curQuestion.value = qcont.split("").join(",");
|
|
|
+
|
|
|
+ quesionEditShow.value = true;
|
|
|
+ updateQuestionEditPos(index);
|
|
|
+}
|
|
|
+
|
|
|
+function updateQuestionEditPos(index: number) {
|
|
|
+ const panelBodyDom = panelBodyRef.value as HTMLDivElement;
|
|
|
+ const itemDom = panelBodyDom.querySelector(
|
|
|
+ `.question-item-${index}`
|
|
|
+ ) as HTMLDivElement;
|
|
|
+ let left = itemDom.offsetLeft + 30;
|
|
|
+ left = Math.min(panelBodyDom.clientWidth - 165, left);
|
|
|
+ quesionEditPos.value.left = left;
|
|
|
+ quesionEditPos.value.top = itemDom.offsetTop - 54;
|
|
|
+}
|
|
|
+
|
|
|
+function hideEditQuestion() {
|
|
|
+ quesionEditShow.value = false;
|
|
|
+ curQuestionIndex.value = -1;
|
|
|
+}
|
|
|
+
|
|
|
+function onSaveQuesion() {
|
|
|
+ if (!quesionEditShow.value) return;
|
|
|
+
|
|
|
+ if (!curQuestion.value) {
|
|
|
+ message.error("请输入答案!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const questionCont = curQuestion.value.split(",").join("");
|
|
|
+
|
|
|
+ if (!questionCont) {
|
|
|
+ message.error("请输入答案!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ questionList.value[curQuestionIndex.value] = questionCont;
|
|
|
+ quesionEditShow.value = false;
|
|
|
+
|
|
|
+ emit("update:questions", questionList.value);
|
|
|
+ emit("change", questionList.value);
|
|
|
+}
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.questions,
|
|
|
+ (val) => {
|
|
|
+ if (!val) return;
|
|
|
+ questionList.value = [...val];
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true,
|
|
|
+ }
|
|
|
+);
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+.question-panel {
|
|
|
+ .panel-body {
|
|
|
+ margin: 15px -14px 0;
|
|
|
+ padding: 0 14px;
|
|
|
+ border-top: 1px solid @border-color1;
|
|
|
+ position: relative;
|
|
|
+ font-size: 0;
|
|
|
+
|
|
|
+ &-title {
|
|
|
+ padding: 12px 0 8px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ > p {
|
|
|
+ color: @text-color3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-item {
|
|
|
+ display: inline-block;
|
|
|
+ vertical-align: middle;
|
|
|
+ width: 20%;
|
|
|
+ font-size: 14px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ > span {
|
|
|
+ display: inline-block;
|
|
|
+ width: 30px;
|
|
|
+ padding-right: 4px;
|
|
|
+ text-align: right;
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+ .ant-btn {
|
|
|
+ padding-left: 10px;
|
|
|
+ padding-right: 10px;
|
|
|
+
|
|
|
+ &.is-active {
|
|
|
+ border-color: @brand-color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .queston-edit {
|
|
|
+ position: absolute;
|
|
|
+ width: 165px;
|
|
|
+ background: #f2f3f5;
|
|
|
+ box-shadow: 0px 4px 8px 0px rgba(54, 61, 89, 0.2);
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 8px;
|
|
|
+ border: 1px solid @border-color1;
|
|
|
+ z-index: 9;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ant-descriptions-row) {
|
|
|
+ .ant-descriptions-item {
|
|
|
+ padding-bottom: 8px;
|
|
|
+ .ant-descriptions-item-label {
|
|
|
+ display: block;
|
|
|
+ color: @text-color2;
|
|
|
+ }
|
|
|
+ .ant-descriptions-item-label::after {
|
|
|
+ margin-inline: 2px 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:first-child {
|
|
|
+ .ant-descriptions-item-label {
|
|
|
+ width: 66px;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-radio-wrapper span.ant-radio + * {
|
|
|
+ padding-inline-start: 4px;
|
|
|
+ padding-inline-end: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ant-descriptions-row:nth-of-type(2)) {
|
|
|
+ .ant-descriptions-item {
|
|
|
+ padding-bottom: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ant-descriptions-row:last-child) {
|
|
|
+ .ant-descriptions-item {
|
|
|
+ padding-bottom: 0;
|
|
|
+
|
|
|
+ .ant-descriptions-item-container {
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-descriptions-item-label {
|
|
|
+ line-height: 28px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-btn {
|
|
|
+ padding: 2px 6px;
|
|
|
+ height: 28px;
|
|
|
+ min-width: 28px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|