zhangjie 3 жил өмнө
parent
commit
aa4c1849f6

+ 6 - 1
src/App.vue

@@ -3,6 +3,7 @@ import { NConfigProvider, NMessageProvider, useMessage } from "naive-ui";
 import { zhCN, dateZhCN } from "naive-ui";
 import { defineComponent, watchEffect } from "vue";
 import { useStore, setStore, store } from "./store/store";
+import themeOverrides from "./themeOverrides";
 
 setStore(useStore());
 
@@ -26,7 +27,11 @@ watchEffect(() => {
 
 <template>
   <n-message-provider :duration="10000">
-    <n-config-provider :locale="zhCN" :dateLocale="dateZhCN">
+    <n-config-provider
+      :locale="zhCN"
+      :dateLocale="dateZhCN"
+      :themeOverrides="themeOverrides"
+    >
       <router-view />
       <DummyComp />
     </n-config-provider>

+ 22 - 0
src/api/siteMessage.ts

@@ -0,0 +1,22 @@
+import { httpApp } from "@/plugins/axiosApp";
+import { SiteMessage } from "@/types/student-client";
+
+/** 获取消息列表 */
+export async function messageListApi(
+  userId: number,
+  siteMessagesTimeStamp: number
+) {
+  return httpApp.get<any, { data: SiteMessage[] }>(
+    `/api/ecs_exam_work/notice/getUserNoticeList?${userId}${siteMessagesTimeStamp}`
+  );
+}
+/** 更新消息状态为已读 */
+export async function updateNotieReadStatusApi(noticeId: string) {
+  return httpApp.post<any>(
+    "/api/ecs_exam_work/notice/updateNoticeReadStatus",
+    null,
+    {
+      params: { noticeId },
+    }
+  );
+}

+ 15 - 0
src/assets/back.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="14px" height="12px" viewBox="0 0 14 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
+    <title>返回</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="9-管理端-公告详情" transform="translate(-1327.000000, -120.000000)">
+            <g id="返回" transform="translate(1327.000000, 120.000000)">
+                <polyline id="Path-2" stroke="#CCCCCC" stroke-width="2" transform="translate(4.500000, 6.000000) scale(-1, 1) translate(-4.500000, -6.000000) " points="2 1 7 6 2 11"></polyline>
+                <rect id="Rectangle-8" fill="#CCCCCC" x="2" y="5" width="12" height="2"></rect>
+            </g>
+        </g>
+    </g>
+</svg>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 9 - 0
src/assets/sms-read.svg


+ 14 - 0
src/assets/sms-unread.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="12px" viewBox="0 0 16 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
+    <title>sms-未读</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="8-考生端-公告列表" transform="translate(-260.000000, -235.000000)" fill="#FDAD08">
+            <g id="sms-未读" transform="translate(260.000000, 235.000000)">
+                <path d="M10.2612161,6.01241042 L15.9078877,0.460631922 C15.7791453,0.191589577 15.5045861,0.00576547231 15.1865006,0.00576547231 L0.799296703,0.00576547231 C0.481230769,0.00576547231 0.206652015,0.191589577 0.07798779,0.460631922 L5.7245812,6.01241042 C6.97732845,6.80249511 9.00846886,6.80249511 10.2612161,6.01241042 Z M0,1.563557 L0,10.4731661 L4.69352869,6.00699674 L0,1.563557 Z M11.2922686,6.00699674 L15.9857973,10.4731661 L15.9857973,1.563557 L11.2922686,6.00699674 Z M10.5570891,6.70301629 C9.14094261,7.60211726 6.8448547,7.60211726 5.42872772,6.70301629 L5.21726984,6.50284691 L0.0903540904,11.569544 C0.223746032,11.8252964 0.490998779,11.9999609 0.799296703,11.9999609 L15.1865006,11.9999609 C15.4948181,11.9999609 15.7621294,11.8252964 15.8954432,11.569544 L10.768547,6.50284691 L10.5570891,6.70301629 Z" id="Fill-1"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 20 - 29
src/components/MainLayout/MainLayout.vue

@@ -52,7 +52,7 @@ const menuRouterDict: Record<string, string> = {
   STU_ONLINE_HOMEWORK: "/online-homework",
   STU_ONLINE_PRACTICE: "/online-practice",
   STU_OFFLINE_EXAM: "/offline-exam",
-  STU_NOTICE: "/site-message",
+  STU_NOTICE: "SiteMessage",
   STU_MODIFY_PWD: "ChangePassword",
 };
 let curMenu = $ref("");
@@ -98,7 +98,7 @@ const toModifyPwd = () => {
 };
 const toLogout = () => {
   logger({
-    cnl: ["console"],
+    cnl: ["console", "server"],
     pgn: curMenuOption?.name,
     act: "点击",
     stk: "退出登录",
@@ -108,7 +108,6 @@ const toLogout = () => {
 onMounted(async () => {
   void initData();
   await getMenus();
-  console.log(route.name);
 
   if (menuOptions.length && route.name === "MainLayout") {
     void router.push({ name: menuOptions.slice(-1)[0].routeName });
@@ -119,11 +118,9 @@ onMounted(async () => {
 });
 
 watch(
-  () => route,
+  () => route.name,
   (val) => {
-    console.log(val);
-
-    if (val.name === "MainLayout") return;
+    if (val === "MainLayout") return;
     routerChange();
   }
 );
@@ -337,8 +334,6 @@ watch(
   z-index: 100;
   overflow: auto;
   background-color: var(--app-color-header-bg);
-  border-top-right-radius: var(--app-border-radius-large);
-  border-bottom-right-radius: var(--app-border-radius-large);
 }
 .home-logo {
   width: 80px;
@@ -356,37 +351,33 @@ watch(
   color: var(--app-color-white);
 }
 .home-menu-item {
-  margin: 5px 0;
-  padding: 0 10px;
+  margin: 0;
 }
 .home-menu-content {
-  padding: 10px;
-  border-radius: var(--app-border-radius);
+  padding: 10px 20px 10px 45px;
   line-height: 20px;
   cursor: pointer;
   position: relative;
 }
-.home-menu-content:hover {
-  color: var(--app-color-primary-light);
-}
-.home-menu-content.is-active {
-  background-color: var(--app-color-primary);
-  color: var(--app-color-white);
-}
-
 .home-menu-content::before {
   content: "";
-  display: inline-block;
-  vertical-align: middle;
-  width: 10px;
-  height: 10px;
+  display: block;
+  position: absolute;
+  width: 8px;
+  height: 8px;
+  top: 17px;
+  left: 30px;
   border-radius: 50%;
   border: 2px solid var(--app-color-white);
-  margin-right: 10px;
+  opacity: 0.5;
 }
-.home-menu-content > span {
-  display: inline-block;
-  vertical-align: middle;
+.home-menu-content:hover,
+.home-menu-content.is-active {
+  background-color: rgba(255, 255, 255, 0.5);
+}
+.home-menu-content:hover::before,
+.home-menu-content.is-active::before {
+  opacity: 1;
 }
 
 /* home-body */

+ 44 - 45
src/features/ChangePassword/ChangePassword.vue

@@ -56,54 +56,53 @@ const toSubmit = async () => {
 </script>
 
 <template>
-  <div class="pwd-box">
-    <n-form
-      ref="modelFormRef"
-      :model="formModel"
-      :rules="formRules"
-      :showLabel="false"
-      size="medium"
-    >
-      <n-form-item label="旧秘密" path="password">
-        <n-input
-          v-model:value="formModel.password"
-          type="password"
-          placeholder="请输入旧秘密"
-          clearable
-          @change="inputChange"
-        />
-      </n-form-item>
-      <n-form-item label="新密码" path="newPassword">
-        <n-input
-          v-model:value="formModel.newPassword"
-          type="password"
-          placeholder="请输入新密码(6到18位的数字或字母)"
-          clearable
-          @change="inputChange"
-        />
-      </n-form-item>
-      <n-form-item label="新密码" path="renewPassword">
-        <n-input
-          v-model:value="formModel.renewPassword"
-          type="password"
-          placeholder="请再次输入新密码"
-          clearable
-          @change="inputChange"
-        />
-      </n-form-item>
-      <n-form-item>
-        <n-button type="primary" @click="toSubmit"> 保存 </n-button>
-      </n-form-item>
-    </n-form>
+  <div class="part-box pwd-box">
+    <div class="pwd-box-content">
+      <n-form
+        ref="modelFormRef"
+        :model="formModel"
+        :rules="formRules"
+        :showLabel="false"
+        size="medium"
+      >
+        <n-form-item label="旧秘密" path="password">
+          <n-input
+            v-model:value="formModel.password"
+            type="password"
+            placeholder="请输入旧秘密"
+            clearable
+            @change="inputChange"
+          />
+        </n-form-item>
+        <n-form-item label="新密码" path="newPassword">
+          <n-input
+            v-model:value="formModel.newPassword"
+            type="password"
+            placeholder="请输入新密码(6到18位的数字或字母)"
+            clearable
+            @change="inputChange"
+          />
+        </n-form-item>
+        <n-form-item label="新密码" path="renewPassword">
+          <n-input
+            v-model:value="formModel.renewPassword"
+            type="password"
+            placeholder="请再次输入新密码"
+            clearable
+            @change="inputChange"
+          />
+        </n-form-item>
+        <n-form-item>
+          <n-button type="success" @click="toSubmit"> 保存 </n-button>
+        </n-form-item>
+      </n-form>
+    </div>
   </div>
 </template>
 
 <style scoped>
-.pwd-box {
-  padding: 60px 40px;
-  border: 1px solid var(--app-color-border);
-  border-radius: var(--app-border-radius);
-  max-width: 600px;
-  /* margin: 0 auto; */
+.pwd-box-content {
+  max-width: 500px;
+  min-width: 200px;
 }
 </style>

+ 101 - 0
src/features/SiteMessage/SiteMessage.vue

@@ -0,0 +1,101 @@
+<script lang="ts" setup>
+import { SiteMessage } from "@/types/student-client";
+import { messageListApi, updateNotieReadStatusApi } from "@/api/siteMessage";
+import { store } from "@/store/store";
+import { DataTableColumns, useMessage } from "naive-ui";
+import { h, computed, onMounted } from "vue";
+import { useRouter } from "vue-router";
+import { RowKey } from "naive-ui/lib/data-table/src/interface";
+
+const message = useMessage();
+const router = useRouter();
+
+const columns: DataTableColumns<SiteMessage> = [
+  {
+    type: "selection",
+  },
+  {
+    title: "标题",
+    key: "title",
+    render(row) {
+      return h("p", { class: "qm-link-text", onClick: () => toDetail(row) }, [
+        h("i", {
+          class: [
+            "icon",
+            "qm-mr-10",
+            row.hasRead ? "icon-sms-read" : "icon-sms-unread",
+          ],
+        }),
+        h("span", {}, row.title),
+      ]);
+    },
+  },
+  {
+    title: "发送时间",
+    key: "publishTime",
+    width: 180,
+  },
+];
+const messageList = computed(() => store.siteMessage.messages);
+let selectedMessageIds = $ref<RowKey[]>([]);
+const pagination = $ref({
+  page: 1,
+  pageSize: 10,
+  onChange: (page: number) => {
+    pagination.page = page;
+  },
+});
+
+const getList = async () => {
+  const res = await messageListApi(store.user.id, store.siteMessagesTimeStamp);
+  const messages = res.data.map((item) => {
+    let nitem = { ...item };
+    if (item.hasRecalled) {
+      nitem.title = "发送者已撤回消息:" + nitem.title;
+      nitem.content = "该消息已经被发送者撤回。";
+    }
+    return nitem;
+  });
+  store.siteMessage.messages = messages;
+};
+const selectedChange = (ids: RowKey[]) => {
+  selectedMessageIds = ids;
+};
+const toDetail = (row: SiteMessage) => {
+  void router.push({ name: "SiteMessageDetail", params: { noticeId: row.id } });
+};
+
+const toMarkRead = async () => {
+  if (!selectedMessageIds.length) {
+    message.error("请先选择要标记的信息!");
+    return;
+  }
+  await updateNotieReadStatusApi(selectedMessageIds.join());
+  message.success("修改成功!");
+  store.updateSiteMessagesTimeStamp();
+  void getList();
+};
+
+onMounted(() => {
+  void getList();
+});
+</script>
+
+<template>
+  <div class="box-justify qm-mb-20">
+    <h3 class="qm-big-text">公告通知</h3>
+    <n-button @click="toMarkRead"
+      ><i class="icon icon-message-read qm-mr-10"></i> 标记为已读</n-button
+    >
+  </div>
+  <div class="part-box">
+    <div class="tips-info qm-mb-5">*仅保留近1年的公告通知。</div>
+    <n-data-table
+      :columns="columns"
+      :data="messageList"
+      :pagination="pagination"
+      :rowKey="(row) => row.id"
+      @update:checkedRowKeys="selectedChange"
+    ></n-data-table>
+  </div>
+</template>

+ 37 - 0
src/features/SiteMessage/SiteMessageDetail.vue

@@ -0,0 +1,37 @@
+<script lang="ts" setup>
+import { SiteMessage } from "@/types/student-client";
+import { store } from "@/store/store";
+import { useRoute } from "vue-router";
+import { onMounted } from "vue";
+
+let notice: SiteMessage | undefined = $ref();
+const route = useRoute();
+
+const toBack = () => {
+  window.history.go(-1);
+};
+
+onMounted(() => {
+  const curNoticeId = Number(route.params.noticeId);
+  const curNotice = store.siteMessage.messages.find(
+    (item) => item.id === curNoticeId
+  );
+  notice = curNotice;
+});
+</script>
+
+<template>
+  <div class="box-justify qm-mb-20">
+    <div></div>
+    <n-button @click="toBack"
+      ><i class="icon icon-back qm-mr-10"></i> 返回列表</n-button
+    >
+  </div>
+  <div v-if="notice" class="part-box">
+    <div class="message-title tw-text-center">
+      <h3 class="text-4xl">{{ notice.title }}</h3>
+      <p class="tips-info">发布时间: {{ notice.publishTime }}</p>
+    </div>
+    <div class="message-desc" v-html="notice.content"></div>
+  </div>
+</template>

+ 12 - 0
src/router/index.ts

@@ -2,6 +2,8 @@ import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
 import UserLogin from "@/features/UserLogin/UserLogin.vue";
 import MainLayout from "@/components/MainLayout/MainLayout.vue";
 import ChangePassword from "@/features/ChangePassword/ChangePassword.vue";
+import SiteMessage from "@/features/SiteMessage/SiteMessage.vue";
+import SiteMessageDetail from "@/features/SiteMessage/SiteMessageDetail.vue";
 import { resetStore, store } from "@/store/store";
 
 const routes: RouteRecordRaw[] = [
@@ -16,6 +18,16 @@ const routes: RouteRecordRaw[] = [
         component: ChangePassword,
         name: "ChangePassword",
       },
+      {
+        path: "site-message",
+        component: SiteMessage,
+        name: "SiteMessage",
+      },
+      {
+        path: "site-message/:noticeId",
+        component: SiteMessageDetail,
+        name: "SiteMessageDetail",
+      },
     ],
   },
   {

+ 5 - 0
src/store/store.ts

@@ -11,6 +11,7 @@ const initStore: Store = {
   homeworkList: {} as Store["homeworkList"],
   practiceList: {} as Store["practiceList"],
   siteMessage: {} as Store["siteMessage"],
+  siteMessagesTimeStamp: Date.now(),
   globalMaskCount: 0,
   QECSConfig: {} as Store["QECSConfig"],
   network: {} as Store["network"],
@@ -30,6 +31,7 @@ const resetState: Omit<
   homeworkList: {} as Store["homeworkList"],
   practiceList: {} as Store["practiceList"],
   siteMessage: {} as Store["siteMessage"],
+  siteMessagesTimeStamp: Date.now(),
 };
 
 export function resetStore() {
@@ -61,6 +63,9 @@ export const useStore = defineStore("ecs", {
         store.globalMaskCount = 0;
       }
     },
+    updateSiteMessagesTimeStamp() {
+      store.siteMessagesTimeStamp = Date.now();
+    },
   },
 });
 

+ 14 - 12
src/styles/cssvar.css

@@ -1,28 +1,28 @@
 :root {
-  --app-color-header-bg: #43434d;
-  --app-color-body-bg: #eff0f5;
-  --app-color-border: #eff0f5;
+  --app-color-header-bg: #717171;
+  --app-color-body-bg: #fafafa;
+  --app-color-border: #eee;
   --app-color-border-dark: #d3d5e0;
 
-  --app-color-text-main: #1f2225;
-  --app-color-text-sub: #333639;
-  --app-color-text-light: #8b8fa1;
+  --app-color-text-main: #222c32;
+  --app-color-text-sub: #515a6e;
+  --app-color-text-light: #999;
 
-  --app-color-primary: #18a058;
-  --app-color-primary-light: #46b379;
-  --app-color-success: #18a058;
+  --app-color-primary: #2d8cf0;
+  --app-color-primary-light: #679cd4;
+  --app-color-success: #13bb8a;
   --app-color-success-light: #46b379;
   --app-color-warning: #f0a020;
   --app-color-warning-light: #f3b34d;
-  --app-color-danger: #d03050;
-  --app-color-danger-light: #d95973;
+  --app-color-error: #ed4014;
+  --app-color-error-light: #d95973;
   --app-color-info: #767c82;
   --app-color-white: #fff;
 
   --app-font-weight: 400;
   --app-font-weight-bold: 600;
 
-  --app-border-radius: 5px;
+  --app-border-radius: 6px;
   --app-border-radius-large: 10px;
   --app-border-radius-huge: 20px;
 
@@ -30,6 +30,8 @@
   --app-font-size-tiny: 12px;
   --app-font-size-large: 16px;
 
+  --app-line-height: 20px;
+
   --app-min-width: 1280px;
 
   --app-font-family: "Helvetica Neue", Helvetica, "PingFang SC",

+ 138 - 3
src/styles/global.css

@@ -1,9 +1,11 @@
 @import "./tailwind.css";
 @import "./nprogress.css";
 @import "./cssvar.css";
+@import "./icon.css";
 
 body {
   font-size: var(--app-font-size);
+  font-weight: var(--app-font-weight);
   color: var(--app-color-text-main);
   min-width: var(--app-min-width);
   min-height: 600px;
@@ -12,20 +14,153 @@ body {
   -moz-osx-font-smoothing: grayscale;
   user-select: none;
 }
+/* qm */
+.qm-primary-text {
+  font-size: var(--app-font-size);
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: 22px;
+  letter-spacing: 0px;
+  color: var(--app-color-text-main);
+}
+.qm-primary-strong-text {
+  font-size: var(--app-font-size);
+  font-weight: bold;
+  font-stretch: normal;
+  line-height: 14px;
+  letter-spacing: 0px;
+  color: var(--app-color-text-sub);
+}
+.qm-secondary-text {
+  font-size: var(--app-font-size-tiny);
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: var(--app-line-height);
+  letter-spacing: 0px;
+  color: var(--app-color-text-sub);
+}
+.qm-big-text {
+  font-size: var(--app-font-size-large);
+  font-weight: normal;
+  font-stretch: normal;
+  line-height: 22px;
+  letter-spacing: 0px;
+  color: var(--app-color-text-main);
+}
+.qm-title-text {
+  font-size: 18px;
+  font-weight: bold;
+  font-stretch: normal;
+  line-height: 24px;
+  letter-spacing: 0px;
+  color: var(--app-color-text-sub);
+}
+.qm-primary-button {
+  /* font-size: 36px; */
+  display: inline-block;
+  height: 36px;
+  font-size: var(--app-font-size);
+  color: var(--app-color-white);
+  background-color: var(--app-color-primary);
+  border-radius: var(--app-border-radius);
+  padding: 0 30px;
+  line-height: 36px;
+  overflow: hidden;
+}
+.qm-primary-button:hover {
+  color: var(--app-color-white);
+  background-color: var(--app-color-primary);
+}
+.n-button.qm-primary-button {
+  line-height: 1.5em;
+}
+.n-button.qm-primary-button-padding-fix {
+  padding: 0;
+}
+.qm-secondary-button {
+  height: 36px;
+  font-size: var(--app-font-size);
+  color: var(--app-color-text-light);
+  background-color: var(--app-color-white);
+  border-radius: var(--app-border-radius);
+  padding: 0 30px;
+  line-height: 36px;
+  overflow: hidden;
+}
+.qm-secondary-button:hover {
+  color: var(--app-color-text-light);
+  background-color: var(--app-color-white);
+}
+.n-button.qm-secondary-button {
+  line-height: 1.5em;
+}
+.qm-icon-button {
+  height: 36px;
+  font-size: 36px;
+  color: var(--app-color-text-light);
+  background-color: var(--app-color-white);
+  line-height: 36px;
+  overflow: hidden;
+}
+.qm-icon-button:hover {
+  color: var(--app-color-text-sub);
+  background-color: var(--app-color-white);
+}
+.qm-link-text {
+  color: var(--app-color-text-main);
+  cursor: pointer;
+}
+.qm-link-text:hover {
+  color: var(--app-color-primary);
+}
+
+.qm-mr-5 {
+  margin-right: 5px;
+}
+.qm-mr-10 {
+  margin-right: 10px;
+}
+.qm-mb-5 {
+  margin-bottom: 5px;
+}
+.qm-mb-10 {
+  margin-bottom: 10px;
+}
+.qm-mb-20 {
+  margin-bottom: 20px;
+}
 
 .main-text-color {
   color: var(--app-color-text-main);
 }
-
 .secondary-text {
   color: var(--app-color-text-sub);
   font-size: var(--app-font-size-tiny);
 }
-
 .header-small-text {
   font-size: var(--app-font-size-tiny);
 }
 .highlight-text {
-  color: white;
+  color: var(--app-color-white);
+  font-size: var(--app-font-size);
+}
+
+/* other */
+.part-box {
+  padding: 20px;
+  border: 1px solid var(--app-color-border);
+  border-radius: var(--app-border-radius);
+}
+.tips-info {
   font-size: var(--app-font-size);
+  line-height: var(--app-line-height);
+  color: var(--app-color-text-light);
+}
+.tips-error {
+  color: var(--app-color-error);
+}
+.box-justify {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
 }

+ 24 - 0
src/styles/icon.css

@@ -0,0 +1,24 @@
+/* icon */
+.icon {
+  display: inline-block;
+  vertical-align: middle;
+  width: 16px;
+  height: 16px;
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+}
+.icon-sms-read {
+  width: 16px;
+  height: 14px;
+  background-image: url(../assets/sms-read.svg);
+}
+.icon-sms-unread {
+  width: 16px;
+  height: 14px;
+  background-image: url(../assets/sms-unread.svg);
+}
+.icon-back {
+  width: 16px;
+  height: 14px;
+  background-image: url(../assets/back.svg);
+}

+ 13 - 0
src/themeOverrides.ts

@@ -0,0 +1,13 @@
+import { GlobalThemeOverrides } from "naive-ui";
+
+const themeOverrides: GlobalThemeOverrides = {
+  common: {
+    primaryColor: "#2d8cf0",
+    primaryColorHover: "#679cd4",
+    primaryColorPressed: "#679cd4",
+    successColor: "#13bb8a",
+    errorColor: "#ed4014",
+  },
+};
+
+export default themeOverrides;

+ 9 - 1
src/types/student-client.d.ts

@@ -111,6 +111,7 @@ export type Store = {
     /** 未读消息总数 */
     unreadCount: number;
   };
+  siteMessagesTimeStamp: number;
   /** 在线考试待考列表 */
   examList: OnlineExam[];
   /** 在线考试已结束的考试列表 */
@@ -162,7 +163,14 @@ export type Store = {
 };
 
 type SchoolDomain = `${string}.ecs.qmth.com.cn`;
-type SiteMessage = { id: number; hasRead: boolean; content: string };
+export type SiteMessage = {
+  id: number;
+  hasRead: boolean;
+  hasRecalled: boolean;
+  title: string;
+  content: string;
+  publishTime: string;
+};
 
 type BaseExam = {
   /** 考试批次id */

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно