|
@@ -1,10 +1,319 @@
|
|
|
<template>
|
|
|
- <div class="">主观题校验</div>
|
|
|
+ <div class="flex direction-column full">
|
|
|
+ <mark-header :exclude-operations="['remark', 'problem', 'example', 'delete', 'bookmark']" @click="onOperationClick">
|
|
|
+ <el-button type="primary" size="small" class="m-l-base" plain @click="onEditScore">修改给分</el-button>
|
|
|
+ <el-button type="primary" size="small" class="m-l-base m-r-auto" @click="onConfirm">提交确认</el-button>
|
|
|
+ </mark-header>
|
|
|
+ <div class="flex flex-1 overflow-hidden p-base mark-container">
|
|
|
+ <div
|
|
|
+ class="flex flex-1 direction-column radius-base fill-blank mark-content"
|
|
|
+ :class="{ 'text-center': center }"
|
|
|
+ :style="{ 'background-color': backgroundColor }"
|
|
|
+ >
|
|
|
+ <span class="preview" @click="onPreview">
|
|
|
+ <svg-icon name="preview"></svg-icon>
|
|
|
+ </span>
|
|
|
+ <right-button class="next-button" @click="checkNext" />
|
|
|
+ <div class="flex-1 p-base scroll-auto mark-content-paper">
|
|
|
+ <img :src="dataUrl" alt="" class="paper-img" :style="{ 'background-color': frontColor }" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="p-base radius-base fill-blank scroll-auto m-l-base problem-list">
|
|
|
+ <base-form size="small" :model="formModel" :items="formItems" :label-width="useVW(62)">
|
|
|
+ <template #form-item-search>
|
|
|
+ <el-button type="primary" @click="onSearch">查询</el-button>
|
|
|
+ </template>
|
|
|
+ </base-form>
|
|
|
+ <div class="flex items-center p-l-base">
|
|
|
+ <span>{{ statusText }}</span>
|
|
|
+ <span>: 共{{ tableData.length }}</span>
|
|
|
+ </div>
|
|
|
+ <base-table
|
|
|
+ ref="tableRef"
|
|
|
+ size="small"
|
|
|
+ height="45vh"
|
|
|
+ :data="tableData"
|
|
|
+ :columns="columns"
|
|
|
+ @current-change="onCurrentChange"
|
|
|
+ ></base-table>
|
|
|
+ <base-table
|
|
|
+ v-if="currentSubjectiveCheck"
|
|
|
+ class="m-t-base"
|
|
|
+ size="small"
|
|
|
+ height="150px"
|
|
|
+ :data="[currentSubjectiveCheck]"
|
|
|
+ :columns="subColumns"
|
|
|
+ @row-dblclick="onDbClick"
|
|
|
+ >
|
|
|
+ <template #empty> 暂无数据 </template>
|
|
|
+ </base-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <scoring-panel-with-confirm
|
|
|
+ v-model:visible="editScoreVisible"
|
|
|
+ v-model:score="modelScore"
|
|
|
+ :main-number="mockTask.mainNumber"
|
|
|
+ modal
|
|
|
+ :toggle-modal="false"
|
|
|
+ @submit="onSubmit"
|
|
|
+ ></scoring-panel-with-confirm>
|
|
|
+ <image-preview v-model="previewModalVisible" :url="MockImg"></image-preview>
|
|
|
+ <mark-history-list :id="currentViewHistory?.secretNumber" v-model="visibleHistory" type="secret"></mark-history-list>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts" name="QualitySubjectiveCheck">
|
|
|
/** 主观题校验 */
|
|
|
-import { reactive, ref } from 'vue'
|
|
|
+import { reactive, ref, computed, watch } from 'vue'
|
|
|
+import { ElButton } from 'element-plus'
|
|
|
+import { useSetImgBg } from '@/hooks/useSetImgBg'
|
|
|
+import useFetch from '@/hooks/useFetch'
|
|
|
+import useVW from '@/hooks/useVW'
|
|
|
+import useForm from '@/hooks/useForm'
|
|
|
+import useOptions from '@/hooks/useOptions'
|
|
|
+import useMarkHeader from '@/hooks/useMarkHeader'
|
|
|
+import useTableCheck from '@/hooks/useTableCheck'
|
|
|
+import BaseForm from '@/components/element/BaseForm.vue'
|
|
|
+import BaseTable from '@/components/element/BaseTable.vue'
|
|
|
+import MarkHistoryList from '@/components/shared/MarkHistoryList.vue'
|
|
|
+import RightButton from '@/components/shared/RightButton.vue'
|
|
|
+import MarkHeader from '@/components/shared/MarkHeader.vue'
|
|
|
+import ScoringPanelWithConfirm from '@/components/shared/ScoringPanelWithConfirm.vue'
|
|
|
+import ImagePreview from '@/components/shared/ImagePreview.vue'
|
|
|
+import SvgIcon from '@/components/common/SvgIcon.vue'
|
|
|
+
|
|
|
+import MockImg from '@/assets/mock/SAMPA-1.jpg'
|
|
|
+import type { SetImgBgOption } from '@/hooks/useSetImgBg'
|
|
|
+import type { ExtractMultipleApiResponse, ExtractApiParams } from 'api-type'
|
|
|
+import type { MarkHeaderInstance, EpFormItem, EpTableColumn } from 'global-type'
|
|
|
+
|
|
|
+type RowType = ExtractMultipleApiResponse<'getSubjectiveCheckList'> & { index: number }
|
|
|
+
|
|
|
+/** 给分板 */
|
|
|
+const editScoreVisible = ref<boolean>(false)
|
|
|
+
|
|
|
+/** 图片预览 */
|
|
|
+const previewModalVisible = ref<boolean>(false)
|
|
|
+
|
|
|
+/** 分数 */
|
|
|
+const modelScore = ref<number[]>([])
|
|
|
+
|
|
|
+const mockTask = ref<{ mainNumber: number }>({
|
|
|
+ mainNumber: 1,
|
|
|
+})
|
|
|
+
|
|
|
+const {
|
|
|
+ rotate,
|
|
|
+ scale,
|
|
|
+ center,
|
|
|
+ frontColor,
|
|
|
+ backgroundColor,
|
|
|
+ onBack,
|
|
|
+ onScaleChange,
|
|
|
+ onCenter,
|
|
|
+ onRotate,
|
|
|
+ setBackgroundColor,
|
|
|
+ setFrontColor,
|
|
|
+ onViewStandard,
|
|
|
+} = useMarkHeader()
|
|
|
+
|
|
|
+const imgOption = computed<SetImgBgOption>(() => {
|
|
|
+ return {
|
|
|
+ image: MockImg,
|
|
|
+ immediate: true,
|
|
|
+ rotate: rotate.value,
|
|
|
+ scale: scale.value,
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const { drawing, dataUrl } = useSetImgBg(imgOption)
|
|
|
+
|
|
|
+/** 刷新 */
|
|
|
+const onRefresh = () => {
|
|
|
+ onSearch()
|
|
|
+}
|
|
|
+
|
|
|
+/** 预览试卷 */
|
|
|
+const onPreview = () => {
|
|
|
+ previewModalVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const onEditScore = () => {
|
|
|
+ editScoreVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+type OperationClick = MarkHeaderInstance['onClick']
|
|
|
+
|
|
|
+type OperationType = Parameters<Exclude<OperationClick, undefined>>[0]['type']
|
|
|
+
|
|
|
+const operationHandles: Partial<Record<OperationType, (...args: any) => void>> = {
|
|
|
+ back: onBack,
|
|
|
+ 'scale-change': onScaleChange,
|
|
|
+ center: onCenter,
|
|
|
+ rotate: onRotate,
|
|
|
+ 'front-color': setFrontColor,
|
|
|
+ 'background-color': setBackgroundColor,
|
|
|
+ refresh: onRefresh,
|
|
|
+ standard: onViewStandard,
|
|
|
+}
|
|
|
+
|
|
|
+const onOperationClick: OperationClick = ({ type, value }) => {
|
|
|
+ operationHandles[type]?.(value)
|
|
|
+}
|
|
|
+
|
|
|
+const formModel = reactive<ExtractApiParams<'getSubjectiveCheckList'>>({
|
|
|
+ subjectCode: '',
|
|
|
+ mainNumber: void 0,
|
|
|
+ checked: false,
|
|
|
+ rule: 'RULE1',
|
|
|
+ pageNumber: 1,
|
|
|
+ pageSize: 9999999,
|
|
|
+})
|
|
|
+
|
|
|
+const { mainQuestionList, dataModel, changeModelValue, onOptionInit } = useOptions(['subject', 'question'])
|
|
|
+
|
|
|
+watch(dataModel, () => {
|
|
|
+ formModel.subjectCode = dataModel.subject || ''
|
|
|
+ formModel.mainNumber = dataModel.question
|
|
|
+})
|
|
|
+
|
|
|
+const { defineColumn, _ } = useForm()
|
|
|
+
|
|
|
+const span10 = defineColumn(_, '', { span: 10 })
|
|
|
+const span12 = defineColumn(_, '', { span: 12 })
|
|
|
+
|
|
|
+const rules = [
|
|
|
+ '主观分有分,客观分为0',
|
|
|
+ '主观分为0分,客观分大于40分',
|
|
|
+ '主观分为1,2分,客观分大于45分',
|
|
|
+ '主观分大于10分,客观分小于20分',
|
|
|
+].map((v, i) => ({
|
|
|
+ label: v,
|
|
|
+ value: `RULE${i + 1}`,
|
|
|
+}))
|
|
|
+
|
|
|
+const formItems = computed<EpFormItem[]>(() => [
|
|
|
+ span10({
|
|
|
+ rowKey: 'row-1',
|
|
|
+ label: '科目',
|
|
|
+ prop: 'subjectCode',
|
|
|
+ slotType: 'select',
|
|
|
+ slot: { options: mainQuestionList.value, onChange: changeModelValue('subject') },
|
|
|
+ }),
|
|
|
+ span10({
|
|
|
+ rowKey: 'row-1',
|
|
|
+ label: '大题',
|
|
|
+ prop: 'mainNumber',
|
|
|
+ slotType: 'select',
|
|
|
+ slot: { options: mainQuestionList.value, onChange: changeModelValue('question') },
|
|
|
+ }),
|
|
|
+ { rowKey: 'row-1', slotName: 'search', labelWidth: '10px', colProp: { span: 4 } },
|
|
|
+ span12({
|
|
|
+ rowKey: 'row-2',
|
|
|
+ label: '校验规则',
|
|
|
+ prop: 'rule',
|
|
|
+ slotType: 'select',
|
|
|
+ slot: {
|
|
|
+ options: rules,
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ span10({
|
|
|
+ rowKey: 'row-3',
|
|
|
+ label: '状态',
|
|
|
+ prop: 'checked',
|
|
|
+ slotType: 'select',
|
|
|
+ slot: {
|
|
|
+ options: [
|
|
|
+ { label: '未处理', value: false },
|
|
|
+ { label: '已处理', value: true },
|
|
|
+ { label: '全部', value: '' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ }),
|
|
|
+])
|
|
|
+
|
|
|
+/** 主观题校验 */
|
|
|
+const columns: EpTableColumn<RowType>[] = [
|
|
|
+ { label: '序号', type: 'index' },
|
|
|
+ { label: '密号', prop: 'secretNumber' },
|
|
|
+ { label: '大题名称', prop: 'mainName' },
|
|
|
+ { label: '处理结果', prop: 'status' },
|
|
|
+ { label: '处理人', prop: 'headerName' },
|
|
|
+ { label: '处理时间', prop: 'updateTime' },
|
|
|
+]
|
|
|
+
|
|
|
+const subColumns: EpTableColumn<RowType>[] = [
|
|
|
+ { label: '密号', prop: 'secretNumber' },
|
|
|
+ { label: '大题名称', prop: 'mainName' },
|
|
|
+ { label: '当前成绩', prop: 'markerScore' },
|
|
|
+ { label: '修改成绩', prop: 'headerScore' },
|
|
|
+]
|
|
|
+
|
|
|
+const { fetch: getSubjectiveCheckList, result: subjectiveCheckList } = useFetch('getSubjectiveCheckList')
|
|
|
+
|
|
|
+const {
|
|
|
+ tableRef,
|
|
|
+ tableData,
|
|
|
+ current: currentSubjectiveCheck,
|
|
|
+ currentView: currentViewHistory,
|
|
|
+ next: checkNext,
|
|
|
+ visibleHistory,
|
|
|
+ onDbClick,
|
|
|
+ onCurrentChange,
|
|
|
+} = useTableCheck(subjectiveCheckList)
|
|
|
+
|
|
|
+const statusText = ref<string>('未处理')
|
|
|
+
|
|
|
+const onSearch = async () => {
|
|
|
+ getSubjectiveCheckList(formModel).then(() => {
|
|
|
+ statusText.value = formModel.checked === true ? '已处理' : formModel.checked === false ? '未处理' : '全部'
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/** 给分 */
|
|
|
+const { fetch: subjectiveCheckMark } = useFetch('subjectiveCheckMark')
|
|
|
+const onSubmit = () => {
|
|
|
+ if (currentSubjectiveCheck.value) {
|
|
|
+ subjectiveCheckMark({ taskId: currentSubjectiveCheck.value.taskId, scores: modelScore.value })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 确认 */
|
|
|
+const { fetch: subjectiveCheckConfirm } = useFetch('subjectiveCheckConfirm')
|
|
|
+
|
|
|
+const onConfirm = () => {
|
|
|
+ if (currentSubjectiveCheck.value) {
|
|
|
+ subjectiveCheckConfirm({ taskId: currentSubjectiveCheck.value.taskId })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onOptionInit(onSearch)
|
|
|
</script>
|
|
|
|
|
|
-<style scoped lang="scss"></style>
|
|
|
+<style scoped lang="scss">
|
|
|
+.mark-container {
|
|
|
+ .mark-content {
|
|
|
+ position: relative;
|
|
|
+ .preview {
|
|
|
+ position: absolute;
|
|
|
+ cursor: pointer;
|
|
|
+ top: 10px;
|
|
|
+ right: 20px;
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+ .next-button {
|
|
|
+ position: absolute;
|
|
|
+ right: -20px;
|
|
|
+ top: 300px;
|
|
|
+ }
|
|
|
+ .mark-content-paper {
|
|
|
+ img {
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .problem-list {
|
|
|
+ width: 580px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|