index.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. <template>
  2. <div class="data-check">
  3. <div class="check-menu">
  4. <div class="check-menu-body">
  5. <ul>
  6. <li
  7. v-for="(item, index) in studentList"
  8. :key="item.id"
  9. :class="{ 'is-active': dataCheckStore.curStudent?.id === item.id }"
  10. @click="onSelectStudent(index)"
  11. >
  12. {{ item.imageName }}
  13. </li>
  14. </ul>
  15. </div>
  16. <div class="check-menu-page">
  17. <SimplePagination
  18. v-model="pageNumber"
  19. :total="total"
  20. :page-size="pageSize"
  21. @change="onChangeListPage"
  22. />
  23. </div>
  24. </div>
  25. <div class="check-body">
  26. <ScanImage
  27. v-if="dataCheckStore.curPage"
  28. :key="dataCheckStore.curPage.kid"
  29. :cant-change-img="true"
  30. @prev="onPrevPage"
  31. @next="onNextPage"
  32. />
  33. </div>
  34. <CheckAction @search="onSearch" />
  35. </div>
  36. </template>
  37. <script setup lang="ts">
  38. import { ref, reactive, onMounted, computed, onBeforeUnmount } from "vue";
  39. import { message } from "ant-design-vue";
  40. import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons-vue";
  41. import { DataCheckListFilter, DataCheckListItem } from "@/ap/types/dataCheck";
  42. import { allCheckList, fetchAndParseData } from "./api";
  43. import { StudentPage } from "./types";
  44. import { useDataCheckStore, useAppStore } from "@/store";
  45. import SimplePagination from "@/components/SimplePagination/index.vue";
  46. import ScanImage from "./ScanImage/index.vue";
  47. import CheckAction from "./CheckAction.vue";
  48. defineOptions({
  49. name: "DataCheck",
  50. });
  51. const dataCheckStore = useDataCheckStore();
  52. const appStore = useAppStore();
  53. dataCheckStore.resetInfo();
  54. let searchModel = {} as DataCheckListFilter;
  55. const pageNumber = ref(1);
  56. const pageSize = ref(20);
  57. const total = ref(0);
  58. const pageCount = ref(0);
  59. const allStudentList = ref<DataCheckListItem[]>([]);
  60. const studentList = ref<DataCheckListItem[]>([]);
  61. const dataList = ref<StudentPage[]>([]);
  62. const loading = ref(false);
  63. async function getAllStudents() {
  64. loading.value = true;
  65. const res = await allCheckList().catch(() => null);
  66. loading.value = false;
  67. if (!res) return;
  68. allStudentList.value = res;
  69. total.value = res.length;
  70. pageCount.value = Math.ceil(total.value / pageSize.value);
  71. }
  72. async function getList() {
  73. studentList.value = allStudentList.value.slice(
  74. (pageNumber.value - 1) * pageSize.value,
  75. pageNumber.value * pageSize.value
  76. );
  77. parseStudentPageList(studentList.value);
  78. }
  79. function parseStudentPageList(students: DataCheckListItem[]) {
  80. dataList.value = [] as StudentPage[];
  81. students.forEach((student, studentIndex) => {
  82. student.papers.forEach((paper, paperIndex) => {
  83. if (!paper.pages) return;
  84. paper.pages.forEach((page, pageIndex) => {
  85. const row: StudentPage = {
  86. ...page,
  87. paperId: paper.id,
  88. paperIndex,
  89. paperNumber: paper.number,
  90. pageIndex,
  91. studentIndex,
  92. studentId: student.id,
  93. examId: searchModel.examId,
  94. kid: `${student.id}-${studentIndex}-${paperIndex}-${pageIndex}`,
  95. };
  96. if (
  97. row.question?.result?.length &&
  98. Array.isArray(row.question?.result)
  99. ) {
  100. row.question.result = row.question.result.map((item: string) =>
  101. item.replace(/[^#a-zA-Z]/g, "")
  102. );
  103. }
  104. dataList.value.push(row);
  105. });
  106. });
  107. });
  108. }
  109. // table
  110. function onChangeListPage(index: number) {
  111. pageNumber.value = index;
  112. getList();
  113. selectPage(0);
  114. }
  115. async function onSearch() {
  116. pageNumber.value = 1;
  117. await getAllStudents();
  118. onChangeListPage(1);
  119. }
  120. // student
  121. function onSelectStudent(index: number) {
  122. const student = studentList.value[index];
  123. const pageIndex = dataList.value.findIndex(
  124. (item) => item.studentId === student.id
  125. );
  126. if (pageIndex === -1) return;
  127. selectPage(pageIndex);
  128. }
  129. async function onPrevStudent() {
  130. if (dataCheckStore.curStudentIndex <= 0) {
  131. if (pageNumber.value === 1) {
  132. message.error("没有上一个学生了");
  133. return;
  134. }
  135. pageNumber.value--;
  136. await getList();
  137. onSelectStudent(studentList.value.length - 1);
  138. return;
  139. }
  140. onSelectStudent(dataCheckStore.curStudentIndex - 1);
  141. }
  142. async function onNextStudent() {
  143. if (dataCheckStore.curStudentIndex >= studentList.value.length - 1) {
  144. if (pageNumber.value >= pageCount.value) {
  145. message.error("没有下一个学生了");
  146. return;
  147. }
  148. pageNumber.value++;
  149. await getList();
  150. onSelectStudent(0);
  151. return;
  152. }
  153. onSelectStudent(dataCheckStore.curStudentIndex + 1);
  154. }
  155. function getImgDpi(imgWidth: number): number {
  156. const dpi = (25.4 * imgWidth) / 183;
  157. if (Math.abs(dpi - 100) < 5) {
  158. return 100;
  159. }
  160. if (Math.abs(dpi - 150) < 5) {
  161. return 150;
  162. }
  163. return 150;
  164. }
  165. function getImgSize(url: string) {
  166. return new Promise<{ width: number; height: number }>((resolve) => {
  167. const img = new Image();
  168. img.src = url;
  169. console.log(url);
  170. img.onload = () => {
  171. resolve({
  172. width: img.width,
  173. height: img.height,
  174. });
  175. };
  176. });
  177. }
  178. // page
  179. async function selectPage(index: number) {
  180. const recogData = await fetchAndParseData(dataList.value[index].recogUri);
  181. const size = await getImgSize(dataList.value[index].sheetUri);
  182. dataList.value[index].recogDpi = getImgDpi(size.width);
  183. dataList.value[index].recogData = recogData;
  184. dataCheckStore.setInfo({
  185. curPage: dataList.value[index],
  186. curPageIndex: index,
  187. });
  188. if (!dataCheckStore.curPage) return;
  189. const curStudent = studentList.value[
  190. dataCheckStore.curPage.studentIndex
  191. ] as DataCheckListItem;
  192. dataCheckStore.setInfo({
  193. curStudent,
  194. curStudentIndex: dataCheckStore.curPage.studentIndex,
  195. });
  196. }
  197. async function onPrevPage() {
  198. if (dataCheckStore.curPageIndex <= 0) {
  199. if (pageNumber.value === 1) {
  200. message.error("没有上一张了");
  201. return;
  202. }
  203. pageNumber.value--;
  204. await getList();
  205. selectPage(dataList.value.length - 1);
  206. return;
  207. }
  208. selectPage(dataCheckStore.curPageIndex - 1);
  209. }
  210. async function onNextPage() {
  211. if (dataCheckStore.curPageIndex >= dataList.value.length - 1) {
  212. if (pageNumber.value >= pageCount.value) {
  213. message.error("没有下一张了");
  214. return;
  215. }
  216. pageNumber.value++;
  217. await getList();
  218. selectPage(0);
  219. return;
  220. }
  221. selectPage(dataCheckStore.curPageIndex + 1);
  222. }
  223. // shortcut
  224. function registShortcut() {
  225. document.addEventListener("keydown", shortcutHandle);
  226. }
  227. function removeShortcut() {
  228. document.removeEventListener("keydown", shortcutHandle);
  229. }
  230. function shortcutHandle(e: KeyboardEvent) {
  231. const moveAction = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
  232. if (!moveAction.includes(e.code) || e.repeat) {
  233. return;
  234. }
  235. e.preventDefault();
  236. if (e.code === "ArrowUp") {
  237. onPrevStudent();
  238. return;
  239. }
  240. if (e.code === "ArrowDown") {
  241. onNextStudent();
  242. return;
  243. }
  244. if (e.code === "ArrowLeft") {
  245. onPrevPage();
  246. return;
  247. }
  248. if (e.code === "ArrowRight") {
  249. onNextPage();
  250. return;
  251. }
  252. }
  253. onMounted(async () => {
  254. const appConfig = await window.electronApi.getAppIni();
  255. if (!appConfig.base.serverUrl) {
  256. message.error("请先设置服务器地址");
  257. return;
  258. }
  259. appStore.setState({ serverUrl: appConfig.base.serverUrl });
  260. registShortcut();
  261. onSearch();
  262. });
  263. onBeforeUnmount(() => {
  264. removeShortcut();
  265. });
  266. </script>