소스 검색

Merge branch 'flow' of ruiqigogs/yf-portal-vue into master

yanghao 6 일 전
부모
커밋
37d6351ac3
7개의 변경된 파일783개의 추가작업 그리고 73개의 파일을 삭제
  1. 1 0
      components.d.ts
  2. 33 0
      src/api/user.ts
  3. 19 4
      src/config/axios/service.ts
  4. 23 0
      src/router/index.ts
  5. 221 69
      src/views/index.vue
  6. 240 0
      src/views/news/index.vue
  7. 246 0
      src/views/notices/index.vue

+ 1 - 0
components.d.ts

@@ -21,6 +21,7 @@ declare module 'vue' {
     ElDropdown: typeof import('element-plus/es')['ElDropdown']
     ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
     ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']

+ 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,
+  });
+};

+ 19 - 4
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,14 +242,23 @@ 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";
+        }
       })
       .catch(() => {
         sessionStorage.setItem(reloginCancelKey, "true");
         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("登录超时,请重新登录");

+ 23 - 0
src/router/index.ts

@@ -87,6 +87,29 @@ const routes: RouteRecordRaw[] = [
       title: "DeepOil 智慧经营平台 | 驾驶舱",
     },
   },
+
+  {
+    path: "/news",
+    name: "News",
+    component: () => import("@/views/news/index.vue"),
+    meta: {
+      title: "DeepOil 智慧经营平台 | 新闻",
+    },
+  },
+
+  {
+    path: "/notice-redhead",
+    name: "NoticeRedhead",
+    component: () => import("@/views/notices/index.vue"),
+    meta: {
+      // 动态设置页面标题
+      dynamicTitle: (route) => {
+        const { paramsObject } = parseURL(route.fullPath);
+        const { title } = paramsObject;
+        return title || "新闻";
+      },
+    },
+  },
 ];
 
 const router = createRouter({

+ 221 - 69
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,54 @@
         <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]" />
+                <Icon
+                  icon="mdi:dots-horizontal"
+                  class="text-[22px]"
+                  @click="handleNoticeMoreClick"
+                />
               </button>
             </div>
 
             <div class="space-y-2 p-2 pt-3">
+              <div
+                v-if="!userStore.getUser.username"
+                class="flex h-full items-center justify-center"
+              >
+                <div class="text-[#8a9ab0] pt-5">登录后查看</div>
+              </div>
+
+              <div
+                v-if="!currentNoticeList.length && userStore.getUser.username"
+                class="flex h-[120px] items-center justify-center text-[#8a9ab0]"
+              >
+                暂无数据
+              </div>
               <article
-                v-for="(notice, noticeIndex) in notices"
-                :key="`${notice.title}-${noticeIndex}`"
+                v-if="userStore.getUser.username"
+                v-for="notice in currentNoticeList"
+                :key="notice.id"
                 class="notice-item cursor-pointer"
+                @click="handleNoticeItemClick(notice)"
               >
-                <div class="notice-item__desc">{{ notice.desc }}</div>
+                <div class="notice-item__desc">{{ notice.docsubject }}</div>
+                <div class="date text-[12px] text-[#9cadc0]">
+                  {{ notice.docvaliddate }}
+                </div>
               </article>
             </div>
           </section>
@@ -157,7 +192,7 @@
                 v-if="!oaTasks.length && userStore.getUser.username"
                 class="flex h-full items-center justify-center"
               >
-                <div class="text-[#8a9ab0]">暂无数据</div>
+                <div class="text-[#8a9ab0] pt-10">暂无待办</div>
               </div>
 
               <article
@@ -193,29 +228,38 @@
           >
             <div class="side-card__header w-[90%] ml-[15px]">
               <div class="notice-badge">{{ newsPanelTitle }}</div>
-              <button type="button" class="side-card__more">
+              <button type="button" class="side-card__more" @click="goNews">
                 <Icon icon="mdi:dots-horizontal" class="text-[22px]" />
               </button>
             </div>
             <div class="space-y-2 p-4 pt-2">
+              <div
+                v-if="!userStore.getUser.username"
+                class="flex h-full items-center justify-center"
+              >
+                <div class="text-[#8a9ab0] pt-10">登录后查看</div>
+              </div>
+
+              <div
+                v-if="!newsList.length && userStore.getUser.username"
+                class="flex h-full items-center justify-center"
+              >
+                <div class="text-[#8a9ab0] pt-10">暂无新闻</div>
+              </div>
               <article
                 v-for="news in newsList"
-                :key="news.title"
+                :key="news.id"
                 class="news-mini cursor-pointer rounded-md"
+                @click="handleNoticeItemClick(news)"
               >
-                <img
-                  :src="news.image"
-                  :alt="news.title"
-                  class="h-[34px] w-[52px] rounded-[8px] object-cover"
-                />
                 <div class="min-w-0 flex-1">
                   <div
-                    class="line-clamp-2 text-[13px] font-semibold leading-[1.45] text-[#41597d]"
+                    class="line-clamp-1 text-[13px] font-semibold leading-[1.45] text-[#41597d]"
                   >
-                    {{ news.title }}
+                    {{ news.docsubject }}
                   </div>
                   <div class="mt-2 text-[12px] text-[#9cadc0]">
-                    {{ news.date }}
+                    {{ news.docvaliddate }}
                   </div>
                 </div>
               </article>
@@ -247,7 +291,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 +305,9 @@ import {
   ssoLogin,
   zentaoSsoLogin,
   getOATasks,
+  getNotices,
+  getRedHeadFiles,
+  getNews,
 } from "@/api/user";
 import { useUserStore } from "@/stores/useUserStore";
 import { getAccessToken } from "@/utils/auth";
@@ -312,34 +359,23 @@ type PortalSection = {
 };
 
 type NoticeItem = {
-  title: string;
-  desc: string;
-};
-
-type SidePanel = {
-  title: string;
-  height: string;
-};
-
-type TodoTask = {
-  title: string;
-  meta: string;
-  tag: string;
-  tagClass: string;
-};
-
-type NewsItem = {
-  title: string;
-  date: string;
-  image: string;
+  id?: string | number;
+  title?: string;
+  desc?: string;
+  docsubject?: string;
 };
 
 const router = useRouter();
 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,34 +483,48 @@ 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[] = [
-  {
-    title: "科瑞石油成功交付首套自动化钻机",
-    date: "2026-04-18",
-    image: banner1,
-  },
-  {
-    title: "DeepOil 智慧平台荣获行业创新奖",
-    date: "2026-04-15",
-    image: banner2,
-  },
-  {
-    title: "数字人才培养“扬帆计划”正式启动",
-    date: "2026-04-12",
-    image: img3,
-  },
-];
+const newsList = ref([]);
+
+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);
+};
+
+let currentTabKey = ref<NoticeTabKey>("notice");
+const handleNoticeTabChange = async (tabKey: NoticeTabKey) => {
+  activeNoticeTab.value = tabKey;
+  currentTabKey.value = tabKey;
+
+  // if (noticeListMap.value[tabKey].length) return;
+
+  if (userStore.getUser.username) {
+    await loadNoticeList(tabKey);
+  }
+};
+
+const handleNoticeMoreClick = () => {
+  router.push({
+    path: "/notice-redhead",
+    query: {
+      tabKey: currentTabKey.value,
+    },
+  });
+};
 
 const protectedOpen = (url: string) => {
   if (userStore.getUser.username && getAccessToken()) {
@@ -659,6 +709,8 @@ onMounted(async () => {
   dingTalkAutoLogin();
   slideTimer = setInterval(nextSlide, 5000);
 
+  await loadNoticeList("notice");
+
   if (userStore.getUser.username) {
     try {
       const res = await getOATasks({
@@ -667,11 +719,64 @@ onMounted(async () => {
         pageSize: 10,
       });
       oaTasks.value = res.todoList.slice(0, 3);
+
+      const newList = await getNews({
+        pageNum: 1,
+        pageSize: 10,
+      });
+
+      newsList.value = newList.list.slice(0, 3);
     } finally {
     }
   }
 });
 
+const handleNoticeItemClick = async (notice) => {
+  const res = await ssoLogin({
+    username: userStore.getUser.username,
+  });
+
+  if (res) {
+    const ua = window.navigator.userAgent.toLowerCase();
+    if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
+      dd.biz.util.openLink({
+        url:
+          "https://yfoa.keruioil.com/wui/index.html" +
+          "?ssoToken=" +
+          res +
+          "#/main", // 先跳你的 SSO 链接
+        onSuccess: () => {
+          // 延迟跳目标业务地址(和你原来 setTimeout 逻辑一致)
+          setTimeout(() => {
+            dd.biz.util.openLink({
+              url: `https://yfoa.keruioil.com/spa/document/index.jsp?openAttachment=0&id=${notice.id}`,
+            });
+          }, 100);
+        },
+      });
+    } else {
+      const loading = ElLoading.service({
+        lock: true,
+        text: "正在跳转,请稍候...",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+      const newTab = window.open("", "_blank");
+      newTab.location.href =
+        "https://yfoa.keruioil.com/wui/index.html" +
+        "?ssoToken=" +
+        res +
+        "#/main";
+
+      setTimeout(function () {
+        newTab.location.href = `https://yfoa.keruioil.com/spa/document/index.jsp?openAttachment=0&id=${notice.id}`;
+        setTimeout(() => {
+          loading.close();
+        }, 500);
+      }, 100);
+    }
+  }
+};
+
 const handleTask = async (row) => {
   const res = await ssoLogin({
     username: userStore.getUser.username,
@@ -718,6 +823,10 @@ const handleTask = async (row) => {
   }
 };
 
+const goNews = () => {
+  router.push("/news");
+};
+
 onUnmounted(() => {
   if (slideTimer) {
     clearInterval(slideTimer);
@@ -1039,6 +1148,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;
@@ -1064,11 +1200,21 @@ onUnmounted(() => {
   border-radius: 10px;
 }
 
+.notice-item:hover {
+  box-shadow: 0 12px 28px rgba(63, 107, 169, 0.1);
+  transition: all 0.2s ease;
+  transform: translateY(-1px);
+}
+
 .notice-item__title,
 .notice-item__desc {
   color: #507698;
   font-size: 13px;
   line-height: 1.5;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 1;
+  overflow: hidden;
 }
 
 .panel-title {
@@ -1104,6 +1250,12 @@ onUnmounted(() => {
   border: none;
 }
 
+.news-mini:hover {
+  box-shadow: 0 12px 28px rgba(63, 107, 169, 0.1);
+  transition: all 0.2s ease;
+  transform: translateY(-1px);
+}
+
 .placeholder-panel {
   display: flex;
   height: 100%;

+ 240 - 0
src/views/news/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div class="news-container">
+    <Header />
+
+    <div class="content-wrapper mt-15 max-w-[1200px] mx-auto">
+      <h2 class="page-title">新闻中心</h2>
+
+      <!-- 新闻列表区域 - 使用 el-table -->
+      <div v-loading="loading" class="table-wrapper">
+        <el-table
+          :data="newsList"
+          style="width: 100%"
+          height="70vh"
+          v-loading="loading"
+          element-loading-text="加载中..."
+          :empty-text="loading ? '' : '暂无数据'"
+          stripe
+          :header-cell-style="{
+            backgroundColor: '#e9f7ff',
+            color: 'black',
+            fontWeight: '400',
+          }"
+          :cell-style="{
+            color: 'black',
+          }"
+        >
+          <!-- 序号列 -->
+          <el-table-column
+            type="index"
+            label="序号"
+            width="60"
+            align="center"
+          />
+
+          <!-- 标题列 -->
+          <el-table-column prop="title" label="标题" min-width="300">
+            <template #default="{ row }">
+              <span class="table-title">{{ row.docsubject }}</span>
+            </template>
+          </el-table-column>
+
+          <!-- 时间列 -->
+          <el-table-column
+            prop="docvaliddate"
+            label="发布时间"
+            width="180"
+            align="center"
+            sortable
+          />
+
+          <!-- 操作列(可选) -->
+          <el-table-column label="操作" width="100" align="center">
+            <template #default="{ row }">
+              <el-button
+                link
+                type="primary"
+                size="small"
+                @click="handleNoticeItemClick(row)"
+                >查看详情</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+
+      <!-- 分页区域 -->
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="pagination.pageNum"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+
+    <Footer />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import Header from "@components/home/header.vue";
+import Footer from "@components/home/Footer.vue";
+import { ElLoading } from "element-plus";
+import { getNews, ssoLogin } from "@/api/user";
+import { useUserStore } from "@/stores/useUserStore";
+const userStore = useUserStore();
+// 状态定义
+const loading = ref(false);
+const newsList = ref([]);
+
+// 分页参数
+const pagination = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const handleNoticeItemClick = async (notice) => {
+  const res = await ssoLogin({
+    username: userStore.getUser.username,
+  });
+
+  if (res) {
+    const ua = window.navigator.userAgent.toLowerCase();
+    if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
+      dd.biz.util.openLink({
+        url:
+          "https://yfoa.keruioil.com/wui/index.html" +
+          "?ssoToken=" +
+          res +
+          "#/main", // 先跳你的 SSO 链接
+        onSuccess: () => {
+          // 延迟跳目标业务地址(和你原来 setTimeout 逻辑一致)
+          setTimeout(() => {
+            dd.biz.util.openLink({
+              url: `https://yfoa.keruioil.com/spa/document/index.jsp?openAttachment=0&id=${notice.id}`,
+            });
+          }, 100);
+        },
+      });
+    } else {
+      const loading = ElLoading.service({
+        lock: true,
+        text: "正在跳转,请稍候...",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+      const newTab = window.open("", "_blank");
+      newTab.location.href =
+        "https://yfoa.keruioil.com/wui/index.html" +
+        "?ssoToken=" +
+        res +
+        "#/main";
+
+      setTimeout(function () {
+        newTab.location.href = `https://yfoa.keruioil.com/spa/document/index.jsp?openAttachment=0&id=${notice.id}`;
+        setTimeout(() => {
+          loading.close();
+        }, 500);
+      }, 100);
+    }
+  }
+};
+
+// 获取新闻列表方法
+const fetchNewsList = async () => {
+  loading.value = true;
+  try {
+    const res = await getNews({
+      pageNum: pagination.pageNum,
+      pageSize: pagination.pageSize,
+    });
+
+    // 请根据实际接口返回结构调整以下赋值逻辑
+    if (res && res.list) {
+      newsList.value = res.list || [];
+      pagination.total = res.total || 0;
+    } else if (res && res.data) {
+      // 兼容另一种常见返回结构
+      newsList.value = res.data.list || [];
+      pagination.total = res.data.total || 0;
+    }
+  } catch (error) {
+    console.error("获取新闻列表失败:", error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+// 分页大小改变事件
+const handleSizeChange = (val) => {
+  pagination.pageSize = val;
+  pagination.pageNum = 1;
+  fetchNewsList();
+};
+
+// 当前页改变事件
+const handleCurrentChange = (val) => {
+  pagination.pageNum = val;
+  fetchNewsList();
+};
+
+// 组件挂载时获取数据
+onMounted(() => {
+  fetchNewsList();
+});
+</script>
+
+<style scoped>
+.news-container {
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f7fa;
+}
+
+.content-wrapper {
+  flex: 1;
+  max-width: 1200px;
+  /* margin: 0 auto; */
+  padding: 20px;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.page-title {
+  margin-bottom: 20px;
+  color: #303133;
+  border-left: 5px solid #409eff;
+  padding-left: 10px;
+}
+
+.table-wrapper {
+  min-height: 400px;
+  background: #fff;
+  padding: 20px;
+  border-radius: 4px;
+}
+
+.table-title {
+  font-weight: 500;
+  color: #303133;
+}
+
+.table-summary {
+  color: #606266;
+  font-size: 13px;
+}
+
+.pagination-container {
+  display: flex;
+  justify-content: center;
+  margin-top: 10px;
+  padding: 20px 0;
+}
+</style>

+ 246 - 0
src/views/notices/index.vue

@@ -0,0 +1,246 @@
+<template>
+  <div class="news-container">
+    <Header />
+
+    <div class="content-wrapper mt-15 max-w-[1200px] mx-auto">
+      <h2 class="page-title">
+        {{ route.query.tabKey === "notice" ? "公告" : "红头文件" }}
+      </h2>
+
+      <!-- 新闻列表区域 - 使用 el-table -->
+      <div v-loading="loading" class="table-wrapper">
+        <el-table
+          :data="newsList"
+          style="width: 100%"
+          height="70vh"
+          v-loading="loading"
+          element-loading-text="加载中..."
+          :empty-text="loading ? '' : '暂无数据'"
+          stripe
+          :header-cell-style="{
+            backgroundColor: '#e9f7ff',
+            color: 'black',
+            fontWeight: '400',
+          }"
+          :cell-style="{
+            color: 'black',
+          }"
+        >
+          <!-- 序号列 -->
+          <el-table-column
+            type="index"
+            label="序号"
+            width="60"
+            align="center"
+          />
+
+          <!-- 标题列 -->
+          <el-table-column prop="title" label="标题" min-width="300">
+            <template #default="{ row }">
+              <span class="table-title">{{ row.docsubject }}</span>
+            </template>
+          </el-table-column>
+
+          <!-- 时间列 -->
+          <el-table-column
+            prop="docvaliddate"
+            label="发布时间"
+            width="180"
+            align="center"
+            sortable
+          />
+
+          <!-- 操作列(可选) -->
+          <el-table-column label="操作" width="100" align="center">
+            <template #default="{ row }">
+              <el-button
+                link
+                type="primary"
+                size="small"
+                @click="handleNoticeItemClick(row)"
+                >查看详情</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+
+      <!-- 分页区域 -->
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="pagination.pageNum"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+
+    <Footer />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import Header from "@components/home/header.vue";
+import Footer from "@components/home/Footer.vue";
+import { ElLoading } from "element-plus";
+import { getNews, ssoLogin, getNotices, getRedHeadFiles } from "@/api/user";
+import { useUserStore } from "@/stores/useUserStore";
+import { useRoute } from "vue-router";
+const userStore = useUserStore();
+const route = useRoute();
+// 状态定义
+const loading = ref(false);
+const newsList = ref([]);
+
+// 分页参数
+const pagination = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const handleNoticeItemClick = async (notice) => {
+  const res = await ssoLogin({
+    username: userStore.getUser.username,
+  });
+
+  if (res) {
+    const ua = window.navigator.userAgent.toLowerCase();
+    if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
+      dd.biz.util.openLink({
+        url:
+          "https://yfoa.keruioil.com/wui/index.html" +
+          "?ssoToken=" +
+          res +
+          "#/main", // 先跳你的 SSO 链接
+        onSuccess: () => {
+          // 延迟跳目标业务地址(和你原来 setTimeout 逻辑一致)
+          setTimeout(() => {
+            dd.biz.util.openLink({
+              url: `https://yfoa.keruioil.com/spa/document/index.jsp?openAttachment=0&id=${notice.id}`,
+            });
+          }, 100);
+        },
+      });
+    } else {
+      const loading = ElLoading.service({
+        lock: true,
+        text: "正在跳转,请稍候...",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+      const newTab = window.open("", "_blank");
+      newTab.location.href =
+        "https://yfoa.keruioil.com/wui/index.html" +
+        "?ssoToken=" +
+        res +
+        "#/main";
+
+      setTimeout(function () {
+        newTab.location.href = `https://yfoa.keruioil.com/spa/document/index.jsp?openAttachment=0&id=${notice.id}`;
+        setTimeout(() => {
+          loading.close();
+        }, 500);
+      }, 100);
+    }
+  }
+};
+
+// 获取新闻列表方法
+const fetchNewsList = async () => {
+  loading.value = true;
+  try {
+    const requestApi =
+      route.query.tabKey === "notice" ? getNotices : getRedHeadFiles;
+    const res = await requestApi({
+      pageNum: pagination.pageNum,
+      pageSize: pagination.pageSize,
+    });
+
+    // 请根据实际接口返回结构调整以下赋值逻辑
+    if (res && res.list) {
+      newsList.value = res.list || [];
+      pagination.total = res.total || 0;
+    } else if (res && res.data) {
+      // 兼容另一种常见返回结构
+      newsList.value = res.data.list || [];
+      pagination.total = res.data.total || 0;
+    }
+  } catch (error) {
+    console.error("获取新闻列表失败:", error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+// 分页大小改变事件
+const handleSizeChange = (val) => {
+  pagination.pageSize = val;
+  pagination.pageNum = 1;
+  fetchNewsList();
+};
+
+// 当前页改变事件
+const handleCurrentChange = (val) => {
+  pagination.pageNum = val;
+  fetchNewsList();
+};
+
+// 组件挂载时获取数据
+onMounted(() => {
+  fetchNewsList();
+});
+</script>
+
+<style scoped>
+.news-container {
+  min-height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f7fa;
+}
+
+.content-wrapper {
+  flex: 1;
+  max-width: 1200px;
+  /* margin: 0 auto; */
+  padding: 20px;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.page-title {
+  margin-bottom: 20px;
+  color: #303133;
+  border-left: 5px solid #409eff;
+  padding-left: 10px;
+}
+
+.table-wrapper {
+  min-height: 400px;
+  background: #fff;
+  padding: 20px;
+  border-radius: 4px;
+}
+
+.table-title {
+  font-weight: 500;
+  color: #303133;
+}
+
+.table-summary {
+  color: #606266;
+  font-size: 13px;
+}
+
+.pagination-container {
+  display: flex;
+  justify-content: center;
+  margin-top: 10px;
+  padding: 20px 0;
+}
+</style>