index.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <template>
  2. <div class="flex direction-column full">
  3. <mark-header
  4. :exclude-operations="['remark', 'problem', 'example', 'delete', 'bookmark']"
  5. :paper-path="current?.filePath"
  6. :secret-number="current?.secretNumber"
  7. @click="onOperationClick"
  8. >
  9. </mark-header>
  10. <div class="flex flex-1 overflow-hidden p-base mark-container">
  11. <splitpanes class="default-theme" style="height: 100%" @resize="setPaneSize">
  12. <pane
  13. max-size="80"
  14. :size="paneSize"
  15. class="flex flex-1 direction-column radius-base fill-blank mark-content"
  16. :class="{ 'text-center': center }"
  17. :style="{ 'background-color': backgroundColor }"
  18. >
  19. <span class="preview" @click="onPreview">
  20. <svg-icon name="preview"></svg-icon>
  21. </span>
  22. <p v-if="current" class="absolute mark-score">{{ current.markScore }}</p>
  23. <!-- <right-button class="next-button" @click="checkNext" /> -->
  24. <div class="flex-1 p-base scroll-auto mark-content-paper img-wrap">
  25. <img :src="dataUrl" alt="" class="paper-img" :style="{ 'background-color': frontColor }" />
  26. </div>
  27. </pane>
  28. <pane
  29. max-size="80"
  30. :size="100 - paneSize"
  31. class="flex direction-column p-base radius-base fill-blank table-view"
  32. >
  33. <div class="flex items-center justify-between detail-info-table-header">
  34. <el-button custom-1 size="small" class="detail-info-label">
  35. <span class="">{{ PaperType }}共:</span>
  36. <span class="m-l-extra-small detail-info-label-num">{{ monitorDetail?.length }}</span>
  37. </el-button>
  38. </div>
  39. <div class="flex-1 scroll-auto m-t-mini">
  40. <base-table
  41. ref="tableRef"
  42. border
  43. stripe
  44. size="small"
  45. height="100%"
  46. :data="tableData"
  47. :columns="columns"
  48. highlight-current-row
  49. :cell-style="{ padding: '6px 0' }"
  50. @current-change="onCurrentChange"
  51. @row-dblclick="onDbClick"
  52. ></base-table>
  53. </div>
  54. </pane>
  55. </splitpanes>
  56. </div>
  57. </div>
  58. <image-preview v-model="previewModalVisible" :url="current?.filePath"></image-preview>
  59. <mark-history-list
  60. :id="currentViewHistory?.taskId"
  61. v-model="visibleHistory"
  62. :task="currentViewHistory"
  63. ></mark-history-list>
  64. </template>
  65. <script setup lang="tsx" name="TrainingDetail">
  66. /** 培训监控调卷详情 */
  67. import { ref, computed, watch } from 'vue'
  68. import { useRoute } from 'vue-router'
  69. import { ElButton } from 'element-plus'
  70. import { useSetImgBg } from '@/hooks/useSetImgBg'
  71. import useFetch from '@/hooks/useFetch'
  72. import useMarkHeader from '@/hooks/useMarkHeader'
  73. import useTableCheck from '@/hooks/useTableCheck'
  74. import MarkHeader from '@/components/shared/MarkHeader.vue'
  75. import BaseTable from '@/components/element/BaseTable.vue'
  76. import ImagePreview from '@/components/shared/ImagePreview.vue'
  77. import RightButton from '@/components/shared/RightButton.vue'
  78. import SvgIcon from '@/components/common/SvgIcon.vue'
  79. import MarkHistoryList from '@/components/shared/MarkHistoryList.vue'
  80. import SecNumberStatus from '@/components/common/secNumberStatus.vue'
  81. import type { SetImgBgOption } from '@/hooks/useSetImgBg'
  82. import type { ExtractApiResponse } from '@/api/api'
  83. import type { MarkHeaderInstance, EpTableColumn } from 'global-type'
  84. import { Splitpanes, Pane } from 'splitpanes'
  85. import { setPaneSize } from '@/utils/common'
  86. import useMainStore from '@/store/main'
  87. const mainStore = useMainStore()
  88. const paneSize = computed(() => {
  89. return mainStore.paneSizeConfig[location.pathname] || 60
  90. })
  91. type RowType = ExtractApiResponse<'getTrainingMonitorDetail'> & { index: number }
  92. const { query } = useRoute()
  93. const PaperType = computed(() => {
  94. return query.stage === 'FORCE' ? '强制考核卷' : '培训卷'
  95. })
  96. /** 图片预览 */
  97. const previewModalVisible = ref<boolean>(false)
  98. const {
  99. rotate,
  100. scale,
  101. center,
  102. frontColor,
  103. backgroundColor,
  104. onBack,
  105. onScaleChange,
  106. onCenter,
  107. onRotate,
  108. setBackgroundColor,
  109. setFrontColor,
  110. onViewStandard,
  111. } = useMarkHeader()
  112. /** 刷新 */
  113. const onRefresh = () => {
  114. fetchData()
  115. }
  116. /** 预览试卷 */
  117. const onPreview = () => {
  118. previewModalVisible.value = true
  119. }
  120. type OperationClick = MarkHeaderInstance['onClick']
  121. type OperationType = Parameters<Exclude<OperationClick, undefined>>[0]['type']
  122. const operationHandles: Partial<Record<OperationType, (...args: any) => void>> = {
  123. back: onBack,
  124. 'scale-change': onScaleChange,
  125. center: onCenter,
  126. rotate: onRotate,
  127. 'front-color': setFrontColor,
  128. 'background-color': setBackgroundColor,
  129. refresh: onRefresh,
  130. standard: onViewStandard,
  131. }
  132. const onOperationClick: OperationClick = ({ type, value }) => {
  133. operationHandles[type]?.(value)
  134. }
  135. const columns: EpTableColumn<RowType>[] = [
  136. {
  137. label: '密号',
  138. prop: 'secretNumber',
  139. width: 110,
  140. fixed: 'left',
  141. formatter(row: any) {
  142. return (
  143. <SecNumberStatus
  144. secretNumber={row.secretNumber}
  145. checked={row.checked}
  146. corrected={row.corrected}
  147. ></SecNumberStatus>
  148. )
  149. },
  150. },
  151. { label: '标准分', prop: 'score', width: 60 },
  152. { label: '评卷给分', prop: 'markScore', width: 75 },
  153. // { label: '分组', prop: 'group' },
  154. { label: '分组', prop: 'groupName', width: 60 },
  155. { label: '评卷员', prop: 'markerName', minWidth: 60 },
  156. { label: '评卷时间', prop: 'markTime', width: 130 },
  157. ]
  158. //该接口貌似返回的全量,不需要pageSize
  159. const { fetch: getTrainingMonitorDetail, result: trainingMonitorDetail } = useFetch('getTrainingMonitorDetail')
  160. const { fetch: getAssessMonitorDetail, result: assessMonitorDetail } = useFetch('getAssessMonitorDetail')
  161. const fetchData = () => {
  162. query.stage === 'FORCE'
  163. ? getAssessMonitorDetail({ forceGroupMarkerId: query.forceGroupMarkerId as string })
  164. : getTrainingMonitorDetail({ markerId: query.markerId as string, taskType: query.stage as SamplePaperType })
  165. }
  166. const monitorDetail = computed(() => {
  167. return query.stage === 'FORCE' ? assessMonitorDetail.value : trainingMonitorDetail.value
  168. })
  169. const {
  170. tableRef,
  171. tableData,
  172. current,
  173. currentView: currentViewHistory,
  174. next: checkNext,
  175. visibleHistory,
  176. onDbClick,
  177. onCurrentChange,
  178. } = useTableCheck(monitorDetail)
  179. // watch(current, () => {
  180. // if (current.value) {
  181. // useFetch('viewActiveCheck').fetch({ taskId: current.value?.taskId })
  182. // }
  183. // })
  184. const imgOption = computed<SetImgBgOption>(() => {
  185. return {
  186. image: current?.value?.filePath,
  187. rotate: rotate.value,
  188. scale: scale.value,
  189. }
  190. })
  191. const { drawing, dataUrl } = useSetImgBg(imgOption, frontColor, setFrontColor)
  192. fetchData()
  193. </script>
  194. <style scoped lang="scss">
  195. .mark-container {
  196. .mark-content {
  197. position: relative;
  198. .preview {
  199. position: absolute;
  200. cursor: pointer;
  201. top: 20px;
  202. right: 25px;
  203. font-size: 38px;
  204. }
  205. .next-button {
  206. position: absolute;
  207. right: -20px;
  208. top: 300px;
  209. }
  210. .mark-content-paper {
  211. img {
  212. max-width: 100%;
  213. }
  214. }
  215. }
  216. .table-view {
  217. // width: 480px;
  218. .detail-info-label {
  219. .detail-info-label-num {
  220. min-width: 32px;
  221. height: 24px;
  222. line-height: 24px;
  223. background: #00987b;
  224. border-radius: 4px;
  225. }
  226. }
  227. }
  228. }
  229. </style>