yanghao 1 týždeň pred
rodič
commit
9bb8d8efb1
4 zmenil súbory, kde vykonal 145 pridanie a 26 odobranie
  1. 33 0
      src/api/user.ts
  2. 14 3
      src/config/axios/service.ts
  3. 96 21
      src/views/index.vue
  4. 2 2
      src/views/login.vue

+ 33 - 0
src/api/user.ts

@@ -161,3 +161,36 @@ export const markOAMessageAsRead = async (id) => {
     url: "/admin-api/portal/todo/oa/notice/readed?workcode=" + id, // portal/todo/oa/notice/readed
   });
 };
+
+// 首页公告
+export const getNotices = async (params) => {
+  return await request.get({
+    url:
+      "/admin-api/portal/home/notice?pageNo=" +
+      params.pageNum +
+      "&pageSize=" +
+      params.pageSize,
+  });
+};
+
+// 红头文件
+export const getRedHeadFiles = async (params) => {
+  return await request.get({
+    url:
+      "/admin-api/portal/home/red?pageNo=" +
+      params.pageNum +
+      "&pageSize=" +
+      params.pageSize,
+  });
+};
+
+// 新闻资讯
+export const getNews = async (params) => {
+  return await request.get({
+    url:
+      "/admin-api/portal/home/news?pageNo=" +
+      params.pageNum +
+      "&pageSize=" +
+      params.pageSize,
+  });
+};

+ 14 - 3
src/config/axios/service.ts

@@ -204,15 +204,21 @@ const refreshToken = async () => {
       getRefreshToken(),
   );
 };
+
 const handleAuthorized = () => {
   const isManualLogout = sessionStorage.getItem(manualLogoutKey) === "true";
   const isReloginCanceled = sessionStorage.getItem(reloginCancelKey) === "true";
-
+  const ua = window.navigator.userAgent.toLowerCase();
   if (isManualLogout || isReloginCanceled) {
     deleteUserCache();
     removeToken();
+
     if (!window.location.href.includes("login")) {
-      window.location.href = "/login";
+      if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
+        window.location.href = "/";
+      } else {
+        window.location.href = "/login";
+      }
     }
     return Promise.reject("登录超时,请重新登录");
   }
@@ -236,6 +242,7 @@ const handleAuthorized = () => {
         deleteUserCache(); // 删除用户缓存
         removeToken();
         isRelogin.show = false;
+
         window.location.href = "/login";
       })
       .catch(() => {
@@ -243,7 +250,11 @@ const handleAuthorized = () => {
         deleteUserCache(); // 删除用户缓存
         removeToken();
         isRelogin.show = false; // 重置显示状态
-        window.location.href = "/login";
+        if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
+          window.location.href = "/";
+        } else {
+          window.location.href = "/login";
+        }
       });
   }
   return Promise.reject("登录超时,请重新登录");

+ 96 - 21
src/views/index.vue

@@ -1,4 +1,4 @@
-<template>
+<template>
   <div class="portal-home min-h-screen bg-[#eef3f9] text-[#17345f]">
     <Header />
 
@@ -43,16 +43,16 @@
             <div
               class="carousel-indicators absolute bottom-4 left-1/2 z-30 flex transform -translate-x-1/2 space-x-2"
             >
-              <button
+              <span
                 v-for="(_, i) in slides"
                 :key="i"
                 @click="currentIndex = i"
                 :class="{
                   'bg-[#0c4eb5]': currentIndex === i,
-                  'bg-[#d1ddea]': currentIndex !== i,
+                  'bg-[#2f333c]/50': currentIndex !== i,
                 }"
                 class="w-3 h-3 rounded-full transition-colors"
-              ></button>
+              ></span>
             </div>
           </div>
         </div>
@@ -114,19 +114,38 @@
         <aside class="space-y-4">
           <section class="side-card side-card--notice rounded-md p-2">
             <div class="side-card__header w-[95%] ml-[8px]">
-              <div class="notice-badge">{{ noticeLabel }}</div>
+              <div class="notice-tabs">
+                <button
+                  v-for="tab in noticeTabs"
+                  :key="tab.key"
+                  type="button"
+                  :class="[
+                    'notice-tab',
+                    activeNoticeTab === tab.key ? 'notice-tab--active' : '',
+                  ]"
+                  @click="handleNoticeTabChange(tab.key)"
+                >
+                  {{ tab.label }}
+                </button>
+              </div>
               <button type="button" class="side-card__more">
                 <Icon icon="mdi:dots-horizontal" class="text-[22px]" />
               </button>
             </div>
 
             <div class="space-y-2 p-2 pt-3">
+              <div
+                v-if="!currentNoticeList.length"
+                class="flex h-[120px] items-center justify-center text-[#8a9ab0]"
+              >
+                暂无数据
+              </div>
               <article
