RecognizeArbitrate.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. <template>
  2. <div class="arbitrate">
  3. <div class="arbitrate-title">
  4. <a-button class="arbitrate-back" @click="goback">
  5. <template #icon><SwapLeftOutlined /> </template>后退
  6. </a-button>
  7. <div class="arbitrate-stat">
  8. <p class="color-success">
  9. <CheckCircleFilled />已处理:{{ progress.finishCount }}
  10. </p>
  11. <p class="color-error">
  12. <CloseCircleFilled />未处理:{{ progress.todoCount }}
  13. </p>
  14. </div>
  15. </div>
  16. <div class="arbitrate-body">
  17. <RecognizeImage
  18. v-if="curArbitrateTaskDetail"
  19. :img-src="curArbitrateTaskDetail.uri"
  20. />
  21. <!-- arbitrate action modal -->
  22. <div v-if="curArbitrateTaskDetail" class="arbitrate-modal">
  23. <a-row align="top" :gutter="8" class="m-b-8px">
  24. <a-col :span="6">
  25. <div class="modal-box">
  26. <p class="box-title">客观题</p>
  27. <p class="box-cont">{{ curTaskDetailName }}</p>
  28. </div>
  29. </a-col>
  30. <a-col :span="12">
  31. <div class="modal-box modal-origin"></div>
  32. </a-col>
  33. <a-col :span="6">
  34. <div class="modal-box" @click="changePrevTaskDetail">
  35. <p class="box-title">左键</p>
  36. <p class="box-cont">上一个</p>
  37. </div>
  38. </a-col>
  39. </a-row>
  40. <a-row align="top" :gutter="8">
  41. <a-col :span="3">
  42. <div class="modal-box">
  43. <p class="box-title">一评结果</p>
  44. <p class="box-cont">{{ curArbitrateTaskDetail.result1 }}</p>
  45. </div>
  46. </a-col>
  47. <a-col :span="3">
  48. <div class="modal-box">
  49. <p class="box-title">二评结果</p>
  50. <p class="box-cont">{{ curArbitrateTaskDetail.result2 }}</p>
  51. </div>
  52. </a-col>
  53. <a-col :span="12">
  54. <div class="modal-box modal-options">
  55. <a-button
  56. v-for="option in curArbitrateTaskDetail.options"
  57. :key="option"
  58. :type="
  59. curArbitrateTaskDetail.value.result.includes(option)
  60. ? 'primary'
  61. : 'default'
  62. "
  63. @click="selectOption(option)"
  64. >{{ option }}</a-button
  65. >
  66. </div>
  67. </a-col>
  68. <a-col :span="6">
  69. <div class="modal-box">
  70. <p class="box-title">Enter键</p>
  71. <p class="box-cont">下一个</p>
  72. </div>
  73. </a-col>
  74. </a-row>
  75. </div>
  76. </div>
  77. </div>
  78. </template>
  79. <script setup lang="ts">
  80. import {
  81. computed,
  82. ref,
  83. reactive,
  84. onMounted,
  85. onBeforeUnmount,
  86. nextTick,
  87. } from "vue";
  88. import { useRoute, useRouter } from "vue-router";
  89. import {
  90. CheckCircleFilled,
  91. CloseCircleFilled,
  92. SwapLeftOutlined,
  93. } from "@ant-design/icons-vue";
  94. import { message } from "ant-design-vue";
  95. import RecognizeImage from "./RecognizeImage.vue";
  96. import {
  97. recognizeArbitrateTask,
  98. recognizeArbitrateSave,
  99. recognizeArbitrateProgress,
  100. recognizeArbitrateHistory,
  101. } from "@/ap/recognizeCheck";
  102. import {
  103. RecognizeArbitrateItem,
  104. RecognizeArbitrateTaskDetail,
  105. RecognizeArbitrateSavePage,
  106. } from "@/ap/types/recognizeCheck";
  107. import { parseRecogData, parseDetailSize } from "@/utils/recog/recog";
  108. defineOptions({
  109. name: "RecognizeArbitrate",
  110. });
  111. const router = useRouter();
  112. const route = useRoute();
  113. const groupId = route.params.groupId ? Number(route.params.groupId) : 0;
  114. // 任务进度
  115. const progress = ref({
  116. finishCount: 0,
  117. todoCount: 0,
  118. });
  119. async function updateProgress() {
  120. const res = await recognizeArbitrateProgress(groupId);
  121. progress.value = res || {};
  122. }
  123. const curArbitrateTaskDetails = ref([] as RecognizeArbitrateTaskDetail[]);
  124. const curArbitrateTaskDetailIndex = ref(0);
  125. const curArbitrateTaskDetail = ref<RecognizeArbitrateTaskDetail | null>(null);
  126. const nextArbitrateTaskDetail = computed(() => {
  127. return curArbitrateTaskDetails.value[curArbitrateTaskDetailIndex.value + 1];
  128. });
  129. // 下一个细分任务是否是另一个任务
  130. const nextDetailIsAnotherTask = computed(() => {
  131. return !nextArbitrateTaskDetail.value;
  132. });
  133. const curTaskDetailName = computed(() => {
  134. if (!curArbitrateTaskDetail.value) return "";
  135. if (curArbitrateTaskDetail.value.type === "question") {
  136. return `#${curArbitrateTaskDetail.value.index}`;
  137. }
  138. return "-";
  139. });
  140. // 获取任务
  141. let curTask: RecognizeArbitrateItem | null = null;
  142. async function getTask() {
  143. const res = await recognizeArbitrateTask(groupId).catch(() => false);
  144. curTask = res || null;
  145. if (!curTask) {
  146. return;
  147. }
  148. curArbitrateTaskDetails.value = parseDetails(curTask);
  149. }
  150. async function initData() {
  151. await getTask();
  152. curArbitrateTaskDetailIndex.value = 0;
  153. setCurTaskDetail();
  154. }
  155. // 解析仲裁任务详情
  156. function parseDetails(
  157. data: RecognizeArbitrateItem
  158. ): RecognizeArbitrateTaskDetail[] {
  159. const details: RecognizeArbitrateTaskDetail[] = [];
  160. data.pages.forEach((page) => {
  161. const recogData = parseRecogData(page.recogData);
  162. if (!recogData) return;
  163. // 缺考
  164. if (page.absent) {
  165. details.push({
  166. ...parseDetailSize(recogData.absent.fill_result[0], "absent", 0, []),
  167. result1: page.absent ? page.absent[0] : "",
  168. result2: page.absent ? page.absent[1] : "",
  169. pageIndex: page.index,
  170. groupId: groupId,
  171. uri: page.uri,
  172. });
  173. }
  174. // 违纪
  175. if (page.breach) {
  176. details.push({
  177. ...parseDetailSize(recogData.breach.fill_result[0], "breach", 0, []),
  178. result1: page.breach ? page.breach[0] : "",
  179. result2: page.breach ? page.breach[1] : "",
  180. pageIndex: page.index,
  181. groupId: groupId,
  182. uri: page.uri,
  183. });
  184. }
  185. // 试卷类型
  186. if (page.paperType) {
  187. details.push({
  188. ...parseDetailSize(
  189. recogData.paperType.fill_result[0],
  190. "paperType",
  191. 0,
  192. []
  193. ),
  194. result1: page.paperType ? page.paperType[0] : "",
  195. result2: page.paperType ? page.paperType[1] : "",
  196. pageIndex: page.index,
  197. groupId: groupId,
  198. uri: page.uri,
  199. });
  200. }
  201. // 试题
  202. let index = 0;
  203. recogData.question.forEach((gGroup) => {
  204. gGroup.fill_result.forEach((qRecog) => {
  205. qRecog.index = ++index;
  206. const questionResult = page.question[qRecog.index];
  207. const arbitrate = questionResult && questionResult.length >= 2;
  208. if (!arbitrate) return;
  209. details.push({
  210. ...parseDetailSize(qRecog, "question", qRecog.index, []),
  211. result1: questionResult ? questionResult[0] : "",
  212. result2: questionResult ? questionResult[1] : "",
  213. pageIndex: page.index,
  214. groupId: groupId,
  215. uri: page.uri,
  216. });
  217. });
  218. });
  219. });
  220. return details;
  221. }
  222. // 任务执行流程 ----------------- start>
  223. function setCurTaskDetail() {
  224. curArbitrateTaskDetail.value =
  225. curArbitrateTaskDetails.value[curArbitrateTaskDetailIndex.value];
  226. }
  227. const detailChanging = ref(false);
  228. async function changeNextTaskDetail() {
  229. if (detailChanging.value) return;
  230. detailChanging.value = true;
  231. if (nextDetailIsAnotherTask.value) {
  232. await submitCurTask().catch(() => {});
  233. await getNextTask().catch(() => {});
  234. await nextTick(() => {
  235. detailChanging.value = false;
  236. });
  237. return;
  238. }
  239. curArbitrateTaskDetailIndex.value++;
  240. setCurTaskDetail();
  241. setTimeout(() => {
  242. detailChanging.value = false;
  243. }, 500);
  244. }
  245. async function changePrevTaskDetail() {
  246. if (detailChanging.value) return;
  247. if (curArbitrateTaskDetailIndex.value === 0) {
  248. detailChanging.value = true;
  249. await getPrevTask().catch(() => {});
  250. await nextTick(() => {
  251. detailChanging.value = false;
  252. });
  253. return;
  254. }
  255. detailChanging.value = true;
  256. curArbitrateTaskDetailIndex.value--;
  257. setCurTaskDetail();
  258. setTimeout(() => {
  259. detailChanging.value = false;
  260. }, 500);
  261. }
  262. async function getPrevTask() {
  263. let result = true;
  264. const res = await recognizeArbitrateHistory({
  265. groupId,
  266. id: curTask?.id,
  267. }).catch(() => {
  268. result = false;
  269. });
  270. if (!result) {
  271. message.error("获取上一个任务失败!");
  272. return;
  273. }
  274. if (!res) {
  275. message.error("没有上一个任务了!");
  276. return;
  277. }
  278. curTask = res;
  279. curArbitrateTaskDetails.value = parseDetails(curTask);
  280. curArbitrateTaskDetailIndex.value = curArbitrateTaskDetails.value.length - 1;
  281. setCurTaskDetail();
  282. }
  283. async function getNextTask() {
  284. let result = true;
  285. const res = await recognizeArbitrateHistory({
  286. groupId,
  287. id: curTask?.id,
  288. next: true,
  289. }).catch(() => {
  290. result = false;
  291. });
  292. if (!result) {
  293. message.error("获取下一个任务失败!");
  294. return;
  295. }
  296. if (!res) {
  297. message.error("没有下一个任务了!");
  298. return;
  299. }
  300. curTask = res;
  301. curArbitrateTaskDetails.value = parseDetails(curTask);
  302. curArbitrateTaskDetailIndex.value = 0;
  303. setCurTaskDetail();
  304. }
  305. function selectOption(option: string) {
  306. if (!curArbitrateTaskDetail.value) return;
  307. // 单选直接赋值
  308. if (!curArbitrateTaskDetail.value.multiple) {
  309. curArbitrateTaskDetail.value.result = [option];
  310. return;
  311. }
  312. // 多选情况
  313. // 空直接赋值,空值与其他互斥
  314. if (option === "#") {
  315. curArbitrateTaskDetail.value.result = ["#"];
  316. return;
  317. }
  318. let result = curArbitrateTaskDetail.value.result.filter(
  319. (item) => item === "#"
  320. );
  321. if (result.includes(option)) {
  322. result = result.filter((item) => item !== option);
  323. } else {
  324. result.push(option);
  325. }
  326. // 保证result的顺序和options的顺序是一致的
  327. curArbitrateTaskDetail.value.result =
  328. curArbitrateTaskDetail.value.options.filter((item) =>
  329. result.includes(item)
  330. );
  331. }
  332. // 键盘事件
  333. function registKeyEvent() {
  334. document.addEventListener("keydown", keyEventHandle);
  335. }
  336. function removeKeyEvent() {
  337. document.removeEventListener("keydown", keyEventHandle);
  338. }
  339. function keyEventHandle(e: KeyboardEvent) {
  340. if (e.code === "Enter") {
  341. e.preventDefault();
  342. onConfirm();
  343. return;
  344. }
  345. }
  346. // 保存任务详情信息
  347. async function onConfirm() {
  348. if (!curArbitrateTaskDetail.value?.result.length) {
  349. message.error("请完成仲裁结果!");
  350. return;
  351. }
  352. await changeNextTaskDetail();
  353. }
  354. // 提交当前任务仲裁数据
  355. function getTaskTypeResult(
  356. type: "absent" | "breach" | "paperType",
  357. pageIndex: number
  358. ) {
  359. const taskDetail = curArbitrateTaskDetails.value.find(
  360. (item) => item.pageIndex === pageIndex && item.type === type
  361. );
  362. const result = taskDetail ? taskDetail.result.join() : null;
  363. if (type === "paperType") return result;
  364. return result ? result === "true" : null;
  365. }
  366. async function submitCurTask() {
  367. if (!curTask) return;
  368. const curArbitrateTaskDetailQuestionResults: Record<string, string> = {};
  369. curArbitrateTaskDetails.value.forEach((item) => {
  370. if (item.type !== "question") return;
  371. const k = `${item.pageIndex}-${item.index}`;
  372. curArbitrateTaskDetailQuestionResults[k] = item.result.join("");
  373. });
  374. const pages = curTask.pages.map((page) => {
  375. const absentResult = getTaskTypeResult(
  376. "absent",
  377. page.index
  378. ) as RecognizeArbitrateSavePage["absent"];
  379. const breachResult = getTaskTypeResult(
  380. "breach",
  381. page.index
  382. ) as RecognizeArbitrateSavePage["breach"];
  383. const paperTypeResult = getTaskTypeResult(
  384. "paperType",
  385. page.index
  386. ) as RecognizeArbitrateSavePage["paperType"];
  387. const questionResult: Record<number, string> = {};
  388. curTaskDetails.value.forEach((item) => {
  389. if (item.type !== "question") return;
  390. const k = `${item.pageIndex}-${item.index}`;
  391. const result = item.arbitrate
  392. ? curArbitrateTaskDetailQuestionResults[k]
  393. : item.result.join("");
  394. questionResult[item.index] = result;
  395. });
  396. const npage: RecognizeArbitrateSavePage = {
  397. index: page.index,
  398. absent: absentResult,
  399. breach: breachResult,
  400. paperType: paperTypeResult,
  401. question: questionResult,
  402. selective: null,
  403. };
  404. return npage;
  405. });
  406. await recognizeArbitrateSave({ id: curTask.id, pages }).catch(() => false);
  407. }
  408. // 任务执行流程 ----------------- end>
  409. // 返回
  410. function goback() {
  411. router.back();
  412. }
  413. onMounted(() => {
  414. initData();
  415. registKeyEvent();
  416. });
  417. onBeforeUnmount(() => {
  418. removeKeyEvent();
  419. });
  420. </script>