|
|
@@ -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;
|