student-client.d.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. export type Store = {
  2. /** 当前用户 */
  3. user: {
  4. id: number;
  5. /** 身份证号 */
  6. identityNumber: string;
  7. /** 学号 */
  8. studentCodeList: string[];
  9. /** 显示的姓名 */
  10. displayName: string;
  11. /** @deprecated 姓名,但可能为空 */
  12. name: string;
  13. /** 学生手机号 */
  14. phoneNumber: string;
  15. /** 属于那个学校 */
  16. schoolDomain: SchoolDomain;
  17. /** 顶级机构id */
  18. rootOrgId: number;
  19. /** 底照URL */
  20. photoPath: string;
  21. /** 机构名称 */
  22. orgName: string;
  23. /** 顶级机构名称 */
  24. rootOrgName: string;
  25. /** 用户的另一个id */
  26. key: string;
  27. /** 登录认证信息 */
  28. token: string | null;
  29. /** 专业 */
  30. specialty: string;
  31. };
  32. /** 学生端配置。 Q代表qmth */
  33. QECSConfig: {
  34. /** 学号登录别名 */
  35. STUDENT_CODE_LOGIN_ALIAS: string;
  36. /** 身份证号登录别名 */
  37. IDENTITY_NUMBER_LOGIN_ALIAS: string;
  38. /** 防作弊配置 !!!需对json进行转换!!! */
  39. PREVENT_CHEATING_CONFIG: Partial<
  40. [
  41. "FULL_SCREEN_TOP",
  42. "DISABLE_REMOTE_ASSISTANCE",
  43. "DISABLE_VIRTUAL_CAMERA",
  44. "DISABLE_MULTISCREEN"
  45. ]
  46. >;
  47. /** 学校是否有定制logo !!!需对json进行转换!!! */
  48. IS_CUSTOM_MENU_LOGO: boolean;
  49. /** 定制logo的URL */
  50. CUS_MENU_LOGO_FILE_URL: string;
  51. /** 定制登录页背景图的URL */
  52. STUDENT_CLIENT_BG_PICTURE_URL: string;
  53. /** 学生端控制台设置 */
  54. STUDENT_CLIENT_CONSOLE_CONFIG: string;
  55. /** 首页背景图 */
  56. LOGO_FILE_URL: string;
  57. /** 产品名称,登录页显示 */
  58. OE_STUDENT_SYS_NAME: string;
  59. /** 登录页可选的登录类型 !!!需对json进行转换!!! */
  60. LOGIN_TYPE: Partial<["STUDENT_CODE", "IDENTITY_NUMBER"]>;
  61. ROOT_ORG_ID: number;
  62. /** @deprecated 登录支持的客户端类型。新版只支持Electron包。 */
  63. LOGIN_SUPPORT: Partial<["NATIVE", "BROWSER"]>;
  64. };
  65. /** 电脑时间管理 */
  66. sysTime: {
  67. /** 与服务器差异,服务器时间大于学生端时间,则返回正数,否则返回负数 */
  68. difference: number;
  69. /** 网络延迟 通过网络请求来判断 */
  70. rtt: number;
  71. /** 上次的Date.now() 有的电脑上存在Date.now()不更新 */
  72. lastTime: number;
  73. /** 判断时钟是否不走动了 */
  74. isTimerFrozen: boolean;
  75. };
  76. /** 网络状况 */
  77. network: {
  78. /** 网络状况是即时的,如何获取? */
  79. /** computed */
  80. isOK: boolean;
  81. /** 百度不通,则网络不通 */
  82. pingBaidu: boolean;
  83. /** 云平台api是否通畅 */
  84. pingQmth: boolean;
  85. /** 阿里云文件存储 阿里云日志 分开? */
  86. pingAli: boolean;
  87. /** websocket */
  88. pingWebSocket: boolean;
  89. };
  90. /** 菜单项 */
  91. menus: Array<{
  92. /** 菜单名称 */
  93. name: string;
  94. /** 菜单指向链接,是path */
  95. link: string;
  96. /** 此处要细化为 type = 'STU_NOTICE' | 'STU_MODIFY_PWD',在router中也要用 */
  97. routeCode: string;
  98. }>;
  99. /** app下载 */
  100. appDownload: {
  101. /** 是否开放下载 */
  102. enabled: boolean;
  103. /** 下载链接 */
  104. url: string;
  105. };
  106. /** 站内消息 */
  107. siteMessage: {
  108. messages: SiteMessage[];
  109. /** 第一个未读消息 popup用 computed */
  110. firstUnreadMessage: SiteMessage | null;
  111. /** 忽略的站内消息,不弹popup */
  112. ignoreMessageIds: number[];
  113. /** 未读消息总数 */
  114. unreadCount: number;
  115. };
  116. siteMessagesTimeStamp: number;
  117. /** 在线考试待考列表 */
  118. examList: OnlineExam[];
  119. /** 在线考试已结束的考试列表 */
  120. endedExamList: OnlineExam[];
  121. /** 在线作业 */
  122. homeworkList: OnlineExam[];
  123. /** 在线练习 */
  124. practiceList: PracticeExam[];
  125. /** 当前开考的exam,非断点续考才会需要发出这么网络请求,正常开考是从前页面带过来的 */
  126. exam: {
  127. /** 考生的考试记录id */
  128. examRecordDataId: number;
  129. /** 考试批次id */
  130. examId: number;
  131. /** 当前试题序号,起始为1,初始为1. 和route里面的order不好算优先级和时机,暂时不用这个字段了 */
  132. // order: number;
  133. /** 考试类型 */
  134. examType: ExamType;
  135. /** 课程名称 */
  136. courseName: string;
  137. /** 是否开启微信作答 */
  138. weixinAnswerEnabled: boolean;
  139. /** 是否开启人脸比对 */
  140. faceCheckEnabled: boolean;
  141. /** 是否开启活体检测 */
  142. faceLivenessEnabled: boolean;
  143. /** 活体检测选项 */
  144. faceLivenessOption: {
  145. /** 进入考试后多久开启活体检测(分钟) */
  146. faceVerifyMinute: number;
  147. /** 活体检测类型 S1: FaceID S2: 自研活体 */
  148. identificationOfLivingBodyScheme: "S1" | "S2";
  149. };
  150. /** 抓拍间隔(秒) */
  151. SNAPSHOT_INTERVAL: number;
  152. /** 考试冻结(秒) */
  153. freezeTime: number;
  154. /** 考试时长(秒) */
  155. duration: number;
  156. /** 考试剩余时间(毫秒ms) */
  157. remainTime: number;
  158. /** 是否开启微信作答方式 */
  159. WEIXIN_ANSWER_ENABLED: boolean;
  160. /** 练习显示答案的类型 IN_PRACTICE:在练习过程中显示答案 NO_ANSWER:练习过程中不显示答案 */
  161. practiceType: "IN_PRACTICE" | "NO_ANSWER";
  162. /** 试卷结构 */
  163. paperStruct: PaperStruct;
  164. /** 试题结构 */
  165. examQuestionList: ExamQuestion[];
  166. /** 试题过滤类型 */
  167. questionFilterType: "ALL" | "ANSWERED" | "SIGNED" | "UNANSWERED";
  168. allAudioPlayTimes: { audioName: string; times: number }[];
  169. questionQrCodeScanned: { order: number };
  170. questionAnswerFileUrl: {
  171. order: number;
  172. fileUrl: string;
  173. transferFileType: string;
  174. }[];
  175. };
  176. // /** 考试中的状态 */
  177. // examing: {};
  178. // /** 获取到的camera source */
  179. camera: {
  180. /** 获取摄像头,且在多页面共享,减少打开摄像头失败的次数 */
  181. stream: MediaStream | null;
  182. };
  183. globalMaskCount: 0;
  184. spinMessage?: string;
  185. };
  186. type SchoolDomain = `${string}.ecs.qmth.com.cn`;
  187. export type SiteMessage = {
  188. id: number;
  189. hasRead: boolean;
  190. hasRecalled: boolean;
  191. title: string;
  192. content: string;
  193. publishTime: string;
  194. };
  195. export type ExamType = "ONLINE" | "ONLINE_HOMEWORK" | "PRACTICE";
  196. type BaseExam = {
  197. /** 考试批次id */
  198. examId: number;
  199. /** 考生id,用户的每场考试都有一个考生id */
  200. examStudentId: number;
  201. /** 考生的考试记录id */
  202. examRecordDataId: number;
  203. /** 考试类型 */
  204. examType: ExamType;
  205. /** 课程名称 */
  206. courseName: string;
  207. /** 课程层次 */
  208. courseLevel: string;
  209. /** 专业名称 */
  210. specialtyName: string;
  211. /** 考试开始时间。日期时间字符串,以前叫后台改为数字,但后台不改,遗留的设计错误。 */
  212. startTime: string;
  213. /** 考试结束时间 */
  214. endTime: string;
  215. };
  216. type ExamCycle = {
  217. /** 考试周期是否开启循环 */
  218. examCycleEnabled: boolean;
  219. /** 周循环中周几开启考试 1-7 */
  220. examCycleWeek: number[];
  221. /** 周期循环中开启的时间段 HHmm [['08:00', '10:30'], ['15:00', '17:00']] */
  222. examCycleTimeRange: { timeRange: [string, string] }[];
  223. };
  224. export type OnlineExam = BaseExam &
  225. ExamCycle & {
  226. /** 课程id */
  227. courseId: number;
  228. /** 是否显示考生承诺书 */
  229. showUndertaking: boolean;
  230. /** 考生承诺书内容 */
  231. undertaking: string;
  232. /** 是否启用人脸比对 */
  233. faceEnable: boolean;
  234. /** 是否启用人脸比对的强制或非强制 */
  235. faceCheck: boolean;
  236. /** 剩余考试次数 */
  237. allowExamCount: number;
  238. /** 是否允许查看客观分 */
  239. isObjScoreView: boolean;
  240. };
  241. export type PracticeExam = ExamCycle & {
  242. /** 考试批次id */
  243. examId: number;
  244. /** 考试批次名称 */
  245. examName: string;
  246. /** 考试类型 */
  247. examType: ExamType;
  248. /** 考生id,用户的每场考试都有一个考生id */
  249. examStudentId: number;
  250. /** 课程id */
  251. courseId: number;
  252. /** 课程名称 */
  253. courseName: string;
  254. /** 课程code */
  255. courseCode: string;
  256. /** 考试开始时间。 */
  257. startTime: string;
  258. /** 考试结束时间 */
  259. endTime: string;
  260. /** 练习次数 */
  261. practiceCount: number;
  262. /** 剩余练习次数 */
  263. allowExamCount: number;
  264. /** 最近正确率(后端已乘100) */
  265. recentObjectiveAccuracy: number;
  266. /** 平均正确率(后端已乘100) */
  267. aveObjectiveAccuracy: number;
  268. /** 最高正确率(后端已乘100) */
  269. maxObjectiveAccuracy: number;
  270. };
  271. export type OfflineExam = BaseExam & {
  272. /** 机构名称 */
  273. orgName: string;
  274. /** 考试周期是否开启循环 */
  275. offlineFiles: Array<{ offlineFileUrl: string; originalFileName: string }>;
  276. /** 试卷id */
  277. paperId: string;
  278. };
  279. export type OnlinePracticeRecord = {
  280. id: number;
  281. /** 考试开始时间。日期时间字符串,以前叫后台改为数字,但后台不改,遗留的设计错误。 */
  282. startTime: string;
  283. /** 考试结束时间 */
  284. endTime: string;
  285. /** 练习时长 类型待确认??? */
  286. usedExamTime: number;
  287. /** 总题量 */
  288. totalQuestionCount: number;
  289. /** 正确的数量 */
  290. succQuestionNum: number;
  291. /** 错误的数量 */
  292. failQuestionNum: number;
  293. /** 未答的数量 */
  294. notAnsweredCount: number;
  295. /** 客观题正确率(后端已乘100) */
  296. objectiveAccuracy: number;
  297. };
  298. export type OnlinePracticeRecordResult = {
  299. courseName: string;
  300. courseCode: string;
  301. /** 客观题正确率(后端已乘100) */
  302. objectiveAccuracy: string;
  303. paperStructInfos: Array<{
  304. index: number;
  305. /** 题目分类 */
  306. title: string;
  307. /** 题量 */
  308. questionCount: number;
  309. /** 正确的数量 */
  310. succQuestionNum: number;
  311. /** 错误的数量 */
  312. failQuestionNum: number;
  313. /** 未答的数量 */
  314. notAnsweredCount: number;
  315. }>;
  316. };
  317. // export type PaperStruct = {
  318. // defaultPaper: {
  319. // /** index = mainNumber - 1 ??待确定 */
  320. // questionGroupList: Array<{
  321. // groupName: string;
  322. // groupScore: number;
  323. // questionWrapperList: Array<{
  324. // questionId: string;
  325. // body: string;
  326. // examQuestionList: ExamQuestion[];
  327. // /** 小题的列表,学生端用不着,只用到它的length */
  328. // questionUnitWrapperList: Array<{ id: string }>;
  329. // questionUnitList: Array<{
  330. // body: string;
  331. // }>;
  332. // }>;
  333. // }>;
  334. // };
  335. // };
  336. export type QuestionUnitItem = {
  337. body: string;
  338. questionOptionList: Array<{ body: string }>;
  339. questionType: string;
  340. rightAnswer: string[];
  341. };
  342. export type QuestionWrapperItem = {
  343. questionId: string;
  344. /** 试题先导内容,一般出现在套题中 */
  345. body: string;
  346. /** 试题小题的信息列表 */
  347. examQuestionList: ExamQuestion[];
  348. /** 试题小题的内容列表,一般应该是用不到的 */
  349. questionUnitList: QuestionUnitItem[];
  350. /** 小题的列表,学生端用不着,只用到它的length */
  351. questionUnitWrapperList: Array<{ id: string }>;
  352. limitedPlayTimes: number;
  353. };
  354. export type PaperStruct = {
  355. defaultPaper: {
  356. /** index = mainNumber - 1 ??待确定 */
  357. questionGroupList: Array<{
  358. groupName: string;
  359. groupScore: number;
  360. /** 试题列表 */
  361. questionWrapperList: QuestionWrapperItem[];
  362. }>;
  363. };
  364. };
  365. export type ExamQuestion = {
  366. /** 试题id */
  367. questionId: string;
  368. /** 题目序号 */
  369. order: number;
  370. /** 题目在答题中的序号。 old groupOrder */
  371. inGroupOrder: number;
  372. /** 限制音频播放次数。从paperStruct[][]['limitedPlayTimes']得到。前端添加。 */
  373. limitedPlayTimes: number;
  374. questionScore: number;
  375. /** 学生填写的答案,和C端、App端结构一致 */
  376. studentAnswer: string;
  377. /** 试题类型 */
  378. questionType:
  379. | "SINGLE_CHOICE"
  380. | "MULTIPLE_CHOICE"
  381. | "TRUE_OR_FALSE"
  382. | "FILL_UP"
  383. | "ESSAY";
  384. /** 小题乱序。细化??? */
  385. optionPermutation: number[];
  386. /** 大题名称 */
  387. groupName: string;
  388. /** 什么的总分??? */
  389. groupTotal: number;
  390. /** 是否被标上星号。 TODO: 改名为 isStarred */
  391. isSign: boolean;
  392. /** 大题号 */
  393. mainNumber: number;
  394. /** 小题号 */
  395. subNumber: number;
  396. /** 试题内容。重点要重构音频、填空题###的处理逻辑 */
  397. body: string;
  398. /** 是否为套题。此处要梳理数据结构,重新计算!!! */
  399. isNestedQuestion: boolean;
  400. /** 文本作答的题目,是否开启音频作答。和考试的weixinAnswerEnabled有关。 */
  401. answerType: "SINGLE_AUDIO" | null;
  402. /** 练习时有正确答案 */
  403. rightAnswer?: string[];
  404. /** 后端给的类型前端好像用不着,待确认??? */
  405. questionOptionList: any;
  406. /** 题目中是否有音频 */
  407. hasAudio: boolean;
  408. /** 试题内容。通过网络获取。 */
  409. questionContent: string;
  410. /** 试题内容是否已通过网络获取到。 TODO: 改名为 gotQuestionContent */
  411. getQuestionContent: boolean;
  412. /** 答案是否已经被用户更新过了 */
  413. dirty: boolean;
  414. /** 只有第一题有此数据,用来像服务器保存音频播放次数 */
  415. audioPlayTimes: { audioName: string; times: number }[];
  416. };
  417. export type ExamInProgress = {
  418. /** "S-101000" 请重试 */
  419. code: "000000" | "S-101000";
  420. /** 是否超过断点次数限制 */
  421. isExceed: boolean;
  422. /** 允许的最大断点次数 */
  423. maxInterruptNum: number;
  424. /** 允许的最大切屏次数 */
  425. maxSwitchScreenCount: number;
  426. examId: number;
  427. examRecordDataId: number;
  428. };
  429. // export const REMOTE_APPS = [
  430. // ["qq", "QQ"],
  431. // ["teamviewer", "TeamViewer"],
  432. // ["lookmypc", "LookMyPC"],
  433. // ["xt", "协通"],
  434. // ["winaw32", "Symantec PCAnywhere"],
  435. // ["pcaquickconnect", "Symantec PCAnywhere"],
  436. // ["sessioncontroller", "Symantec PCAnywhere"],
  437. // [/sunloginclient/gi, "向日葵"],
  438. // [/sunloginremote/gi, "向日葵"],
  439. // [/选择免安装运行,截图识别/gi, "向日葵"],
  440. // ["wemeetapp", "腾讯会议"],
  441. // ["wechat", "微信"],
  442. // ] as const;
  443. // define process.env
  444. // clientVersion.ts 管理可用客户端版本。
  445. // api: getCurrentClientVersion isSupportedClientVersion
  446. /** 得到当前客户端版本 1.9.* */
  447. type GetCurrentClientVersion = () => string;
  448. type IsSupportedClientVersion = () => boolean;
  449. // updateManager.ts 应用(非客户端)是否应该更新了
  450. // api: isNewWebAppAvailable isNewBackendAvailable getNewBackendDesc
  451. // native.ts 管理原生及系统命令,详细说明path和相对path
  452. // api: isElectron execCmd readFile getRemoteApps getVirtualCams
  453. // runtimeMonitor.ts 检测当前运行的环境是否合法
  454. // 检测是否打开了控制台;检测是否使用了高版本的chrome(feature检测)
  455. // 检测onResize;截屏
  456. // cache design
  457. // service worker ; image cache; audio cache/prefetch
  458. // router design 严格受控的页面跳转,既考虑刷新,也考虑缓存
  459. // localStorage design
  460. // loginType accountValue
  461. // sessionStorage design
  462. // store 防破解?仅保存 user, QECSConfig 信息
  463. // 开考流程
  464. // 1. 确保每一次请求的结果都得到充分利用,即在页面中保存好信息,不做多余的重试,不在中途刷新页面,除敏感信息外要从上个页面带到下个页面
  465. // 2. 开考和考试中的信息获取要隔离,方便从不同的页面中进入考试
  466. // 3. 进入页面后,不轻易退出,要做足够的重试。退出的条件要设置充分,比如网络断了。