From fea9c97209e001538c89a8c784c44803faffc6b3 Mon Sep 17 00:00:00 2001
From: popWheat <49830650+j10ccc@users.noreply.github.com>
Date: Wed, 13 Nov 2024 18:41:39 +0800
Subject: [PATCH] refactor(exam): rewrite in hooks (#158)
---
src/components/ExamQuickView/index.vue | 71 ++++++++-----------------
src/pages/exam/index.vue | 57 ++++++++++----------
src/services/services/zfService.ts | 2 +-
src/store/service/exam/collections.ts | 72 ++++++++++++++++++++++++++
src/store/service/exam/queryOptions.ts | 26 ++++++++++
src/utils/promise.ts | 23 ++++++++
6 files changed, 171 insertions(+), 80 deletions(-)
create mode 100644 src/store/service/exam/collections.ts
create mode 100644 src/store/service/exam/queryOptions.ts
create mode 100644 src/utils/promise.ts
diff --git a/src/components/ExamQuickView/index.vue b/src/components/ExamQuickView/index.vue
index d5c483ea..10ffdc86 100644
--- a/src/components/ExamQuickView/index.vue
+++ b/src/components/ExamQuickView/index.vue
@@ -11,13 +11,13 @@
近期考试 ({{ updateTimeString }})
未查询到近日考试信息
{
- if (updateTime.value !== undefined) return dayjs(updateTime.value).fromNow();
- else return "更新失败!";
-});
+const examStore = useExamStore();
/**
- * 筛选近期考试
- *
- * 未来3日
+ * 筛选出未来 3 日的考试
*/
-const filteredExamItems = computed(() => {
- let list: Exam[] = [];
- const exam = ZFService.getExamInfo(selectTerm.value)?.data;
- try {
- list = exam.filter(item => {
- if (item.examTime === "未放开不可查") return 0;
- const { date, start } = getExamTime(item.examTime);
- // 距离考试的剩余时间(ms),为正表示考试为开始,为负表示考试结束
- const resDay = timeUtils.getDayInterval(
- new Date(date + " " + start + ":00")
- );
- return (resDay <= 3 && resDay >= 0 && examState(item.examTime) !== "after");
- });
- } catch (e) {
- console.error(e);
- }
- return list.sort((a, b) => {
+const filteredExams = computed(() => {
+ const examsInTerm = examStore.queryByTermSync()?.exams || [];
+ const filtered = examsInTerm.filter(item => {
+ if (item.examTime === "未放开不可查") return 0;
+ const { date, start } = getExamTime(item.examTime);
+ // 距离考试的剩余时间(ms),为正表示考试为开始,为负表示考试结束
+ const resDay = timeUtils.getDayInterval(
+ new Date(date + " " + start + ":00")
+ );
+ return (resDay <= 3 && resDay >= 0 && examState(item.examTime) !== "after");
+ });
+ return filtered.sort((a, b) => {
const { date: dateA, start: timeA } = getExamTime(a.examTime);
const { date: dateB, start: timeB } = getExamTime(b.examTime);
return dayjs(`${dateA}-${timeA}`) < dayjs(`${dateB}-${timeB}`) ? 1 : -1;
});
});
-const updateTime = computed(() => {
- let updata: Date | null = null;
- try {
- updata = ZFService.getExamInfo(selectTerm.value)?.updateTime;
- if (updata === null) return undefined;
- else return updata;
- } catch {
- return undefined;
- }
+const updateTimeString = computed(() => {
+ const updateTime = examStore.queryByTermSync()?.updateTime;
+ if (examStore.error || !updateTime) return "更新失败";
+ return dayjs(updateTime).fromNow();
});
function nav2Exam() {
@@ -169,8 +146,4 @@ function examState(examTimeString: string) {
else return "after";
}
-onMounted(() => {
- ZFService.updateExamInfo(selectTerm.value);
-});
-
diff --git a/src/pages/exam/index.vue b/src/pages/exam/index.vue
index 7a65bd48..fa476660 100644
--- a/src/pages/exam/index.vue
+++ b/src/pages/exam/index.vue
@@ -14,11 +14,11 @@
-
+
无记录
-
+
@@ -118,8 +118,7 @@
diff --git a/src/services/services/zfService.ts b/src/services/services/zfService.ts
index d9e41e5e..0b436cf3 100644
--- a/src/services/services/zfService.ts
+++ b/src/services/services/zfService.ts
@@ -6,7 +6,7 @@ import { request } from "@/utils";
import { Room } from "@/types/Room";
export default class ZFService {
- static getExamInfo(params: { year: string; term: string }) {
+ static getExamInfo(params: { year: string; term: "上" | "下" | "短" }) {
return request(
api.zf.examInfo, {
method: "POST",
diff --git a/src/store/service/exam/collections.ts b/src/store/service/exam/collections.ts
new file mode 100644
index 00000000..d27d3004
--- /dev/null
+++ b/src/store/service/exam/collections.ts
@@ -0,0 +1,72 @@
+import { useRequestNext } from "@/hooks";
+import { ZFService } from "@/services";
+import useGeneralInfoStore from "@/store/system/generalInfo";
+import { Exam } from "@/types/Exam";
+import { persistedStorage, RequestError } from "@/utils";
+import { withRespDataNeverNull } from "@/utils/promise";
+import Taro from "@tarojs/taro";
+import { defineStore, storeToRefs } from "pinia";
+import { ref } from "vue";
+
+type TermExamCollection = {
+ exams: Exam[],
+ year: string;
+ term: "上" | "下" | "短";
+ updateTime: string;
+};
+
+const useExamStore = defineStore("exam/collections", () => {
+ const { info: generalInfo } = storeToRefs(useGeneralInfoStore());
+ const collections = ref([]);
+
+ const { error, loading, run: fetchExam } = useRequestNext(
+ withRespDataNeverNull(ZFService.getExamInfo), {
+ defaultParams: {
+ year: generalInfo.value.termYear,
+ term: generalInfo.value.term
+ },
+ initialData: [],
+ onSuccess: (exams, params) => {
+ const { year, term } = params!;
+ const existedIndex = collections.value.findIndex(_ => _.year === year && _.term === term);
+
+ if (existedIndex !== -1) {
+ collections.value[existedIndex] = {
+ ...collections.value[existedIndex],
+ exams,
+ updateTime: Date().toString()
+ };
+ } else {
+ collections.value.push({ exams, year, term, updateTime: Date().toString() });
+ }
+ },
+ onError: (e) => {
+ if (e instanceof RequestError) {
+ Taro.showToast({ title: `更新成绩失败: ${e.message}`, icon: "none" });
+ }
+ }
+ }
+ );
+
+ function queryByTermSync(options?: { year: string, term: "上" | "下" | "短" }) {
+ const { year, term } = options ?? { term: generalInfo.value.term, year: generalInfo.value.termYear };
+ const existed = collections.value.find(_ => _.year === year && _.term === term);
+
+ return existed;
+ }
+
+ return {
+ loading,
+ collections,
+ fetchExam,
+ error,
+ queryByTermSync
+ };
+}, {
+ persist: {
+ storage: persistedStorage,
+ pick: ["collections"]
+ }
+});
+
+export default useExamStore;
diff --git a/src/store/service/exam/queryOptions.ts b/src/store/service/exam/queryOptions.ts
new file mode 100644
index 00000000..a2751814
--- /dev/null
+++ b/src/store/service/exam/queryOptions.ts
@@ -0,0 +1,26 @@
+import useGeneralInfoStore from "@/store/system/generalInfo";
+import { defineStore, storeToRefs } from "pinia";
+import { ref } from "vue";
+
+const useExamQueryOptionsStore = defineStore("exam/queryOptions", () => {
+ const { info: generalInfo } = storeToRefs(useGeneralInfoStore());
+
+ const term = ref<"上" | "下" | "短">(generalInfo.value.scoreTerm);
+ const year = ref(generalInfo.value.scoreYear);
+
+ function setOption(value: {
+ term: "上" | "下" | "短";
+ year: string;
+ }) {
+ term.value = value.term;
+ year.value = value.year;
+ }
+
+ return {
+ term,
+ year,
+ setOption
+ };
+});
+
+export default useExamQueryOptionsStore;
diff --git a/src/utils/promise.ts b/src/utils/promise.ts
new file mode 100644
index 00000000..10bdd902
--- /dev/null
+++ b/src/utils/promise.ts
@@ -0,0 +1,23 @@
+import RequestError, { MPErrorCode } from "./request/requestError";
+
+/**
+ * 当 promise 返回的结果为 null 时抛出异常
+ *
+ * 正方的每个接口都有较大的概率返回 data: null
+ */
+export function withRespDataNeverNull(
+ fetcher: (...args: Args) => Promise,
+ options?: {
+ errMsg?: string
+ }
+) {
+ const { errMsg = "响应数据格式异常" } = options ?? {};
+
+ return async (...args: Args) => {
+ const resp = await fetcher(...args);
+ if (resp === null)
+ throw new RequestError(errMsg, MPErrorCode.MP_INVALID_DATA_VALUE);
+
+ return resp as T;
+ };
+}