-                v-for="(notice, noticeIndex) in notices"
-                :key="`${notice.title}-${noticeIndex}`"
+                v-for="notice in currentNoticeList"
+                :key="notice.id"
                 class="notice-item cursor-pointer"
               >
-                <div class="notice-item__desc">{{ notice.desc }}</div>
+                <div class="notice-item__desc">{{ notice.docsubject }}</div>
               </article>
             </div>
           </section>
@@ -247,7 +266,7 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted, onUnmounted, ref } from "vue";
+import { computed, onMounted, onUnmounted, ref } from "vue";
 import * as authUtil from "@/utils/auth";
 import * as dd from "dingtalk-jsapi";
 import Header from "@components/home/header.vue";
@@ -261,6 +280,8 @@ import {
   ssoLogin,
   zentaoSsoLogin,
   getOATasks,
+  getNotices,
+  getRedHeadFiles,
 } from "@/api/user";
 import { useUserStore } from "@/stores/useUserStore";
 import { getAccessToken } from "@/utils/auth";
@@ -312,8 +333,10 @@ type PortalSection = {
 };
 
 type NoticeItem = {
-  title: string;
-  desc: string;
+  id?: string | number;
+  title?: string;
+  desc?: string;
+  docsubject?: string;
 };
 
 type SidePanel = {
@@ -340,6 +363,12 @@ const userStore = useUserStore();
 const noticeLabel = "公告";
 const todoPanelTitle = "待办中心";
 const newsPanelTitle = "新闻";
+const noticeTabs = [
+  { key: "notice", label: "公告" },
+  { key: "redHead", label: "红头文件" },
+] as const;
+
+type NoticeTabKey = (typeof noticeTabs)[number]["key"];
 
 const portalSections: PortalSection[] = [
   {
@@ -447,16 +476,11 @@ const nextSlide = () => {
   currentIndex.value = (currentIndex.value + 1) % slides.value.length;
 };
 
-const notices: NoticeItem[] = [
-  {
-    title: "集团总部关于办公楼设备维保的说明",
-    desc: "关于 2026 年度“DeepOil 数字化转型”优秀项目评选活动的通知",
-  },
-  {
-    title: "集团总部关于办公楼设备维保的说明",
-    desc: "关于 2026 年度“DeepOil 数字化转型”优秀项目评选活动的通知",
-  },
-];
+const activeNoticeTab = ref<NoticeTabKey>("notice");
+const noticeListMap = ref<Record<NoticeTabKey, NoticeItem[]>>({
+  notice: [],
+  redHead: [],
+});
 
 const newsList: NewsItem[] = [
   {
@@ -476,6 +500,28 @@ const newsList: NewsItem[] = [
   },
 ];
 
+const currentNoticeList = computed(
+  () => noticeListMap.value[activeNoticeTab.value] ?? [],
+);
+
+const loadNoticeList = async (tabKey: NoticeTabKey) => {
+  const requestApi = tabKey === "notice" ? getNotices : getRedHeadFiles;
+  const res = await requestApi({
+    pageNum: 1,
+    pageSize: 10,
+  });
+
+  noticeListMap.value[tabKey] = (res?.list || []).slice(0, 3);
+};
+
+const handleNoticeTabChange = async (tabKey: NoticeTabKey) => {
+  activeNoticeTab.value = tabKey;
+
+  if (noticeListMap.value[tabKey].length) return;
+
+  await loadNoticeList(tabKey);
+};
+
 const protectedOpen = (url: string) => {
   if (userStore.getUser.username && getAccessToken()) {
     window.open(url, "_blank");
@@ -659,6 +705,8 @@ onMounted(async () => {
   dingTalkAutoLogin();
   slideTimer = setInterval(nextSlide, 5000);
 
+  await loadNoticeList("notice");
+
   if (userStore.getUser.username) {
     try {
       const res = await getOATasks({
@@ -1039,6 +1087,33 @@ onUnmounted(() => {
   border-radius: 100px;
 }
 
+.notice-tabs {
+  display: inline-flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.notice-tab {
+  height: 25px;
+  min-width: 68px;
+  padding: 0 14px;
+  border: 0;
+  border-radius: 999px;
+  background: transparent;
+  color: #4d6f98;
+  font-size: 14px;
+  font-weight: 700;
+  cursor: pointer;
+  transition:
+    background 0.2s ease,
+    color 0.2s ease;
+}
+
+.notice-tab--active {
+  background: #004098;
+  color: #fff;
+}
+
 .notice-badge::after {
   content: "";
   position: absolute;

+ 2 - 2
src/views/login.vue

@@ -21,7 +21,7 @@
         <h1 class="text-2xl font-bold text-center">登录</h1>
 
         <!-- 用户名密码登陆 -->
-        <!-- <div>
+        <div>
           <el-form
             :model="form"
             :rules="rules"
@@ -62,7 +62,7 @@
               >
             </div>
           </div>
-        </div> -->
+        </div>
 
         <!-- 钉钉登陆 -->
         <div class="text-center">