123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- export type Store = {
- /** 当前用户 */
- user: {
- id: number;
- /** 身份证号 */
- identityNumber: string;
- /** 学号 */
- studentCodeList: string[];
- /** 显示的姓名 */
- displayName: string;
- /** @deprecated 姓名,但可能为空 */
- name: string;
- /** 学生手机号 */
- phoneNumber: string;
- /** 属于那个学校 */
- schoolDomain: SchoolDomain;
- /** 顶级机构id */
- rootOrgId: number;
- /** 底照URL */
- photoPath: string;
- /** 机构名称 */
- orgName: string;
- /** 顶级机构名称 */
- rootOrgName: string;
- /** 用户的另一个id */
- key: string;
- /** 登录认证信息 */
- token: string | null;
- /** 专业 */
- specialty: string;
- };
- /** 学生端配置。 Q代表qmth */
- QECSConfig: {
- /** 学号登录别名 */
- STUDENT_CODE_LOGIN_ALIAS: string;
- /** 身份证号登录别名 */
- IDENTITY_NUMBER_LOGIN_ALIAS: string;
- /** 防作弊配置 !!!需对json进行转换!!! */
- PREVENT_CHEATING_CONFIG: Partial<
- [
- "FULL_SCREEN_TOP",
- "DISABLE_REMOTE_ASSISTANCE",
- "DISABLE_VIRTUAL_CAMERA",
- "DISABLE_MULTISCREEN"
- ]
- >;
- /** 学校是否有定制logo !!!需对json进行转换!!! */
- IS_CUSTOM_MENU_LOGO: boolean;
- /** 定制logo的URL */
- CUS_MENU_LOGO_FILE_URL: string;
- /** 定制登录页背景图的URL */
- STUDENT_CLIENT_BG_PICTURE_URL: string;
- /** 学生端控制台设置 */
- STUDENT_CLIENT_CONSOLE_CONFIG: string;
- /** 首页背景图 */
- LOGO_FILE_URL: string;
- /** 产品名称,登录页显示 */
- OE_STUDENT_SYS_NAME: string;
- /** 登录页可选的登录类型 !!!需对json进行转换!!! */
- LOGIN_TYPE: Partial<["STUDENT_CODE", "IDENTITY_NUMBER"]>;
- ROOT_ORG_ID: number;
- /** @deprecated 登录支持的客户端类型。新版只支持Electron包。 */
- LOGIN_SUPPORT: Partial<["NATIVE", "BROWSER"]>;
- };
- /** 电脑时间管理 */
- sysTime: {
- /** 与服务器差异,服务器时间大于学生端时间,则返回正数,否则返回负数 */
- difference: number;
- /** 网络延迟 通过网络请求来判断 */
- rtt: number;
- /** 上次的Date.now() 有的电脑上存在Date.now()不更新 */
- lastTime: number;
- /** 判断时钟是否不走动了 */
- isTimerFrozen: boolean;
- };
- /** 网络状况 */
- network: {
- /** 网络状况是即时的,如何获取? */
- /** computed */
- isOK: boolean;
- /** 百度不通,则网络不通 */
- pingBaidu: boolean;
- /** 云平台api是否通畅 */
- pingQmth: boolean;
- /** 阿里云文件存储 阿里云日志 分开? */
- pingAli: boolean;
- /** websocket */
- pingWebSocket: boolean;
- };
- /** 菜单项 */
- menus: Array<{
- /** 菜单名称 */
- name: string;
- /** 菜单指向链接,是path */
- link: string;
- /** 此处要细化为 type = 'STU_NOTICE' | 'STU_MODIFY_PWD',在router中也要用 */
- routeCode: string;
- }>;
- /** app下载 */
- appDownload: {
- /** 是否开放下载 */
- enabled: boolean;
- /** 下载链接 */
- url: string;
- };
- /** 站内消息 */
- siteMessage: {
- messages: SiteMessage[];
- /** 第一个未读消息 popup用 computed */
- firstUnreadMessage: SiteMessage | null;
- /** 忽略的站内消息,不弹popup */
- ignoreMessageIds: number[];
- /** 未读消息总数 */
- unreadCount: number;
- };
- siteMessagesTimeStamp: number;
- /** 在线考试待考列表 */
- examList: OnlineExam[];
- /** 在线考试已结束的考试列表 */
- endedExamList: OnlineExam[];
- /** 在线作业 */
- homeworkList: OnlineExam[];
- /** 在线练习 */
- practiceList: PracticeExam[];
- /** 当前开考的exam,非断点续考才会需要发出这么网络请求,正常开考是从前页面带过来的 */
- exam: {
- /** 考生的考试记录id */
- examRecordDataId: number;
- /** 考试批次id */
- examId: number;
- /** 当前试题序号,起始为1,初始为1. 和route里面的order不好算优先级和时机,暂时不用这个字段了 */
- // order: number;
- /** 考试类型 */
- examType: ExamType;
- /** 课程名称 */
- courseName: string;
- /** 是否开启微信作答 */
- weixinAnswerEnabled: boolean;
- /** 是否开启人脸比对 */
- faceCheckEnabled: boolean;
- /** 是否开启活体检测 */
- faceLivenessEnabled: boolean;
- /** 活体检测选项 */
- faceLivenessOption: {
- /** 进入考试后多久开启活体检测(分钟) */
- faceVerifyMinute: number;
- /** 活体检测类型 S1: FaceID S2: 自研活体 */
- identificationOfLivingBodyScheme: "S1" | "S2";
- };
- /** 抓拍间隔(秒) */
- SNAPSHOT_INTERVAL: number;
- /** 考试冻结(秒) */
- freezeTime: number;
- /** 考试时长(秒) */
- duration: number;
- /** 考试剩余时间(毫秒ms) */
- remainTime: number;
- /** 是否开启微信作答方式 */
- WEIXIN_ANSWER_ENABLED: boolean;
- /** 练习显示答案的类型 IN_PRACTICE:在练习过程中显示答案 NO_ANSWER:练习过程中不显示答案 */
- practiceType: "IN_PRACTICE" | "NO_ANSWER";
- /** 试卷结构 */
- paperStruct: PaperStruct;
- /** 试题结构 */
- examQuestionList: ExamQuestion[];
- /** 试题过滤类型 */
- questionFilterType: "ALL" | "ANSWERED" | "SIGNED" | "UNANSWERED";
- allAudioPlayTimes: { audioName: string; times: number }[];
- questionQrCodeScanned: { order: number };
- questionAnswerFileUrl: {
- order: number;
- fileUrl: string;
- transferFileType: string;
- }[];
- };
- // /** 考试中的状态 */
- // examing: {};
- // /** 获取到的camera source */
- camera: {
- /** 获取摄像头,且在多页面共享,减少打开摄像头失败的次数 */
- stream: MediaStream | null;
- };
- globalMaskCount: 0;
- spinMessage?: string;
- };
- type SchoolDomain = `${string}.ecs.qmth.com.cn`;
- export type SiteMessage = {
- id: number;
- hasRead: boolean;
- hasRecalled: boolean;
- title: string;
- content: string;
- publishTime: string;
- };
- export type ExamType = "ONLINE" | "ONLINE_HOMEWORK" | "PRACTICE";
- type BaseExam = {
- /** 考试批次id */
- examId: number;
- /** 考生id,用户的每场考试都有一个考生id */
- examStudentId: number;
- /** 考生的考试记录id */
- examRecordDataId: number;
- /** 考试类型 */
- examType: ExamType;
- /** 课程名称 */
- courseName: string;
- /** 课程层次 */
- courseLevel: string;
- /** 专业名称 */
- specialtyName: string;
- /** 考试开始时间。日期时间字符串,以前叫后台改为数字,但后台不改,遗留的设计错误。 */
- startTime: string;
- /** 考试结束时间 */
- endTime: string;
- };
- type ExamCycle = {
- /** 考试周期是否开启循环 */
- examCycleEnabled: boolean;
- /** 周循环中周几开启考试 1-7 */
- examCycleWeek: number[];
- /** 周期循环中开启的时间段 HHmm [['08:00', '10:30'], ['15:00', '17:00']] */
- examCycleTimeRange: { timeRange: [string, string] }[];
- };
- export type OnlineExam = BaseExam &
- ExamCycle & {
- /** 课程id */
- courseId: number;
- /** 是否显示考生承诺书 */
- showUndertaking: boolean;
- /** 考生承诺书内容 */
- undertaking: string;
- /** 是否启用人脸比对 */
- faceEnable: boolean;
- /** 是否启用人脸比对的强制或非强制 */
- faceCheck: boolean;
- /** 剩余考试次数 */
- allowExamCount: number;
- /** 是否允许查看客观分 */
- isObjScoreView: boolean;
- };
- export type PracticeExam = ExamCycle & {
- /** 考试批次id */
- examId: number;
- /** 考试批次名称 */
- examName: string;
- /** 考试类型 */
- examType: ExamType;
- /** 考生id,用户的每场考试都有一个考生id */
- examStudentId: number;
- /** 课程id */
- courseId: number;
- /** 课程名称 */
- courseName: string;
- /** 课程code */
- courseCode: string;
- /** 考试开始时间。 */
- startTime: string;
- /** 考试结束时间 */
- endTime: string;
- /** 练习次数 */
- practiceCount: number;
- /** 剩余练习次数 */
- allowExamCount: number;
- /** 最近正确率(后端已乘100) */
- recentObjectiveAccuracy: number;
- /** 平均正确率(后端已乘100) */
- aveObjectiveAccuracy: number;
- /** 最高正确率(后端已乘100) */
- maxObjectiveAccuracy: number;
- };
- export type OfflineExam = BaseExam & {
- /** 机构名称 */
- orgName: string;
- /** 考试周期是否开启循环 */
- offlineFiles: Array<{ offlineFileUrl: string; originalFileName: string }>;
- /** 试卷id */
- paperId: string;
- };
- export type OnlinePracticeRecord = {
- id: number;
- /** 考试开始时间。日期时间字符串,以前叫后台改为数字,但后台不改,遗留的设计错误。 */
- startTime: string;
- /** 考试结束时间 */
- endTime: string;
- /** 练习时长 类型待确认??? */
- usedExamTime: number;
- /** 总题量 */
- totalQuestionCount: number;
- /** 正确的数量 */
- succQuestionNum: number;
- /** 错误的数量 */
- failQuestionNum: number;
- /** 未答的数量 */
- notAnsweredCount: number;
- /** 客观题正确率(后端已乘100) */
- objectiveAccuracy: number;
- };
- export type OnlinePracticeRecordResult = {
- courseName: string;
- courseCode: string;
- /** 客观题正确率(后端已乘100) */
- objectiveAccuracy: string;
- paperStructInfos: Array<{
- index: number;
- /** 题目分类 */
- title: string;
- /** 题量 */
- questionCount: number;
- /** 正确的数量 */
- succQuestionNum: number;
- /** 错误的数量 */
- failQuestionNum: number;
- /** 未答的数量 */
- notAnsweredCount: number;
- }>;
- };
- // export type PaperStruct = {
- // defaultPaper: {
- // /** index = mainNumber - 1 ??待确定 */
- // questionGroupList: Array<{
- // groupName: string;
- // groupScore: number;
- // questionWrapperList: Array<{
- // questionId: string;
- // body: string;
- // examQuestionList: ExamQuestion[];
- // /** 小题的列表,学生端用不着,只用到它的length */
- // questionUnitWrapperList: Array<{ id: string }>;
- // questionUnitList: Array<{
- // body: string;
- // }>;
- // }>;
- // }>;
- // };
- // };
- export type QuestionUnitItem = {
- body: string;
- questionOptionList: Array<{ body: string }>;
- questionType: string;
- rightAnswer: string[];
- };
- export type QuestionWrapperItem = {
- questionId: string;
- /** 试题先导内容,一般出现在套题中 */
- body: string;
- /** 试题小题的信息列表 */
- examQuestionList: ExamQuestion[];
- /** 试题小题的内容列表,一般应该是用不到的 */
- questionUnitList: QuestionUnitItem[];
- /** 小题的列表,学生端用不着,只用到它的length */
- questionUnitWrapperList: Array<{ id: string }>;
- limitedPlayTimes: number;
- };
- export type PaperStruct = {
- defaultPaper: {
- /** index = mainNumber - 1 ??待确定 */
- questionGroupList: Array<{
- groupName: string;
- groupScore: number;
- /** 试题列表 */
- questionWrapperList: QuestionWrapperItem[];
- }>;
- };
- };
- export type ExamQuestion = {
- /** 试题id */
- questionId: string;
- /** 题目序号 */
- order: number;
- /** 题目在答题中的序号。 old groupOrder */
- inGroupOrder: number;
- /** 限制音频播放次数。从paperStruct[][]['limitedPlayTimes']得到。前端添加。 */
- limitedPlayTimes: number;
- questionScore: number;
- /** 学生填写的答案,和C端、App端结构一致 */
- studentAnswer: string;
- /** 试题类型 */
- questionType:
- | "SINGLE_CHOICE"
- | "MULTIPLE_CHOICE"
- | "TRUE_OR_FALSE"
- | "FILL_UP"
- | "ESSAY";
- /** 小题乱序。细化??? */
- optionPermutation: number[];
- /** 大题名称 */
- groupName: string;
- /** 什么的总分??? */
- groupTotal: number;
- /** 是否被标上星号。 TODO: 改名为 isStarred */
- isSign: boolean;
- /** 大题号 */
- mainNumber: number;
- /** 小题号 */
- subNumber: number;
- /** 试题内容。重点要重构音频、填空题###的处理逻辑 */
- body: string;
- /** 是否为套题。此处要梳理数据结构,重新计算!!! */
- isNestedQuestion: boolean;
- /** 文本作答的题目,是否开启音频作答。和考试的weixinAnswerEnabled有关。 */
- answerType: "SINGLE_AUDIO" | null;
- /** 练习时有正确答案 */
- rightAnswer?: string[];
- /** 后端给的类型前端好像用不着,待确认??? */
- questionOptionList: any;
- /** 题目中是否有音频 */
- hasAudio: boolean;
- /** 试题内容。通过网络获取。 */
- questionContent: string;
- /** 试题内容是否已通过网络获取到。 TODO: 改名为 gotQuestionContent */
- getQuestionContent: boolean;
- /** 答案是否已经被用户更新过了 */
- dirty: boolean;
- /** 只有第一题有此数据,用来像服务器保存音频播放次数 */
- audioPlayTimes: { audioName: string; times: number }[];
- };
- export type ExamInProgress = {
- /** "S-101000" 请重试 */
- code: "000000" | "S-101000";
- /** 是否超过断点次数限制 */
- isExceed: boolean;
- /** 允许的最大断点次数 */
- maxInterruptNum: number;
- /** 允许的最大切屏次数 */
- maxSwitchScreenCount: number;
- examId: number;
- examRecordDataId: number;
- };
- // export const REMOTE_APPS = [
- // ["qq", "QQ"],
- // ["teamviewer", "TeamViewer"],
- // ["lookmypc", "LookMyPC"],
- // ["xt", "协通"],
- // ["winaw32", "Symantec PCAnywhere"],
- // ["pcaquickconnect", "Symantec PCAnywhere"],
- // ["sessioncontroller", "Symantec PCAnywhere"],
- // [/sunloginclient/gi, "向日葵"],
- // [/sunloginremote/gi, "向日葵"],
- // [/选择免安装运行,截图识别/gi, "向日葵"],
- // ["wemeetapp", "腾讯会议"],
- // ["wechat", "微信"],
- // ] as const;
- // define process.env
- // clientVersion.ts 管理可用客户端版本。
- // api: getCurrentClientVersion isSupportedClientVersion
- /** 得到当前客户端版本 1.9.* */
- type GetCurrentClientVersion = () => string;
- type IsSupportedClientVersion = () => boolean;
- // updateManager.ts 应用(非客户端)是否应该更新了
- // api: isNewWebAppAvailable isNewBackendAvailable getNewBackendDesc
- // native.ts 管理原生及系统命令,详细说明path和相对path
- // api: isElectron execCmd readFile getRemoteApps getVirtualCams
- // runtimeMonitor.ts 检测当前运行的环境是否合法
- // 检测是否打开了控制台;检测是否使用了高版本的chrome(feature检测)
- // 检测onResize;截屏
- // cache design
- // service worker ; image cache; audio cache/prefetch
- // router design 严格受控的页面跳转,既考虑刷新,也考虑缓存
- // localStorage design
- // loginType accountValue
- // sessionStorage design
- // store 防破解?仅保存 user, QECSConfig 信息
- // 开考流程
- // 1. 确保每一次请求的结果都得到充分利用,即在页面中保存好信息,不做多余的重试,不在中途刷新页面,除敏感信息外要从上个页面带到下个页面
- // 2. 开考和考试中的信息获取要隔离,方便从不同的页面中进入考试
- // 3. 进入页面后,不轻易退出,要做足够的重试。退出的条件要设置充分,比如网络断了。
|