index.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. import type { AxiosResponse } from "axios";
  2. interface SplitConfig {
  3. /** index of sheets */
  4. i: number;
  5. /** 覆盖区域的width */
  6. w: number;
  7. /** 覆盖区域的height */
  8. h: number;
  9. /** 从哪里开始覆盖 左上角为 (0, 0) */
  10. x: number;
  11. /** 从哪里开始覆盖 左上角为 (0, 0) */
  12. y: number;
  13. }
  14. type SingleSheetConfig = SplitConfig;
  15. export type PictureSlice = SplitConfig;
  16. export interface MarkStore {
  17. setting: Setting;
  18. groups: Array<Group>;
  19. status: {
  20. /** 个人评卷数量 */
  21. personCount: number;
  22. /** 总评卷数量 */
  23. markedCount: number;
  24. /** 总数量 */
  25. totalCount: number;
  26. /** 问题卷数量 */
  27. problemCount: number;
  28. /** 待仲裁卷数量 */
  29. arbitrateCount: number;
  30. /**成绩校验,批量校验时,已校验的学生id的集合 */
  31. markedCountStuIds?: any;
  32. };
  33. /** 保持数量为3(prefetchCount) */
  34. tasks: Array<Task>;
  35. /** 用来切换task,还有回看 */
  36. currentTask?: Task;
  37. currentQuestion?: Question;
  38. currentScore?: number;
  39. currentSpecialTag?: string;
  40. currentSpecialTagType?: SpecialTag.tagType;
  41. /** 是否打开回评侧边栏 */
  42. historyOpen: boolean;
  43. historyTasks: Array<Task>;
  44. /** 删除这些轨迹 */
  45. removeScoreTracks: Array<Track>;
  46. /** 聚焦这些tracks */
  47. focusTracks: Array<Track>;
  48. message: string | null;
  49. maxModalZIndex: number;
  50. /** 缩略图设置滚动到宽度的百分比 */
  51. minimapScrollToX?: number;
  52. /** 缩略图设置滚动到高度的百分比 */
  53. minimapScrollToY?: number;
  54. /** 是否显示全卷 */
  55. allPaperModal: boolean;
  56. /** 是否显示原卷 */
  57. sheetViewModal: boolean;
  58. /** 是否全局遮盖 */
  59. globalMask: boolean;
  60. }
  61. export interface Setting {
  62. /** 扫描图片或者多媒体,多媒体只允许 common mode */
  63. examType: "SCAN_IMAGE" | "MULTI_MEDIA";
  64. /** 阅卷模式 TRACK | COMMON */
  65. mode: "TRACK" | "COMMON";
  66. /** 是否允许模式切换,true为不允许 */
  67. forceMode: boolean;
  68. /** 是否显示原图功能 */
  69. sheetView: boolean;
  70. /** 是否自动跳转 */
  71. autoScroll: boolean;
  72. /** 原图遮盖规则 */
  73. sheetConfig: Array<SingleSheetConfig>;
  74. /** 是否开启全零分 */
  75. enableAllZero: boolean;
  76. /** 是否允许裁切 */
  77. enableSplit: boolean;
  78. /** 图片服务地址 */
  79. fileServer: string;
  80. /** 评卷员姓名 */
  81. userName: string;
  82. /** 科目信息(试卷和答案功能)*/
  83. subject: Subject;
  84. /** 强制标记是否开启 */
  85. forceSpecialTag: boolean;
  86. /** 评卷小工具控制字段 */
  87. uiSetting: UISetting;
  88. /** 只显示试评名称 TRIAL("试评"), FORMAL("正评"), FINISH("结束"): 结束状态不会在评卷端出现 */
  89. statusValue: "TRIAL" | "FORMAL" | null;
  90. /** 问题卷类型 */
  91. problemTypes: Array<{ id: number; name: string }>;
  92. /** 当前评卷分组号 */
  93. groupNumber: number;
  94. /** 当前评卷分组名称 */
  95. groupTitle: string;
  96. /** 推荐老师评卷的数量,到达这个数量提示老师 */
  97. topCount: number;
  98. /** 使用裁切整图时的裁切配置 [0,1]|[0,0.3,0.25,0.55], */
  99. splitConfig: Array<number>;
  100. /** 预加载任务数量 */
  101. prefetchCount: number;
  102. /** 评卷开始时间 */
  103. startTime: number;
  104. /** 评卷结束时间 */
  105. endTime: number;
  106. /** 是否是未选做类型 */
  107. selective: boolean;
  108. /** 可回评数量是否有限制数 */
  109. remarkCount?: any;
  110. /** 是否展示双评的轨迹 */
  111. doubleTrack?: boolean;
  112. /** 全卷复核页面是否要滑到页面底部才允许点击复核按钮 */
  113. inspectScroll?: boolean;
  114. /** 评卷员页头是否展示客观分 */
  115. showObjectiveScore?: boolean;
  116. }
  117. /** 科目信息(试卷和答案功能) */
  118. interface RawSubject {
  119. /** 科目名称 */
  120. name: string;
  121. /** 科目编号 */
  122. code: string;
  123. /** 学生答案json url */
  124. answerUrl: string;
  125. /** 试卷json url */
  126. paperUrl: string;
  127. }
  128. /** 科目信息(试卷和答案功能)增加前端自定义 questions字段*/
  129. interface Subject extends RawSubject {
  130. /** 前端自定义questions字段, 在多媒体阅卷模式下使用 */
  131. questions: Array<RichTextQuestion>;
  132. }
  133. // setting for admin page
  134. export interface AdminPageSetting {
  135. /** 扫描图片或者多媒体,多媒体只允许 common mode */
  136. examType: "SCAN_IMAGE" | "MULTI_MEDIA";
  137. /** 图片服务地址 */
  138. fileServer: string;
  139. /** 管理员姓名 */
  140. userName: string;
  141. /** 科目信息 */
  142. subject: Subject;
  143. /** 使用裁切整图时的裁切配置 [0,1]|[0,0.3,0.25,0.55], */
  144. splitConfig: Array<number>;
  145. enableSplit: boolean;
  146. doubleTrack?: boolean;
  147. inspectScroll?: boolean;
  148. }
  149. export interface AdminPageSettingForImport extends AdminPageSetting {
  150. studentIds: number[];
  151. tagIds: number[];
  152. inspectCount: number;
  153. message: string;
  154. }
  155. export interface Group {
  156. /** 分组编号 */
  157. number: number;
  158. /** 分组试题 */
  159. groupQuestions: string;
  160. /** 分组title */
  161. title: string;
  162. /** 总评卷数量 */
  163. markedCount: number;
  164. /** 总数量 */
  165. totalCount: number;
  166. }
  167. interface RawTask {
  168. taskId: string;
  169. /** 学生ID */
  170. studentId: string;
  171. /** 任务编号 */
  172. secretNumber: string;
  173. /** 后端处理是否显示 */
  174. /** 学生名称 */
  175. studentName: string;
  176. /** 学生编号 */
  177. studentCode: string;
  178. /** 考试编号 */
  179. examNumber: string;
  180. /** 试卷编号 */
  181. paperNumber?: string;
  182. /** 一般不要用此处的subject,优先用setting.subject */
  183. subject?: Subject;
  184. /** 裁切图url */
  185. sliceUrls: Array<string>;
  186. /** 最高显示优先级,有sliceConfig就用sliceConfig,否则使用sheetConfig */
  187. sliceConfig: Array<PictureSlice>;
  188. /** sliceUrls为空,则是多媒体阅卷,显示JSON */
  189. jsonUrl: string;
  190. questionList: Array<Question>;
  191. specialTagList: Array<SpecialTag>;
  192. /** 原图url */
  193. sheetUrls: Array<string>;
  194. /** 客观分 复核也用到 */
  195. objectiveScore: number;
  196. /** 评卷总分 */
  197. markerScore: number;
  198. /** 评卷时间 */
  199. markerTime: number;
  200. /** 复核有用 */
  201. inspectTime?: number;
  202. /** 是否自评,暂时用不着 */
  203. self: boolean;
  204. /** 是否回评 */
  205. previous: boolean;
  206. /** 是否是打回 */
  207. rejected: boolean;
  208. /** 打回原因 冒号拼接的 */
  209. rejectReason: string;
  210. message: string | null;
  211. rejectScoreList?: any;
  212. headerTagList?: any;
  213. }
  214. type ElementType =
  215. | "FILL_QUESTION"
  216. | "FILL_LINE"
  217. | "EXPLAIN"
  218. | "COMPOSITION"
  219. | "TOPIC_HEAD"
  220. | "CARD_HEAD";
  221. interface CardBaseElement {
  222. id: string;
  223. type: ElementType;
  224. topicNo: number;
  225. startNumber: number;
  226. questionsCount: number;
  227. }
  228. interface CardElement extends CardBaseElement {
  229. parent: CardBaseElement;
  230. }
  231. export interface CardDataItem {
  232. exchange: {
  233. answer_area: Array<{
  234. main_number: number;
  235. sub_number: number | string;
  236. area: [number, number, number, number];
  237. }>;
  238. fill_area: Array<{
  239. field: "question" | "examNumber";
  240. index: number;
  241. single: boolean;
  242. horizontal: boolean;
  243. items: Array<{
  244. main_number: number;
  245. sub_number: number | string;
  246. options: [number, number, number, number][];
  247. }>;
  248. }>;
  249. };
  250. columns: Array<{
  251. elements: CardElement[];
  252. }>;
  253. }
  254. export interface Task extends RawTask {
  255. /** 评卷结果,在task第一次被访问时自动添加,watch currentTask */
  256. markResult: MarkResult;
  257. /** 前端自用,用于标记阅卷开始时间和计算spent */
  258. __markStartTime: number;
  259. // 是否是科组长任务
  260. // 主观题检查时,科组长特殊标记使用headerTagList
  261. __isMarkLeader: string;
  262. // 轨迹图中可能存在客观题填涂框数据,答案数据,题卡数据等
  263. answerMap?: Record<
  264. string,
  265. { answer: string; isRight: boolean; score: number; totalScore: number }
  266. >;
  267. recogDatas?: string[];
  268. cardData?: Array<CardDataItem>;
  269. }
  270. interface RawQuestion {
  271. /** 分组序号 */
  272. groupNumber: number;
  273. /** 大题号 */
  274. mainNumber: number;
  275. /** 小题号 */
  276. subNumber: string;
  277. /** 分数间隔 */
  278. intervalScore: number;
  279. /** 默认分数 */
  280. defaultScore: number;
  281. /** 限制最小分数 */
  282. minScore: number;
  283. /** 限制最大分数 */
  284. maxScore: number;
  285. /** 题目名称 */
  286. title: string;
  287. /** 轨迹列表 */
  288. trackList: Array<Track>;
  289. /** 得分;null的值时是为打回时可以被评卷修改的;null也是从未评分过的情况,要通过rejected来判断 */
  290. score: number | null;
  291. /** 未计分 */
  292. uncalculate: boolean;
  293. /** 选做题分组 */
  294. selectiveIndex: number | null;
  295. /** 无轨迹情况下评卷员打分信息 */
  296. markerList: null | Array<{
  297. // 是否是科组长
  298. header: boolean;
  299. loginName: string;
  300. score: number;
  301. userId: string;
  302. userName: string;
  303. }>;
  304. rejected?: boolean;
  305. questionName?: string;
  306. headerTrack?: Array<Track>;
  307. }
  308. export interface Question extends RawQuestion {
  309. /** question 在 task 里面的 index ,用来对应 scoreList 的 score */
  310. __index: number;
  311. }
  312. export interface ColorMap {
  313. [key: string]: string;
  314. }
  315. /** 轨迹数据 */
  316. export interface Track {
  317. /** 大题号 */
  318. mainNumber: number;
  319. /** 小题号,当前api中只有number // 特殊标记中没有 */
  320. subNumber: string;
  321. /** 前端使用,暂时用不着,赋0 */
  322. number: number;
  323. /** 第几张图 */
  324. offsetIndex: number;
  325. /** 左上角为原点 */
  326. offsetX: number;
  327. offsetY: number;
  328. /** 相对slice的位置比例 */
  329. positionX: number;
  330. positionY: number;
  331. /** 评分数 */
  332. score: number;
  333. /** 是否此处未作答,未作答时,score默认是-0分 */
  334. unanswered: boolean;
  335. userId?: string;
  336. userName?: string;
  337. color?: string;
  338. isByMultMark?: boolean;
  339. // 是否是科组长评卷轨迹
  340. headerMarkScore?: boolean;
  341. }
  342. /** 特殊标记数据 */
  343. export interface SpecialTag {
  344. /** 第几张图 */
  345. offsetIndex: number;
  346. /** 左上角为原点(原图的原点),及相对原图的位置比例 */
  347. offsetX: number;
  348. offsetY: number;
  349. /** 相对裁切图的位置比例 */
  350. positionX: number;
  351. positionY: number;
  352. /** 特殊标记的字符串,勾叉 */
  353. tagName: string;
  354. tagType: "TEXT" | "CIRCLE" | "RIGHT" | "WRONG" | "HALF_RIGTH" | "LINE";
  355. // 分组号
  356. groupNumber?: number;
  357. userId?: number;
  358. color?: string;
  359. isByMultMark?: boolean;
  360. }
  361. export interface UISetting {
  362. /** 给分面板展示形态 */
  363. "score.board.collapse": boolean;
  364. /** 0.2 gap */
  365. /** 试卷缩放比例 */
  366. "answer.paper.scale": number;
  367. /**
  368. * 给分模式
  369. * "keyboard": 键盘模式
  370. * "mouse": 鼠标模式
  371. * */
  372. "normal.mode": "keyboard" | "mouse";
  373. /** 弹窗显示试卷 */
  374. "paper.modal": boolean;
  375. /** 弹窗显示答案 */
  376. "answer.modal": boolean;
  377. /** 弹窗显示缩略图 */
  378. "minimap.modal": boolean;
  379. /** 弹窗显示特殊字符 */
  380. "specialTag.modal": boolean;
  381. /** 弹窗显示快捷键配置 */
  382. "shortCut.modal": boolean;
  383. /** 0.1 gap */
  384. /** 分数标记缩放比例 */
  385. "score.fontSize.scale": number;
  386. }
  387. export interface MarkResult {
  388. taskId: number;
  389. studentId: number;
  390. statusValue: "TRIAL" | "FORMAL" | null;
  391. /** 毫秒单位 */
  392. spent: number;
  393. // 轨迹 or 键盘
  394. markerScore: number | null;
  395. /** 轨迹列表 */
  396. trackList: Array<Track>;
  397. /** 给分列表 */
  398. scoreList: Array<number | null>;
  399. /** 轨迹和键盘都需要 */
  400. specialTagList: Array<SpecialTag>;
  401. /** 问题卷 */
  402. problem: boolean;
  403. /** 问题卷类型 */
  404. problemType: string;
  405. /** 其他问题卷说明 */
  406. problemRemark: string;
  407. /** 当前task是否为学生未选做 */
  408. unselective: boolean;
  409. }
  410. /** 前端自用,用来渲染裁切图 */
  411. export interface SliceImage {
  412. /** 当前是 ObjectURL , 因为 DataURL 性能太差 */
  413. url: string;
  414. indexInSliceUrls: number;
  415. trackList: Array<Track>;
  416. tagList: Array<SpecialTag>;
  417. // originalImageWidth: number; // 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
  418. // originalImageHeight: number; // 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
  419. sliceImageWidth: number;
  420. sliceImageHeight: number;
  421. /** 裁切图在原图中的左上角的x偏移量 */
  422. dx: number;
  423. /** 裁切图在原图中的左上角的y偏移量 */
  424. dy: number;
  425. /** 在多个图片从高至低排列中累积的高度 */
  426. accumTopHeight: number;
  427. /** 当前裁切图有效宽度,大小不一的裁切图时有用。
  428. * 为了能让多张图统一比例的缩放,所以将所有的图的宽度设为一样了。 */
  429. effectiveWidth: number;
  430. }
  431. export type MarkHistoryOrderBy =
  432. | "marker_time"
  433. | "inspect_time"
  434. | "marker_score"
  435. | "seceret_number"
  436. | undefined;
  437. export type MarkHistorySortField = "ASC" | "DESC" | undefined;
  438. export interface HistoryQueryParams {
  439. /** 从1开始 */
  440. pageNumber?: number;
  441. pageSize?: number;
  442. order?: MarkHistoryOrderBy;
  443. sort?: MarkHistorySortField;
  444. secretNumber?: string | null;
  445. subjectCode?: string;
  446. groupNumber?: string;
  447. markerId?: string;
  448. markerScore?: string;
  449. }
  450. export interface GetHistory {
  451. (historyQuery: HistoryQueryParams): Promise<
  452. AxiosResponse<Task[]> | undefined
  453. >;
  454. }
  455. export interface CommonResponse {
  456. /** 请求是否成功 */
  457. success: boolean;
  458. /** 错误消息 */
  459. message: string;
  460. }
  461. /** 仲裁用:评卷明细 */
  462. export interface MarkDetail {
  463. markerName: string;
  464. markerTime: number;
  465. totalScore: number;
  466. scoreList: string;
  467. }
  468. //#region 多媒体评卷
  469. export interface RichTextQuestion {
  470. /** 题目的综合题号 1-2-4 */
  471. unionOrder: string;
  472. body: RichTextJSON;
  473. parentBody: RichTextJSON | null;
  474. answer: Array<RichTextJSON> | null;
  475. objective: boolean | null;
  476. options: Array<{ number: number; body: RichTextJSON }> | null;
  477. hideAnswer?: boolean;
  478. }
  479. export interface QuestionForRender extends Omit<RichTextQuestion, "answer"> {
  480. studentAnswer: RichTextQuestion["answer"];
  481. standardAnswer: RichTextQuestion["answer"];
  482. score: number | null;
  483. totalScore: number;
  484. }
  485. export interface RichTextJSON {
  486. sections: RichTextSectionJSON[];
  487. }
  488. export interface RichTextSectionJSON {
  489. blocks: RichTextBlockJSON[];
  490. }
  491. export interface RichTextBlockJSON {
  492. type: "text" | "image" | "audio" | "cloze";
  493. value: string;
  494. param: {
  495. underline: boolean;
  496. bold: boolean;
  497. italic: boolean;
  498. width: string;
  499. height: string;
  500. } | null;
  501. }
  502. export interface StudentAnswer {
  503. mainNumber: number;
  504. subNumber: string;
  505. subIndex: string;
  506. answer: Array<RichTextJSON> | null;
  507. }
  508. /** 云平台试卷格式 */
  509. export type ECSPaperJSON = {
  510. mainNumber: number;
  511. subNumber: string;
  512. body: RichTextJSON;
  513. parentBody: RichTextJSON | null;
  514. answer: RichTextJSON;
  515. }[];
  516. /** 在线考试平台试卷格式 */
  517. export type OExamPaperJSON = OExamPaperJSONQuestionList[];
  518. interface OExamPaperJSONQuestionList {
  519. /** 大题号 */
  520. number: number;
  521. questions: OExamPaperJSONQuestion[];
  522. }
  523. interface OExamPaperJSONQuestion {
  524. number: number;
  525. body: RichTextJSON;
  526. answer: RichTextJSON[] | null;
  527. objective: boolean | null;
  528. options: Array<{ number: number; body: RichTextJSON }>;
  529. subQuestions: OExamPaperJSONQuestion[] | null;
  530. /** 1-单选,2-多选,3-判断,4-填空,5-问答,6-套题,7-听力,8-配对题 */
  531. structType: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
  532. }
  533. //#endregion
  534. export type StudentObjectiveInfo = {
  535. studentId: string;
  536. studentName: string;
  537. studentCode: string;
  538. campusName: string;
  539. courseCode: string;
  540. courseName: string;
  541. paperNumber: string;
  542. objectiveScore: number;
  543. subjectiveScore: number;
  544. upload: boolean;
  545. absent: boolean;
  546. paperType: string;
  547. sheetUrls: Array<{ index: number; url: string; recogData: string }>;
  548. answers: Array<{
  549. mainNumber: number;
  550. subNumber: string;
  551. answer: string;
  552. exist: boolean;
  553. questionType: string;
  554. standardAnswer: string;
  555. score: number;
  556. totalScore: number;
  557. }>;
  558. titles: { [index: number]: string };
  559. success: boolean;
  560. };
  561. export type StudentSubjectiveInfo = {
  562. studentId: string;
  563. studentName: string;
  564. studentCode: string;
  565. campusName: string;
  566. courseCode: string;
  567. courseName: string;
  568. paperNumber: string;
  569. objectiveScore: number;
  570. subjectiveScore: number;
  571. upload: boolean;
  572. absent: boolean;
  573. paperType: string;
  574. sheetUrls: Array<{ index: number; url: string }>;
  575. answers: Array<{
  576. mainNumber: number;
  577. subNumber: string;
  578. answer: string;
  579. exist: boolean;
  580. questionType: string;
  581. }>;
  582. titles: { [index: number]: string };
  583. success: boolean;
  584. };
  585. export interface StudentSubjectiveCheck {
  586. examId: string;
  587. paperNumber: string;
  588. studentId: string;
  589. }
  590. export interface PaperRecogData {
  591. page_index: number;
  592. question: Array<{
  593. index: number;
  594. fill_result: Array<{
  595. main_number: number;
  596. sub_number: number;
  597. single: number;
  598. fill_option: number[];
  599. suspect_flag: number;
  600. fill_position: string[];
  601. fill_size: number[];
  602. }>;
  603. }>;
  604. }
  605. export interface CardData {
  606. id: string;
  607. content: string;
  608. }