index.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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. export interface Task extends RawTask {
  215. /** 评卷结果,在task第一次被访问时自动添加,watch currentTask */
  216. markResult: MarkResult;
  217. /** 前端自用,用于标记阅卷开始时间和计算spent */
  218. __markStartTime: number;
  219. // 是否是科组长任务
  220. // 主观题检查时,科组长特殊标记使用headerTagList
  221. __isMarkLeader: string;
  222. // 轨迹图中可能存在客观题填涂框数据,答案数据,题卡数据等
  223. answerMap?: Record<string, string>;
  224. recogDatas?: string[];
  225. cardData?: Array<any>;
  226. }
  227. interface RawQuestion {
  228. /** 分组序号 */
  229. groupNumber: number;
  230. /** 大题号 */
  231. mainNumber: number;
  232. /** 小题号 */
  233. subNumber: string;
  234. /** 分数间隔 */
  235. intervalScore: number;
  236. /** 默认分数 */
  237. defaultScore: number;
  238. /** 限制最小分数 */
  239. minScore: number;
  240. /** 限制最大分数 */
  241. maxScore: number;
  242. /** 题目名称 */
  243. title: string;
  244. /** 轨迹列表 */
  245. trackList: Array<Track>;
  246. /** 得分;null的值时是为打回时可以被评卷修改的;null也是从未评分过的情况,要通过rejected来判断 */
  247. score: number | null;
  248. /** 未计分 */
  249. uncalculate: boolean;
  250. /** 选做题分组 */
  251. selectiveIndex: number | null;
  252. rejected?: boolean;
  253. questionName?: string;
  254. headerTrack?: any;
  255. }
  256. export interface Question extends RawQuestion {
  257. /** question 在 task 里面的 index ,用来对应 scoreList 的 score */
  258. __index: number;
  259. }
  260. export interface ColorMap {
  261. [key: string]: string;
  262. }
  263. /** 轨迹数据 */
  264. export interface Track {
  265. /** 大题号 */
  266. mainNumber: number;
  267. /** 小题号,当前api中只有number // 特殊标记中没有 */
  268. subNumber: string;
  269. /** 前端使用,暂时用不着,赋0 */
  270. number: number;
  271. /** 第几张图 */
  272. offsetIndex: number;
  273. /** 左上角为原点 */
  274. offsetX: number;
  275. offsetY: number;
  276. /** 相对slice的位置比例 */
  277. positionX: number;
  278. positionY: number;
  279. /** 评分数 */
  280. score: number;
  281. /** 是否此处未作答,未作答时,score默认是-0分 */
  282. unanswered: boolean;
  283. userId?: string;
  284. userName?: string;
  285. color?: string;
  286. isByMultMark?: boolean;
  287. // 是否是科组长评卷轨迹
  288. headerMarkScore?: boolean;
  289. }
  290. /** 特殊标记数据 */
  291. export interface SpecialTag {
  292. /** 第几张图 */
  293. offsetIndex: number;
  294. /** 左上角为原点(原图的原点),及相对原图的位置比例 */
  295. offsetX: number;
  296. offsetY: number;
  297. /** 相对裁切图的位置比例 */
  298. positionX: number;
  299. positionY: number;
  300. /** 特殊标记的字符串,勾叉 */
  301. tagName: string;
  302. tagType: "TEXT" | "CIRCLE" | "RIGHT" | "WRONG" | "HALF_RIGTH" | "LINE";
  303. // 分组号
  304. groupNumber?: number;
  305. userId?: number;
  306. color?: string;
  307. isByMultMark?: boolean;
  308. }
  309. export interface UISetting {
  310. /** 给分面板展示形态 */
  311. "score.board.collapse": boolean;
  312. /** 0.2 gap */
  313. /** 试卷缩放比例 */
  314. "answer.paper.scale": number;
  315. /**
  316. * 给分模式
  317. * "keyboard": 键盘模式
  318. * "mouse": 鼠标模式
  319. * */
  320. "normal.mode": "keyboard" | "mouse";
  321. /** 弹窗显示试卷 */
  322. "paper.modal": boolean;
  323. /** 弹窗显示答案 */
  324. "answer.modal": boolean;
  325. /** 弹窗显示缩略图 */
  326. "minimap.modal": boolean;
  327. /** 弹窗显示特殊字符 */
  328. "specialTag.modal": boolean;
  329. /** 弹窗显示快捷键配置 */
  330. "shortCut.modal": boolean;
  331. /** 0.1 gap */
  332. /** 分数标记缩放比例 */
  333. "score.fontSize.scale": number;
  334. }
  335. export interface MarkResult {
  336. taskId: number;
  337. studentId: number;
  338. statusValue: "TRIAL" | "FORMAL" | null;
  339. /** 毫秒单位 */
  340. spent: number;
  341. // 轨迹 or 键盘
  342. markerScore: number | null;
  343. /** 轨迹列表 */
  344. trackList: Array<Track>;
  345. /** 给分列表 */
  346. scoreList: Array<number | null>;
  347. /** 轨迹和键盘都需要 */
  348. specialTagList: Array<SpecialTag>;
  349. /** 问题卷 */
  350. problem: boolean;
  351. /** 问题卷类型 */
  352. problemType: string;
  353. /** 其他问题卷说明 */
  354. problemRemark: string;
  355. /** 当前task是否为学生未选做 */
  356. unselective: boolean;
  357. }
  358. /** 前端自用,用来渲染裁切图 */
  359. export interface SliceImage {
  360. /** 当前是 ObjectURL , 因为 DataURL 性能太差 */
  361. url: string;
  362. indexInSliceUrls: number;
  363. trackList: Array<Track>;
  364. tagList: Array<SpecialTag>;
  365. // originalImageWidth: number; // 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
  366. // originalImageHeight: number; // 为了兼容原图还原轨迹而添加的属性,当前CommonMarkBody用不到
  367. sliceImageWidth: number;
  368. sliceImageHeight: number;
  369. /** 裁切图在原图中的左上角的x偏移量 */
  370. dx: number;
  371. /** 裁切图在原图中的左上角的y偏移量 */
  372. dy: number;
  373. /** 在多个图片从高至低排列中累积的高度 */
  374. accumTopHeight: number;
  375. /** 当前裁切图有效宽度,大小不一的裁切图时有用。
  376. * 为了能让多张图统一比例的缩放,所以将所有的图的宽度设为一样了。 */
  377. effectiveWidth: number;
  378. }
  379. export type MarkHistoryOrderBy =
  380. | "marker_time"
  381. | "inspect_time"
  382. | "marker_score"
  383. | "seceret_number"
  384. | undefined;
  385. export type MarkHistorySortField = "ASC" | "DESC" | undefined;
  386. export interface HistoryQueryParams {
  387. /** 从1开始 */
  388. pageNumber?: number;
  389. pageSize?: number;
  390. order?: MarkHistoryOrderBy;
  391. sort?: MarkHistorySortField;
  392. secretNumber?: string | null;
  393. subjectCode?: string;
  394. groupNumber?: string;
  395. markerId?: string;
  396. markerScore?: string;
  397. }
  398. export interface GetHistory {
  399. (historyQuery: HistoryQueryParams): Promise<
  400. AxiosResponse<Task[]> | undefined
  401. >;
  402. }
  403. export interface CommonResponse {
  404. /** 请求是否成功 */
  405. success: boolean;
  406. /** 错误消息 */
  407. message: string;
  408. }
  409. /** 仲裁用:评卷明细 */
  410. export interface MarkDetail {
  411. markerName: string;
  412. markerTime: number;
  413. totalScore: number;
  414. scoreList: string;
  415. }
  416. //#region 多媒体评卷
  417. export interface RichTextQuestion {
  418. /** 题目的综合题号 1-2-4 */
  419. unionOrder: string;
  420. body: RichTextJSON;
  421. parentBody: RichTextJSON | null;
  422. answer: Array<RichTextJSON> | null;
  423. objective: boolean | null;
  424. options: Array<{ number: number; body: RichTextJSON }> | null;
  425. hideAnswer?: boolean;
  426. }
  427. export interface QuestionForRender extends Omit<RichTextQuestion, "answer"> {
  428. studentAnswer: RichTextQuestion["answer"];
  429. standardAnswer: RichTextQuestion["answer"];
  430. score: number | null;
  431. totalScore: number;
  432. }
  433. export interface RichTextJSON {
  434. sections: RichTextSectionJSON[];
  435. }
  436. export interface RichTextSectionJSON {
  437. blocks: RichTextBlockJSON[];
  438. }
  439. export interface RichTextBlockJSON {
  440. type: "text" | "image" | "audio" | "cloze";
  441. value: string;
  442. param: {
  443. underline: boolean;
  444. bold: boolean;
  445. italic: boolean;
  446. width: string;
  447. height: string;
  448. } | null;
  449. }
  450. export interface StudentAnswer {
  451. mainNumber: number;
  452. subNumber: string;
  453. subIndex: string;
  454. answer: Array<RichTextJSON> | null;
  455. }
  456. /** 云平台试卷格式 */
  457. export type ECSPaperJSON = {
  458. mainNumber: number;
  459. subNumber: string;
  460. body: RichTextJSON;
  461. parentBody: RichTextJSON | null;
  462. answer: RichTextJSON;
  463. }[];
  464. /** 在线考试平台试卷格式 */
  465. export type OExamPaperJSON = OExamPaperJSONQuestionList[];
  466. interface OExamPaperJSONQuestionList {
  467. /** 大题号 */
  468. number: number;
  469. questions: OExamPaperJSONQuestion[];
  470. }
  471. interface OExamPaperJSONQuestion {
  472. number: number;
  473. body: RichTextJSON;
  474. answer: RichTextJSON[] | null;
  475. objective: boolean | null;
  476. options: Array<{ number: number; body: RichTextJSON }>;
  477. subQuestions: OExamPaperJSONQuestion[] | null;
  478. /** 1-单选,2-多选,3-判断,4-填空,5-问答,6-套题,7-听力,8-配对题 */
  479. structType: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
  480. }
  481. //#endregion
  482. export type StudentObjectiveInfo = {
  483. studentId: string;
  484. studentName: string;
  485. studentCode: string;
  486. campusName: string;
  487. courseCode: string;
  488. courseName: string;
  489. paperNumber: string;
  490. objectiveScore: number;
  491. subjectiveScore: number;
  492. upload: boolean;
  493. absent: boolean;
  494. paperType: string;
  495. sheetUrls: Array<{ index: number; url: string; recogData: string }>;
  496. answers: Array<{
  497. mainNumber: number;
  498. subNumber: string;
  499. answer: string;
  500. exist: boolean;
  501. questionType: string;
  502. }>;
  503. titles: { [index: number]: string };
  504. success: boolean;
  505. };
  506. export type StudentSubjectiveInfo = {
  507. studentId: string;
  508. studentName: string;
  509. studentCode: string;
  510. campusName: string;
  511. courseCode: string;
  512. courseName: string;
  513. paperNumber: string;
  514. objectiveScore: number;
  515. subjectiveScore: number;
  516. upload: boolean;
  517. absent: boolean;
  518. paperType: string;
  519. sheetUrls: Array<{ index: number; url: string }>;
  520. answers: Array<{
  521. mainNumber: number;
  522. subNumber: string;
  523. answer: string;
  524. exist: boolean;
  525. questionType: string;
  526. }>;
  527. titles: { [index: number]: string };
  528. success: boolean;
  529. };
  530. export interface PaperRecogData {
  531. page_index: number;
  532. question: Array<{
  533. index: number;
  534. fill_result: Array<{
  535. main_number: number;
  536. sub_number: number;
  537. single: number;
  538. fill_option: number[];
  539. suspect_flag: number;
  540. fill_position: string[];
  541. fill_size: number[];
  542. }>;
  543. }>;
  544. }
  545. export interface CardData {
  546. id: string;
  547. content: string;
  548. }