Browse Source

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

yanghao 2 tuần trước cách đây
mục cha
commit
daabc3ff15

+ 4 - 0
components.d.ts

@@ -12,6 +12,7 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     CardItem: typeof import('./src/components/home/CardItem.vue')['default']
+    ElAlert: typeof import('element-plus/es')['ElAlert']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCard: typeof import('element-plus/es')['ElCard']
@@ -38,4 +39,7 @@ declare module 'vue' {
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
   }
+  export interface GlobalDirectives {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }

BIN
src/assets/images/banner1.jpg


BIN
src/assets/images/banner1.png


BIN
src/assets/images/banner2.jpg


BIN
src/assets/images/banner3.jpg


BIN
src/assets/images/bg666.png


BIN
src/assets/images/logo.png


+ 9 - 16
src/components/home/CardItem.vue

@@ -1,13 +1,13 @@
 <template>
   <el-card class="w-full min-w-0 relative" style="border: 0">
     <div
-      class="relative overflow-hidden rounded-md mb-3 h-36 bg-cover bg-center"
+      class="relative overflow-hidden rounded-lg mb-3 h-25 bg-cover bg-center"
       :style="{ backgroundImage: `url(${bg})` }"
     >
       <div class="absolute inset-0 bg-white/0"></div>
       <div class="relative h-full flex items-center gap-3 px-4 cursor-pointer">
         <div class="min-w-0 flex-1">
-          <div class="text-[20px] font-bold text-[#0050b3]">{{ title }}</div>
+          <h3 class="font-bold">{{ title }}</h3>
           <div class="text-[#606266] text-sm mt-1 leading-snug">{{ desc }}</div>
         </div>
       </div>
@@ -15,11 +15,11 @@
 
     <!-- 九宫格布局 -->
     <div class="p-4">
-      <div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
+      <div class="flex flex-wrap gap-4">
         <div
           v-for="(item, index) in items"
           :key="index"
-          class="relative bg-gray-50 rounded-lg p-3 text-center cursor-pointer group transform transition-transform duration-200 hover:bg-blue-50 hover:scale-105 hover:shadow-lg"
+          class="relative rounded-lg p-3 text-center cursor-pointer group transform transition-transform duration-200 hover:bg-blue-50 hover:scale-105 hover:shadow-lg"
           @mouseenter="showTooltip(index)"
           @mouseleave="hideTooltip"
           @click="handleView(item)"
@@ -27,7 +27,7 @@
           <div class="flex flex-col items-center">
             <!-- 功能图标占位符 -->
             <div
-              class="w-13 h-13 bg-blue-100 rounded-lg flex items-center justify-center mb-2 mx-auto transition-colors"
+              class="w-13 h-13 rounded-lg flex items-center justify-center mb-2 mx-auto transition-colors"
             >
               <img
                 :src="getIconByLabel(item.label)"
@@ -39,7 +39,7 @@
             <!-- 功能名称 -->
             <span
               :class="[
-                'text-sm w-full px-1',
+                'text-xs font-medium text-slate-600',
                 isSpecialSystem(item.label)
                   ? 'font-bold text-gray-800'
                   : 'text-gray-700',
@@ -49,23 +49,16 @@
             </span>
 
             <!-- 标签 -->
-            <span
+            <!-- <span
               v-if="item.tag"
               class="mt-1 text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-700"
             >
               {{ item.tag }}
-            </span>
+            </span> -->
           </div>
         </div>
       </div>
     </div>
-
-    <div class="h-[50px] mt-20"></div>
-
-    <div
-      class="absolute bottom-0 left-0 w-full h-36 bg-contain bg-center no-repeat"
-      :style="{ backgroundImage: `url(${bg2[0]})` }"
-    ></div>
   </el-card>
 </template>
 
@@ -126,7 +119,7 @@ const props = defineProps<{
   desc: string;
   items: Item[];
   bg: string;
-  bg2: string[];
+  // bg2: string[];
   id: string;
 }>();
 

+ 3 - 69
src/components/home/Footer.vue

@@ -1,73 +1,7 @@
 <template>
-  <footer class="bg-[#0e1324] text-gray-300">
-    <div
-      class="max-w-[1200px] mx-auto px-4 py-12 grid grid-cols-1 md:grid-cols-6 gap-6"
-    >
-      <div class="md:col-span-2 flex gap-4">
-        <div class="w-24 h-24 bg-white/5 flex items-center justify-center">
-          <!-- QR placeholder -->
-          <div
-            class="w-20 h-20 bg-white/10 flex items-center justify-center text-xs text-white/80"
-          >
-            扫码关注
-          </div>
-        </div>
-        <div class="text-sm leading-relaxed">
-          <div class="text-white font-semibold mb-2 text-[14px]">DeepOil</div>
-          <div class="mb-2 text-[12px]">
-            咨询热线:<span class="text-white font-medium">401-1888-515</span>
-          </div>
-          <div class="text-[12px]">公司邮箱:mkt@DeepOil.com</div>
-        </div>
-      </div>
-
-      <div class="md:col-span-1">
-        <div class="text-white font-semibold mb-3 text-[14px]">DeepOil产品</div>
-        <ul class="space-y-2 text-[12px] text-gray-400">
-          <li>经营驾驶舱</li>
-          <li>智能钻井系统</li>
-          <li>QHSE (安全监控、应急指挥)</li>
-          <li>全局数据治理 (数据中台)</li>
-          <li>行业AI大模型</li>
-          <li>智慧指挥平台</li>
-        </ul>
-      </div>
-
-      <div class="md:col-span-1">
-        <div class="text-white text-[14px] font-semibold mb-3">
-          行业解决方案
-        </div>
-        <ul class="space-y-2 text-[12px] text-gray-400">
-          <li>设备管理系统 (PMS)</li>
-          <li>项目管理 (PM)</li>
-          <li>Chat BI平台</li>
-          <li>数字运营平台</li>
-        </ul>
-      </div>
-
-      <div class="md:col-span-1">
-        <div class="text-white text-[14px] font-semibold mb-3">
-          行业模型市场
-        </div>
-        <ul class="space-y-2 text-[12px] text-gray-400">
-          <li>基础工具</li>
-          <li>行业AI大模型</li>
-          <li>AI智能体</li>
-          <li>数据服务</li>
-        </ul>
-      </div>
-
-      <div class="md:col-span-1">
-        <div class="text-white text-[14px] font-semibold mb-3">关于DeepOil</div>
-        <ul class="space-y-2 text-[12px] text-gray-400">
-          <li>我们的客户</li>
-          <li>合作伙伴</li>
-        </ul>
-      </div>
-    </div>
-
-    <div class="bg-[#08101a] text-center text-[12px] text-gray-400 py-3">
-      Copyright ©2025 DeepOil 鲁ICP备2025162998号-1
+  <footer class="text-gray-300">
+    <div class="text-center text-[12px] text-gray-400 py-3">
+      Copyright ©2026 DeepOil 鲁ICP备2025162998号-1
     </div>
   </footer>
 </template>

+ 26 - 11
src/components/home/header.vue

@@ -3,13 +3,11 @@
     class="fixed w-full top-0 z-100 bg-white border-b border-[#f0f2f5] shadow-sm"
   >
     <div
-      class="w-full mx-auto flex items-center justify-between px-10 pr-0 h-20"
+      class="max-w-[1200px] mx-auto flex items-center justify-between px-10 pr-0 h-20"
     >
       <div class="flex items-center gap-2 cursor-pointer" @click="goHome">
-        <img :src="logo" alt="logo" class="w-20 h-9" />
-        <span class="text-[#02409b] text-[20px] font-bold border-l-2 pl-1"
-          >DeepOil</span
-        >
+        <img :src="logo" alt="logo" class="w-9 h-9 rounded-sm" />
+        <span class="text-[#02409b] text-[20px] font-bold">DeepOil</span>
       </div>
 
       <nav class="hidden lg:flex flex-1 mx-4 ml-10 text-sm">
@@ -27,7 +25,11 @@
               >流程门户</a
             >
           </li>
-          <li><a class="hover:text-[#02409b] cursor-pointer">驾驶舱门户</a></li>
+          <li>
+            <a class="hover:text-[#02409b] cursor-pointer" @click="goDrive"
+              >驾驶舱门户</a
+            >
+          </li>
           <li><a class="hover:text-[#02409b] cursor-pointer">报表门户</a></li>
         </ul>
       </nav>
@@ -300,16 +302,25 @@
     >
       <div class="p-4 space-y-3">
         <ul class="flex flex-col gap-3 text-[#303133]">
-          <li><a class="block py-2">产品</a></li>
-          <li><a class="block py-2">解决方案</a></li>
-          <li><a class="block py-2">典型案例</a></li>
-          <li><a class="block py-2">平台服务</a></li>
-          <li><a class="block py-2">应用市场</a></li>
+          <li>
+            <a
+              class="hover:text-[#02409b] cursor-pointer"
+              @click="router.push('/')"
+              >首页</a
+            >
+          </li>
+
           <li>
             <a class="hover:text-[#02409b] cursor-pointer" @click="goFlow"
               >流程门户</a
             >
           </li>
+          <li>
+            <a class="hover:text-[#02409b] cursor-pointer" @click="goDrive"
+              >驾驶舱门户</a
+            >
+          </li>
+          <li><a class="hover:text-[#02409b] cursor-pointer">报表门户</a></li>
         </ul>
         <div class="flex items-center gap-3 mt-3">
           <template v-if="isLoggedIn">
@@ -532,6 +543,10 @@ const goFlow = () => {
   router.push({ path: "/flow" });
 };
 
+const goDrive = () => {
+  router.push({ path: "/drive" });
+};
+
 const onUserCommand = async (command: string) => {
   if (command === "logout") {
     // await userStore.loginOut();

+ 14 - 1
src/router/index.ts

@@ -11,7 +11,11 @@ import Login from "@/views/login.vue";
 import { getAccessToken } from "@utils/auth";
 import { socialLogin } from "@/api/user";
 import * as authUtil from "@/utils/auth";
-import { isRelogin, manualLogoutKey, reloginCancelKey } from "@/config/axios/service";
+import {
+  isRelogin,
+  manualLogoutKey,
+  reloginCancelKey,
+} from "@/config/axios/service";
 
 import { useUserStoreWithOut } from "@/stores/useUserStore";
 
@@ -74,6 +78,15 @@ const routes: RouteRecordRaw[] = [
       title: "DeepOil 智慧经营平台 | CRM已办列表",
     },
   },
+
+  {
+    path: "/drive",
+    name: "Drive",
+    component: () => import("@/views/drive/index.vue"),
+    meta: {
+      title: "DeepOil 智慧经营平台 | 驾驶舱",
+    },
+  },
 ];
 
 const router = createRouter({

+ 212 - 0
src/views/drive/index.vue

@@ -0,0 +1,212 @@
+<template>
+  <div class="drive-page">
+    <Header />
+
+    <main class="drive-main">
+      <section class="drive-hero">
+        <p class="drive-eyebrow">Dashboard Portal</p>
+        <h1 class="drive-title">驾驶舱门户</h1>
+        <p class="drive-desc">选择一个驾驶舱,快速进入对应业务看板。</p>
+      </section>
+
+      <section class="drive-grid">
+        <button
+          v-for="card in driveCards"
+          :key="card.title"
+          type="button"
+          class="drive-card"
+          @click="openDrive(card.url)"
+        >
+          <div class="drive-card__icon">
+            <Icon :icon="card.icon" />
+          </div>
+          <div class="drive-card__content">
+            <h2 class="drive-card__title">{{ card.title }}</h2>
+            <p class="drive-card__text">{{ card.description }}</p>
+          </div>
+          <div class="drive-card__arrow">
+            <Icon icon="mdi:arrow-top-right" />
+          </div>
+        </button>
+      </section>
+    </main>
+
+    <Footer />
+  </div>
+</template>
+
+<script setup lang="ts">
+import Header from "@components/home/header.vue";
+import Footer from "@components/home/Footer.vue";
+import { Icon } from "@iconify/vue";
+
+type DriveCard = {
+  title: string;
+  description: string;
+  icon: string;
+  url: string;
+};
+
+const driveCards: DriveCard[] = [
+  {
+    title: "生产驾驶舱",
+    description: "查看生产运营态势、核心指标与执行情况。",
+    icon: "mdi:factory",
+    url: "http://172.21.10.42:1080/webroot/decision/v10/entry/access/dbc9cf73-81ce-43f1-9923-45cdfa5d5d3a?preview=true&page_number=1",
+  },
+  {
+    title: "财务驾驶舱",
+    description: "查看财务分析、预算执行与经营数据表现。",
+    icon: "mdi:finance",
+    url: "http://172.21.10.42:1080/webroot/decision/v10/entry/access/e836fb5b-092c-4d64-a324-3beeb4fac0cc?preview=true&page_number=1",
+  },
+];
+
+const openDrive = (url: string) => {
+  window.open(url, "_blank");
+};
+</script>
+
+<style scoped>
+.drive-page {
+  min-height: 100vh;
+  background:
+    radial-gradient(
+      circle at top left,
+      rgba(77, 138, 255, 0.16),
+      transparent 28%
+    ),
+    linear-gradient(180deg, #f4f8fc 0%, #eef3f9 100%);
+  color: #17345f;
+}
+
+.drive-main {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 132px 24px 72px;
+  height: 93vh;
+}
+
+.drive-hero {
+  margin-bottom: 40px;
+}
+
+.drive-eyebrow {
+  margin: 0 0 10px;
+  color: #4a78c2;
+  font-size: 13px;
+  font-weight: 700;
+  letter-spacing: 0.18em;
+  text-transform: uppercase;
+}
+
+.drive-title {
+  margin: 0;
+  color: #0f274a;
+  font-size: clamp(30px, 4vw, 42px);
+  font-weight: 700;
+}
+
+.drive-desc {
+  margin: 14px 0 0;
+  max-width: 560px;
+  color: #5e7391;
+  font-size: 16px;
+  line-height: 1.8;
+}
+
+.drive-grid {
+  display: grid;
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+  gap: 24px;
+}
+
+.drive-card {
+  display: flex;
+  align-items: center;
+  gap: 18px;
+  width: 100%;
+  padding: 28px 26px;
+  border: 1px solid rgba(118, 154, 210, 0.18);
+  border-radius: 24px;
+  background: rgba(255, 255, 255, 0.9);
+  box-shadow: 0 18px 40px rgba(33, 77, 145, 0.08);
+  text-align: left;
+  cursor: pointer;
+  transition:
+    transform 0.2s ease,
+    box-shadow 0.2s ease,
+    border-color 0.2s ease;
+}
+
+.drive-card:hover {
+  transform: translateY(-4px);
+  border-color: rgba(47, 111, 219, 0.36);
+  box-shadow: 0 24px 48px rgba(33, 77, 145, 0.14);
+}
+
+.drive-card__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 64px;
+  height: 64px;
+  border-radius: 18px;
+  background: linear-gradient(135deg, #2f6fdb 0%, #6ea2ff 100%);
+  color: #fff;
+  font-size: 30px;
+  flex-shrink: 0;
+  box-shadow: 0 14px 28px rgba(47, 111, 219, 0.24);
+}
+
+.drive-card__content {
+  min-width: 0;
+  flex: 1;
+}
+
+.drive-card__title {
+  margin: 0;
+  color: #17345f;
+  font-size: 24px;
+  font-weight: 700;
+}
+
+.drive-card__text {
+  margin: 10px 0 0;
+  color: #6c809d;
+  font-size: 14px;
+  line-height: 1.7;
+}
+
+.drive-card__arrow {
+  color: #7c93b6;
+  font-size: 26px;
+  flex-shrink: 0;
+  transition:
+    transform 0.2s ease,
+    color 0.2s ease;
+}
+
+.drive-card:hover .drive-card__arrow {
+  color: #2f6fdb;
+  transform: translate(2px, -2px);
+}
+
+@media (max-width: 768px) {
+  .drive-main {
+    padding: 112px 16px 56px;
+  }
+
+  .drive-grid {
+    grid-template-columns: 1fr;
+  }
+
+  .drive-card {
+    padding: 22px 18px;
+  }
+
+  .drive-card__title {
+    font-size: 20px;
+  }
+}
+</style>

+ 4 - 2
src/views/flow/crmDoneList.vue

@@ -2,7 +2,7 @@
   <div class="todo-list">
     <Header />
 
-    <div class="content mt-15">
+    <div class="content mt-15 max-w-[1200px] mx-auto">
       <div class="flex gap-5 items-center mb-4 bg-[#f9f9f9] py-2">
         <p class="flex items-center">
           <Icon
@@ -28,10 +28,12 @@
       </div>
       <div class="h-[80vh]">
         <el-table
+          v-loading="loading"
           :data="oaTasks"
           style="width: 100%"
           height="70vh"
-          :empty-text="loading ? '加载中...' : '暂无数据'"
+          element-loading-text="加载中..."
+          :empty-text="loading ? '' : '暂无数据'"
           :header-cell-style="{
             backgroundColor: '#e9f7ff',
             color: 'black',

+ 4 - 2
src/views/flow/crmTodoList.vue

@@ -2,7 +2,7 @@
   <div class="todo-list">
     <Header />
 
-    <div class="content mt-15">
+    <div class="content mt-15 max-w-[1200px] mx-auto">
       <div class="flex gap-5 items-center mb-4 bg-[#f9f9f9] py-2">
         <p class="flex items-center">
           <Icon
@@ -28,10 +28,12 @@
       </div>
       <div class="h-[80vh]">
         <el-table
+          v-loading="loading"
           :data="oaTasks"
           style="width: 100%"
           height="70vh"
-          :empty-text="loading ? '加载中...' : '暂无数据'"
+          element-loading-text="加载中..."
+          :empty-text="loading ? '' : '暂无数据'"
           :header-cell-style="{
             backgroundColor: '#e9f7ff',
             color: 'black',

+ 40 - 17
src/views/flow/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="ehr-page">
     <Header />
-    <section class="hero">
+    <section class="hero max-w-[1300px] mx-auto">
       <div class="hero-inner">
         <!-- 判断上下午 -->
         <h1 class="hero-title">
@@ -15,7 +15,7 @@
     </section>
 
     <!-- 任务统计 -->
-    <section class="total">
+    <section class="total max-w-[1300px] mx-auto">
       <div class="total-card" v-for="(item, index) in stats" :key="index">
         <el-popover
           placement="top"
@@ -64,7 +64,7 @@
       </div>
     </section>
 
-    <div class="content">
+    <div class="content max-w-[1300px] mx-auto">
       <div class="search-bar">
         <div class="search-input">
           <Icon icon="mdi:magnify" class="search-icon" />
@@ -196,7 +196,7 @@ import * as echarts from "echarts";
 import { useRouter } from "vue-router";
 import dd from "dingtalk-jsapi";
 const router = useRouter();
-
+import { ElLoading, ElMessage } from "element-plus";
 const userStore = useUserStore();
 
 const lineChartInstance = ref(null);
@@ -466,7 +466,7 @@ const displayedFlows = computed(() => {
   return (allTab.value.flowRespVOS || []).filter((item) =>
     String(item?.flowName || "")
       .toLowerCase()
-      .includes(keyword)
+      .includes(keyword),
   );
 });
 
@@ -493,34 +493,57 @@ const go = async (item) => {
       const res = await ssoLogin({
         username: userStore.getUser.username,
       });
-
       if (res) {
         const ua = window.navigator.userAgent.toLowerCase();
+
         if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
-          console.log(
-            "现在是钉钉。。。。。。。。。。。。。。。。。。。。。。。。。",
-          );
+          // 钉钉环境
+          const loading = ElLoading.service({
+            lock: true,
+            text: "正在跳转,请稍候...",
+            background: "rgba(0, 0, 0, 0.7)",
+          });
+
           const targetUrl1 = item.indexUrl + "?ssoToken=" + res + "#/main";
           const targetUrl2 = item.flowUrl;
+
           dd.biz.util.openLink({
-            url: targetUrl1, // 先跳你的 SSO 链接
+            url: targetUrl1,
             onSuccess: () => {
-              // 延迟跳目标业务地址(和你原来 setTimeout 逻辑一致)
               setTimeout(() => {
-                dd.biz.util.openLink({ url: targetUrl2 });
-              }, 100);
+                dd.biz.util.openLink({
+                  url: targetUrl2,
+                  onSuccess: () => {
+                    loading.close();
+                  },
+                  onFail: (err) => {
+                    loading.close();
+                    ElMessage.error("跳转失败,请重试");
+                  },
+                });
+              }, 2000);
             },
             onFail: (err) => {
-              console.log("钉钉err>>>>>>>>>>>>>>>>>>>>> ", err);
+              loading.close();
+              ElMessage.error("打开链接失败,请重试");
             },
           });
         } else {
-          const newTab = window.open("", "_blank");
+          // 浏览器环境
+          const loading = ElLoading.service({
+            lock: true,
+            text: "正在跳转,请稍候...",
+            background: "rgba(0, 0, 0, 0.7)",
+          });
 
+          const newTab = window.open("", "_blank");
           newTab.location.href = item.indexUrl + "?ssoToken=" + res + "#/main";
 
-          setTimeout(function () {
+          setTimeout(() => {
             newTab.location.href = item.flowUrl;
+            setTimeout(() => {
+              loading.close();
+            }, 500);
           }, 100);
         }
       }
@@ -726,7 +749,7 @@ onMounted(async () => {
           { name: "CRM", value: crmDoneRes.todoCount ?? 0 },
         ];
       },
-      5 * 60 * 1000,
+      10 * 60 * 1000,
     ); // 每5分钟刷新一次
   } else {
     statsLoading.value = false;

+ 15 - 9
src/views/flow/oaDoneList.vue

@@ -2,7 +2,7 @@
   <div class="todo-list">
     <Header />
 
-    <div class="content mt-15">
+    <div class="content mt-15 max-w-[1200px] mx-auto">
       <div class="flex gap-5 items-center mb-4 bg-[#f9f9f9] py-2">
         <p class="flex items-center">
           <Icon
@@ -26,16 +26,14 @@
           />&#36820;&#22238;</el-button
         >
       </div>
-      <div class="h-[90vh]">
+      <div class="h-[80vh]">
         <el-table
+          v-loading="loading"
           :data="oaTasks"
           style="width: 100%"
-          height="80vh"
-          :empty-text="
-            loading
-              ? '&#21152;&#36733;&#20013;...'
-              : '&#26242;&#26080;&#25968;&#25454;'
-          "
+          height="70vh"
+          element-loading-text="加载中..."
+          :empty-text="loading ? '' : '暂无数据'"
           :header-cell-style="{
             backgroundColor: '#e9f7ff',
             color: 'black',
@@ -172,7 +170,7 @@ import { ref, onMounted } from "vue";
 import { getOATasks, ssoLogin } from "@/api/user";
 import { useUserStore } from "@/stores/useUserStore";
 import { Icon } from "@iconify/vue";
-
+import { ElLoading, ElMessage } from "element-plus";
 import router from "@/router";
 const userStore = useUserStore();
 
@@ -241,6 +239,11 @@ const goBackPage = async (row) => {
         },
       });
     } 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" +
@@ -250,6 +253,9 @@ const goBackPage = async (row) => {
 
       setTimeout(function () {
         newTab.location.href = `https://yfoa.keruioil.com/spa/workflow/static4form/index.html?_rdm=1776063595284#/main/workflow/req?requestid=${row.requestId}`;
+        setTimeout(() => {
+          loading.close();
+        }, 500);
       }, 100);
     }
   }

+ 13 - 2
src/views/flow/todoList.vue

@@ -2,7 +2,7 @@
   <div class="todo-list">
     <Header />
 
-    <div class="content mt-15">
+    <div class="content mt-15 max-w-[1200px] mx-auto">
       <div class="flex gap-5 items-center mb-4 bg-[#f9f9f9] py-2">
         <p class="flex items-center">
           <Icon
@@ -28,9 +28,11 @@
       </div>
       <div class="h-[80vh]">
         <el-table
+          v-loading="loading"
           :data="oaTasks"
           style="width: 100%"
-          :empty-text="loading ? '加载中...' : '暂无数据'"
+          element-loading-text="加载中..."
+          :empty-text="loading ? '' : '暂无数据'"
           height="70vh"
           :header-cell-style="{
             backgroundColor: '#e9f7ff',
@@ -169,6 +171,7 @@ import { useUserStore } from "@/stores/useUserStore";
 import { Icon } from "@iconify/vue";
 import router from "@/router";
 const userStore = useUserStore();
+import { ElLoading, ElMessage } from "element-plus";
 
 const oaTasks = ref([]);
 const loading = ref(false);
@@ -203,6 +206,11 @@ const goBackPage = async (row) => {
         },
       });
     } 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" +
@@ -212,6 +220,9 @@ const goBackPage = async (row) => {
 
       setTimeout(function () {
         newTab.location.href = `https://yfoa.keruioil.com/spa/workflow/static4form/index.html?_rdm=1776063595284#/main/workflow/req?requestid=${row.requestId}`;
+        setTimeout(() => {
+          loading.close();
+        }, 500);
       }, 100);
     }
   }

+ 997 - 210
src/views/index.vue

@@ -1,186 +1,562 @@
 <template>
-  <div class="bg-white">
+  <div class="portal-home min-h-screen bg-[#eef3f9] text-[#17345f]">
     <Header />
 
-    <section
-      class="relative w-full overflow-hidden mt-15 md:pb-30 pb-50 pt-9 h-[400px]"
-    >
-      <!-- 轮播背景容器 -->
-
-      <div
-        class="absolute inset-0 flex transition-transform duration-700 ease-in-out"
-      >
-        <div class="min-w-full h-full relative">
-          <video
-            class="absolute inset-0 w-full h-full object-cover"
-            autoplay
-            muted
-            loop
-            playsinline
-            :src="bgvideo"
+    <main class="mx-auto max-w-[1200px] px-6 pb-8 pt-24">
+      <section class="hero-banner overflow-hidden rounded-[6px] relative">
+        <div class="">
+          <!-- 轮播容器 -->
+          <div class="carousel-container relative h-full">
+            <!-- 轮播项 -->
+            <div
+              class="carousel-slide absolute inset-0 transition-opacity duration-500 ease-in-out"
+              v-for="(slide, index) in slides"
+              :key="index"
+              v-show="currentIndex === index"
+              :class="{
+                'opacity-100': currentIndex === index,
+                'opacity-0': currentIndex !== index,
+                'z-10': currentIndex === index,
+                'z-0 pointer-events-none': currentIndex !== index,
+              }"
+            >
+              <div class="hero-copy carousel-caption">
+                <div class="hero-script text-[68px] font-italic text-[#0c4eb5]">
+                  HELLO
+                </div>
+                <p class="hero-text mt-6 text-[24px] text-[#5f6f83]">
+                  {{ slide.text }}
+                </p>
+              </div>
+
+              <!-- 轮播背景图 -->
+              <div class="hero-visual absolute inset-0">
+                <img
+                  :src="slide.image"
+                  alt="Banner"
+                  class="w-full h-full object-cover"
+                />
+              </div>
+            </div>
+
+            <!-- 指示器 -->
+            <div
+              class="carousel-indicators absolute bottom-4 left-1/2 z-30 flex transform -translate-x-1/2 space-x-2"
+            >
+              <button
+                v-for="(_, i) in slides"
+                :key="i"
+                @click="currentIndex = i"
+                :class="{
+                  'bg-[#0c4eb5]': currentIndex === i,
+                  'bg-[#d1ddea]': currentIndex !== i,
+                }"
+                class="w-3 h-3 rounded-full transition-colors"
+              ></button>
+            </div>
+          </div>
+        </div>
+      </section>
+
+      <section class="mt-3 grid gap-4 xl:grid-cols-[1.74fr_0.74fr]">
+        <div class="space-y-4">
+          <article
+            v-for="section in portalSections"
+            :key="section.code"
+            class="platform-block"
+            :style="{ minHeight: section.height }"
           >
-            您的浏览器不支持 HTML5 视频。
-          </video>
+            <div class="platform-block__header">
+              <div class="platform-block__title-wrap">
+                <p class="platform-block__title">{{ section.title }}</p>
+                <span class="platform-block__subtitle">{{
+                  section.subtitle
+                }}</span>
+              </div>
+              <span class="platform-block__watermark">{{ section.code }}</span>
+            </div>
+
+            <div
+              v-if="section.apps?.length"
+              class="grid grid-cols-2 gap-4 p-6 md:grid-cols-4"
+            >
+              <button
+                v-for="(app, appIndex) in section.apps"
+                :key="`${section.code}-${appIndex}-${app.label}`"
+                type="button"
+                :class="[
+                  'platform-app',
+                  boldLabes.includes(app.label)
+                    ? 'platform-app--active'
+                    : 'platform-app--ghost',
+                ]"
+                @click="handlePortalAppClick(app)"
+              >
+                <span class="platform-app__icon">
+                  <img
+                    v-if="app.image"
+                    :src="app.image"
+                    :alt="app.label"
+                    class="h-7 w-7 object-contain"
+                  />
+                  <Icon
+                    v-else
+                    :icon="app.icon || 'mdi:dots-grid'"
+                    class="text-[24px]"
+                  />
+                </span>
+                <span class="text-[#004098] text-sm">{{ app.label }}</span>
+              </button>
+            </div>
+          </article>
         </div>
-      </div>
-
-      <div
-        class="relative z-10 w-full mx-auto h-full px-20 flex items-center mt-5 pt-10"
-      >
-        <div class="max-w-4xl">
-          <h1
-            class="text-2xl md:text-4xl text-white/90 pt-2 font-bold leading-tight slide-up-fade-in"
+
+        <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>
+              <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">
+              <article
+                v-for="(notice, noticeIndex) in notices"
+                :key="`${notice.title}-${noticeIndex}`"
+                class="notice-item cursor-pointer"
+              >
+                <div class="notice-item__desc">{{ notice.desc }}</div>
+              </article>
+            </div>
+          </section>
+
+          <section
+            class="side-card side-card--notice rounded-md p-2"
+            :style="{ minHeight: '190px' }"
           >
-            山东科瑞石油技术门户网站 · DeepOil 智慧经营平台
-          </h1>
-          <p
-            class="mt-3 md:text-4 text-sm md:text-base text-white/90 leading-relaxed slide-up-fade-in"
-            style="animation-delay: 0.2s"
+            <div class="side-card__header w-[91%] ml-[15px]">
+              <div class="notice-badge px-2">{{ todoPanelTitle }}</div>
+              <button type="button" class="side-card__more">全部任务</button>
+            </div>
+            <div class="space-y-2 p-4 pt-2">
+              <article
+                v-for="task in todoTasks"
+                :key="task.title"
+                class="todo-item rounded-md"
+              >
+                <div class="flex min-w-0 items-start justify-between gap-3">
+                  <div class="min-w-0">
+                    <div
+                      class="truncate text-[14px] font-semibold text-[#0d4a9d]"
+                    >
+                      {{ task.title }}
+                    </div>
+                    <div class="mt-1 text-[12px] text-[#8a9ab0]">
+                      {{ task.meta }}
+                    </div>
+                  </div>
+                  <span :class="['todo-item__tag', task.tagClass]">{{
+                    task.tag
+                  }}</span>
+                </div>
+              </article>
+            </div>
+          </section>
+
+          <section
+            class="side-card side-card--content p-2 rounded-md"
+            :style="{ minHeight: '180px' }"
           >
-            连接经营管理、生产指挥与数据智能,打造石油能源行业的一体化数字化平台,
-            为企业提供高效协同、智能决策与可靠的数字化服务。
-          </p>
-          <div
-            class="mt-4 md:mt-6 flex gap-3 slide-up-fade-in"
-            style="animation-delay: 0.4s"
+            <div class="side-card__header w-[90%] ml-[15px]">
+              <div class="notice-badge">{{ newsPanelTitle }}</div>
+              <button type="button" class="side-card__more">
+                <Icon icon="mdi:dots-horizontal" class="text-[22px]" />
+              </button>
+            </div>
+            <div class="space-y-2 p-4 pt-2">
+              <article
+                v-for="news in newsList"
+                :key="news.title"
+                class="news-mini cursor-pointer rounded-md"
+              >
+                <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]"
+                  >
+                    {{ news.title }}
+                  </div>
+                  <div class="mt-2 text-[12px] text-[#9cadc0]">
+                    {{ news.date }}
+                  </div>
+                </div>
+              </article>
+            </div>
+          </section>
+
+          <section
+            class="side-card side-card--placeholder rounded-md"
+            :style="{ minHeight: '78px', backgroundColor: '#3575e4' }"
           >
-            <a
-              class="bg-[#0644a1] hover:bg-[#0a53c1] text-white px-4 py-2 cursor-pointer"
-            >
-              了解平台
-            </a>
-            <a
-              class="bg-white/10 hover:bg-white/20 text-white px-4 py-2 cursor-pointer"
-            >
-              联系我们
-            </a>
-          </div>
-        </div>
-      </div>
-    </section>
-
-    <section
-      class="max-w-[1200px] mx-auto px-2 mb-8 mt-6 flex justify-center items-center gap-3"
-    >
-      <p class="text-2xl">提供多维度、更可靠的数智化服务</p>
-    </section>
-
-    <section class="w-full mx-auto px-4 md:px-20 mb-8 mt-5">
-      <div class="grid gap-5 grid-cols-1 md:grid-cols-2 xl:grid-cols-3">
-        <CardItem
-          v-for="card in cards"
-          :title="card.title"
-          :id="card.id"
-          :desc="card.desc"
-          :items="card.items"
-          :bg="card.bg"
-          :bg2="cardBgs"
-        />
-      </div>
-    </section>
+            <div class="placeholder-panel flex flex-col text-left">
+              <div class="text-sm">需要帮助?</div>
+              <div class="text-[10px]">
+                遇到系统操作问题?我们的7 x 24 小时AI助手随时待命。
+              </div>
+              <div
+                class="bg-white text-sm text-center text-[#004098] py-1 px-2 rounded-full mt-2 w-[30%] cursor-pointer"
+              >
+                立即咨询
+              </div>
+            </div>
+          </section>
+        </aside>
+      </section>
+    </main>
 
     <Footer />
   </div>
 </template>
 
 <script setup lang="ts">
-import { onMounted, ref, onUnmounted } from "vue";
+import { onMounted, onUnmounted, ref } from "vue";
 import * as authUtil from "@/utils/auth";
 import * as dd from "dingtalk-jsapi";
-import { getUnreadNotifyMessageCount } from "@/api/user";
 import Header from "@components/home/header.vue";
-import CardItem from "@components/home/CardItem.vue";
 import Footer from "@components/home/Footer.vue";
-import img1 from "@/assets/images/1.jpg";
-import img2 from "@/assets/images/2.jpg";
-import img3 from "@/assets/images/3.jpg";
-import caiwu from "@/assets/images/caiwu.png";
-import banner1 from "@/assets/images/banner1.jpg";
-import banner2 from "@/assets/images/model.jpeg";
-import bgvideo from "@/assets/bg.mp4";
-import bg2 from "@/assets/images/e4.png";
-import g1 from "@/assets/images/g1.png";
 import { useRouter } from "vue-router";
 import axios from "axios";
+import { Icon } from "@iconify/vue";
+import { getMCSsoToken, ssoLogin, zentaoSsoLogin } from "@/api/user";
 import { useUserStore } from "@/stores/useUserStore";
-const router = useRouter();
+import { getAccessToken } from "@/utils/auth";
+import banner1 from "@/assets/images/banner1.png";
+import banner2 from "@/assets/images/banner2.jpg";
+import banner3 from "@/assets/images/banner3.jpg";
+import img3 from "@/assets/images/3.jpg";
+import oaimage from "@/assets/images/oa.jpg";
+import crmimage from "@/assets/images/crm.jpg";
+import ehrimage from "@/assets/images/ehr.jpg";
+import scmimage from "@/assets/images/scm.png";
+import erpimage from "@/assets/images/fm.jpg";
+import driveimage from "@/assets/images/drive.jpg";
+import pmsimage from "@/assets/images/pms.jpeg";
+import zhonghangimage from "@/assets/images/中航.png";
+import lianyouimage from "@/assets/images/lianyou.jpeg";
+import qhseimage from "@/assets/images/qhse.jpg";
+import zuanjingimage from "@/assets/images/zuanjing.jpeg";
+import yalieimage from "@/assets/images/yalie.png";
+import zhuqiimage from "@/assets/images/zhuqi.png";
+import pmimage from "@/assets/images/pm.jpg";
+import dataimage from "@/assets/images/data.jpg";
+import thinkimage from "@/assets/images/think.jpg";
+import aiimage from "@/assets/images/ai.png";
+import agentimage from "@/assets/images/agent.jpeg";
+import videoimage from "@/assets/images/video.png";
+import fileagent from "@/assets/images/fileagent.png";
+import zhiduagent from "@/assets/images/zhiduagent.png";
+import jishuimage2 from "@/assets/images/jishuimage.png";
+import zhanlueimage from "@/assets/images/zhanlue.jpg"; // 战略解码
+import safeimage from "@/assets/images/safe.png"; // 安全合规管理
+import zuzhiimage from "@/assets/images/zuzhi.jpg";
+import youimage from "@/assets/images/youcnag.png"; // ai智能体
 
-const userStore = useUserStore();
-const bgImages = [banner1, caiwu, banner2];
-// 2. 定义当前显示的图片索引
-const currentBgIndex = ref(0);
+type PortalApp = {
+  label: string;
+  icon?: string;
+  image?: string;
+  active?: boolean;
+};
 
-// 3. 轮播定时器引用
-let bgInterval: number | null = null;
+type PortalSection = {
+  code: string;
+  title: string;
+  subtitle: string;
+  height: string;
+  apps?: PortalApp[];
+};
 
-type Card = {
-  name: string;
+type NoticeItem = {
   title: string;
   desc: string;
-  bg: string;
-  items: Array<{ label: string; tag?: "新" | "热" }>;
-  id: string;
 };
-const cardBgs = [bg2, g1, bg2];
-const cards: Card[] = [
+
+type SidePanel = {
+  title: string;
+  height: string;
+};
+
+type TodoTask = {
+  title: string;
+  meta: string;
+  tag: string;
+  tagClass: string;
+};
+
+type NewsItem = {
+  title: string;
+  date: string;
+  image: string;
+};
+
+const router = useRouter();
+const userStore = useUserStore();
+const heroGreeting = "早上好,保持热爱,奔赴目标!";
+const noticeLabel = "公告";
+const todoPanelTitle = "待办中心";
+const newsPanelTitle = "新闻";
+
+const portalSections: PortalSection[] = [
   {
-    name: "device",
-    title: "数字运营平台",
-    desc: "集成财务、研发、客户、人力等关键业务数据,为管理者提供全局态势感知和战略决策支持",
-    id: "management",
-    bg: img1,
-    items: [
-      { label: "OA办公" },
-      { label: "客户管理(CRM)" },
-      { label: "人力资源(EHR)" },
-      { label: "供应商管理(SRM)" },
-      { label: "财务管理(FM)" },
-      { label: "经营驾驶舱(MC)", tag: "新" },
-      { label: "项目管理(PM)" },
-
-      { label: "技术研发管理" },
-
-      { label: "组织资产管理" },
-      { label: "风控合规管理" },
-      { label: "战略解码与执行" },
-      { label: "研发需求管理" },
+    code: "数",
+    title: "数字化运营平台",
+    subtitle: "高效协同 · 战略洞察",
+    height: "220px",
+    apps: [
+      {
+        label: "OA办公",
+        image: oaimage,
+        active: true,
+      },
+      { label: "客户管理(CRM)", image: crmimage },
+      { label: "人力资源(EHR)", image: ehrimage },
+      { label: "供应商管理(SRM)", image: scmimage },
+      { label: "财务管理(FM)", image: erpimage },
+      { label: "经营驾驶舱(MC)", image: driveimage },
+      { label: "项目管理(PM)", image: pmimage },
+      { label: "技术研发管理", image: jishuimage2 },
+      { label: "战略解码与执行", image: zhanlueimage },
+      { label: "组织资产管理", image: zuzhiimage },
+      { label: "风控合规管理", image: safeimage },
+      { label: "研发需求管理", image: jishuimage2 },
     ],
   },
   {
-    name: "scene",
+    code: "智",
     title: "智慧指挥平台",
-    desc: "深入工业场景,为中小企业数字化升级赋能",
-    id: "command",
-    bg: img2,
-    items: [
-      { label: "设备管理(PMS)" },
-      { label: "中航北斗" },
-      { label: "质量安全管理(QHSE)" },
-      { label: "智慧连油" },
-      { label: "智慧注气" },
-      { label: "智能钻井" },
-      { label: "智慧压裂" },
-      { label: "数字油藏" },
-      { label: "视频中心(VCS)" },
+    subtitle: "高效协同 · 战略洞察",
+    height: "210px",
+    apps: [
+      { label: "设备管理(PMS)", image: pmsimage, active: true },
+      { label: "中航北斗", image: zhonghangimage },
+      { label: "质量安全管理(QHSE)", image: qhseimage },
+      { label: "智慧连油", image: lianyouimage },
+      { label: "智慧注气", image: zhuqiimage },
+      { label: "智能钻井", image: zuanjingimage },
+      { label: "智慧压裂", image: yalieimage },
+      { label: "数字油藏", image: youimage },
+      { label: "视频中心(VCS)", image: videoimage },
     ],
   },
   {
-    name: "factory",
+    code: "CB",
     title: "Chat BI平台",
-    desc: "工厂上云,高效协同,生产效率和产品质量提升",
-    id: "chatbi",
-    bg: img3,
-    items: [
-      { label: "全局数据治理(数据中台)", tag: "热" },
-      { label: "智能决策" },
-      { label: "行业AI大模型", tag: "新" },
-      { label: "AI智能体(智能交互)", tag: "新" },
-      { label: "工艺文件智能体" },
-      { label: "集团制度智能体" },
+    subtitle: "高效协同 · 战略洞察",
+    height: "160px",
+    apps: [
+      { label: "全局数据治理(数据中台)", image: dataimage, active: true },
+      { label: "智能决策", image: thinkimage },
+      { label: "行业AI大模型", image: aiimage },
+      { label: "AI智能体(智能交互)", image: agentimage },
+      { label: "工艺文件智能体", image: fileagent },
+      { label: "集团制度智能体", image: zhiduagent },
     ],
   },
 ];
 
+let boldLabes = ref([
+  "OA办公",
+  "客户管理(CRM)",
+  "设备管理(PMS)",
+  "中航北斗",
+  "智慧连油",
+  "质量安全管理(QHSE)",
+]);
+
+// 添加轮播数据
+const slides = ref([
+  {
+    image: banner1,
+    text: "早上好,保持热爱,奔赴目标!",
+  },
+  {
+    image: banner2,
+    text: "数字化转型,驱动未来增长",
+  },
+  {
+    image: banner3,
+    text: "智慧平台,赋能高效协同",
+  },
+]);
+
+// 当前索引
+const currentIndex = ref(0);
+let slideTimer: ReturnType<typeof setInterval> | null = null;
+
+// 下一页
+const nextSlide = () => {
+  currentIndex.value = (currentIndex.value + 1) % slides.value.length;
+};
+
+// 上一页
+const prevSlide = () => {
+  currentIndex.value =
+    (currentIndex.value - 1 + slides.value.length) % slides.value.length;
+};
+
+const notices: NoticeItem[] = [
+  {
+    title: "集团总部关于办公楼设备维保的说明",
+    desc: "关于 2026 年度“DeepOil 数字化转型”优秀项目评选活动的通知",
+  },
+  {
+    title: "集团总部关于办公楼设备维保的说明",
+    desc: "关于 2026 年度“DeepOil 数字化转型”优秀项目评选活动的通知",
+  },
+];
+
+const todoTasks: TodoTask[] = [
+  {
+    title: "项目立项审批 - QHSE 升级",
+    meta: "发起人:李智慧 · 2小时前",
+    tag: "紧急",
+    tagClass: "bg-[#ffe1e3] text-[#ff5d66]",
+  },
+  {
+    title: "合同会签 - 油服工程中心",
+    meta: "发起人:王志刚 · 4小时前",
+    tag: "普通",
+    tagClass: "bg-[#dbe8ff] text-[#3f74ff]",
+  },
+  {
+    title: "报销单待审批",
+    meta: "来自:财务管理系统 · 昨天",
+    tag: "待办",
+    tagClass: "bg-[#dfe8f3] text-[#6f7f94]",
+  },
+];
+
+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 sidePanels: SidePanel[] = [];
+
+const protectedOpen = (url: string) => {
+  if (userStore.getUser.username && getAccessToken()) {
+    window.open(url, "_blank");
+  } else {
+    router.push({ path: "/login" });
+  }
+};
+
+const handlePortalAppClick = async (app: PortalApp) => {
+  if (!app.label) return;
+
+  if (app.label === "OA办公") {
+    if (userStore.getUser.username && getAccessToken()) {
+      const res = await ssoLogin({ username: userStore.getUser.username });
+      if (res) {
+        window.open(
+          `https://yfoa.keruioil.com/wui/index.html?ssoToken=${res}#/main`,
+          "_blank",
+        );
+      }
+    } else {
+      router.push({ path: "/login" });
+    }
+  }
+
+  if (app.label === "客户管理(CRM)") {
+    protectedOpen(
+      `https://crm-tencent.xiaoshouyi.com/global/sso/callback/00APEB9EEEA9B2E338B686B7ECFA8585808C.action?token=${getAccessToken()}`,
+    );
+  }
+
+  if (app.label === "设备管理(PMS)") {
+    protectedOpen(
+      `${import.meta.env.VITE_PMS_URL}/portalLogin?username=${userStore.getUser.username}`,
+    );
+  }
+
+  if (app.label === "中航北斗") {
+    protectedOpen("https://zhbdgps.cn");
+  }
+
+  if (app.label === "智慧连油") {
+    protectedOpen(
+      `${import.meta.env.VITE_PMS_URL}/portalLogin?username=${userStore.getUser.username}&source=zhly`,
+    );
+  }
+
+  if (app.label === "智慧注气") {
+    protectedOpen(
+      `${import.meta.env.VITE_PMS_URL}/portalLogin?username=${userStore.getUser.username}&source=znzq`,
+    );
+  }
+
+  if (app.label === "视频中心(VCS)") {
+    protectedOpen(
+      `${import.meta.env.VITE_PMS_URL}/portalLogin?username=${userStore.getUser.username}&source=spzx`,
+    );
+  }
+
+  if (app.label === "质量安全管理(QHSE)") {
+    protectedOpen(
+      `${import.meta.env.VITE_PMS_URL}/portalLogin?username=${userStore.getUser.username}&source=qhse`,
+    );
+  }
+
+  if (app.label === "经营驾驶舱(MC)") {
+    if (userStore.getUser.username && getAccessToken()) {
+      const res = await getMCSsoToken();
+      if (res) {
+        window.open(
+          `https://report.deepoil.cc/webroot/decision/v10/entry/access/9fb42908-894a-4373-a6be-ce046a42851d?preview=true&page_number=1&ssoToken=${res}`,
+          "_blank",
+        );
+      }
+    } else {
+      router.push({ path: "/login" });
+    }
+  }
+
+  if (app.label === "研发需求管理") {
+    if (userStore.getUser.username && getAccessToken()) {
+      const res = await zentaoSsoLogin({
+        username: userStore.getUser.username,
+      });
+      if (res) {
+        window.open(
+          `http://project.deepoil.cc/zentao/api.php?m=user&f=apilogin&account=${res.jobNumber}&code=${res.code}&time=${res.timestamp}&token=${res.token}`,
+          "_blank",
+        );
+      }
+    } else {
+      router.push({ path: "/login" });
+    }
+  }
+};
+
 async function loginWithDingTalk() {
   const ddCorpId = import.meta.env.VITE_DD_CORPID;
   const ddClientId = import.meta.env.VITE_DD_CLIENTID;
@@ -197,7 +573,7 @@ async function loginWithDingTalk() {
         .post(
           import.meta.env.BASE_URL + "/admin-api/system/auth/h5SocialLogin",
           {
-            code: code,
+            code,
             type: 20,
             state: new Date().getTime(),
           },
@@ -208,11 +584,8 @@ async function loginWithDingTalk() {
             },
           },
         )
-        .then((res) => {
-          console.log("钉钉免登res>>>>>>>>>>>>>>>>>>", res.data.data);
-
-          authUtil.setToken(res.data.data);
-
+        .then((response) => {
+          authUtil.setToken(response.data.data);
           router.push({
             path: "/login",
           });
@@ -221,103 +594,517 @@ async function loginWithDingTalk() {
     fail: (err: any) => {
       console.log("err :>> ", err);
     },
-    complete: () => {
-      console.log("11 :>> ", 11);
-    },
   });
 }
 
 function dingTalkAutoLogin() {
   const ua = window.navigator.userAgent.toLowerCase();
 
-  console.log("ua>>>>>>>>>>>>>>>>>>>>>>> ", ua);
-
   if (ua.includes("dingtalk") || ua.includes("dingtalkwork")) {
     loginWithDingTalk();
   }
 }
 
-const unreadCount = ref(0); // 未读消息数量
-// 获得未读消息数
-const getUnreadCount = async () => {
-  getUnreadNotifyMessageCount().then((data) => {
-    unreadCount.value = data;
-  });
-};
-
 onMounted(() => {
   dingTalkAutoLogin();
-
-  // 首次加载小红点
-  // getUnreadCount();
-  // 轮询刷新小红点
-  // setInterval(
-  //   () => {
-  //     if (userStore.getIsSetUser) {
-  //       console.log("轮询刷新小红点");
-  //       getUnreadCount();
-  //     } else {
-  //       unreadCount.value = 0;
-  //     }
-  //   },
-  //   1000 * 60 * 1,
-  // );
+  slideTimer = setInterval(nextSlide, 5000);
 });
 
 onUnmounted(() => {
-  if (bgInterval) {
-    clearInterval(bgInterval);
+  if (slideTimer) {
+    clearInterval(slideTimer);
+    slideTimer = null;
   }
 });
 </script>
 
 <style scoped>
-/* 修改垂直滚动条 */
-::-webkit-scrollbar {
-  width: 5px; /* 修改宽度 */
+.portal-home {
+  --portal-blue: #2f6fdb;
+  --portal-blue-dark: #1b57bc;
+  --portal-blue-soft: #d7e7ff;
+  --portal-panel: #deebfb;
+  --portal-panel-soft: #edf5ff;
+  --portal-line: #a8c4f2;
+  background-color: #eef3f9;
+  background-image: url("../assets//images/bg666.png");
+  background-position: center bottom;
+  background-repeat: no-repeat;
+  background-size: 100% auto;
+  background-attachment: fixed;
 }
-::-webkit-scrollbar-thumb {
-  border-radius: 5px;
+
+.hero-banner {
+  position: relative;
+  min-height: 220px;
+  background: linear-gradient(
+    90deg,
+    rgba(255, 255, 255, 0.96) 0%,
+    rgba(241, 247, 255, 0.92) 36%,
+    rgba(223, 237, 255, 0.95) 100%
+  );
+  box-shadow: 0 16px 34px rgba(59, 112, 190, 0.08);
 }
 
-/* 修改滚动条轨道背景色 */
-::-webkit-scrollbar-track {
-  background-color: transparent;
+.hero-banner::before {
+  content: "";
+  position: absolute;
+  inset: 0;
+  background:
+    linear-gradient(140deg, rgba(206, 223, 247, 0.25) 0%, transparent 28%),
+    radial-gradient(
+      circle at 74% 24%,
+      rgba(83, 146, 255, 0.14),
+      transparent 18%
+    ),
+    radial-gradient(
+      circle at 88% 16%,
+      rgba(255, 255, 255, 0.94),
+      transparent 14%
+    );
+  pointer-events: none;
 }
 
-/* 修改滚动条滑块颜色 */
-::-webkit-scrollbar-thumb {
-  background-color: #0a5f73;
+.hero-banner__inner {
+  position: relative;
+  z-index: 1;
+  display: grid;
+  min-height: 220px;
+  grid-template-columns: minmax(260px, 1fr) minmax(360px, 1.3fr);
+  align-items: center;
+  gap: 24px;
+  padding: 22px 36px 18px 58px;
 }
 
-/* 修改滚动条滑块悬停时的颜色 */
-::-webkit-scrollbar-thumb:hover {
-  background-color: #30459c;
+.hero-copy {
+  /* padding-top: 10px; */
 }
 
-.slide-up-fade-in {
-  opacity: 0;
-  transform: translateY(20px);
-  animation: slideUpFadeIn 0.8s forwards ease-out;
+.hero-script {
+  color: #0c4eb5;
+  font-size: 68px;
+  font-style: italic;
+  font-weight: 500;
+  letter-spacing: 0.04em;
+  line-height: 1;
+  font-family: "Comic Sans MS", "Segoe Script", cursive;
 }
 
-@keyframes slideUpFadeIn {
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
+.hero-text {
+  margin-top: 18px;
+  color: #5f6f83;
+  font-size: 24px;
+  letter-spacing: 0.1em;
+}
+
+.hero-progress {
+  margin-top: 48px;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.hero-progress__active {
+  height: 4px;
+  width: 56px;
+  border-radius: 999px;
+  background: #1f67d1;
+}
+
+.hero-progress__line {
+  height: 3px;
+  width: 64px;
+  border-radius: 999px;
+  background: rgba(49, 113, 207, 0.18);
+}
+
+.hero-visual {
+  position: relative;
+  min-height: 180px;
+}
+
+.hero-orb,
+.hero-cube,
+.hero-ring {
+  position: absolute;
+}
+
+.hero-orb {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  border-radius: 28px;
+  background: linear-gradient(
+    180deg,
+    rgba(255, 255, 255, 0.98),
+    rgba(226, 239, 255, 0.95)
+  );
+  color: #65a4ff;
+  box-shadow:
+    0 18px 35px rgba(65, 122, 206, 0.14),
+    inset 0 1px 0 rgba(255, 255, 255, 0.88);
+}
+
+.hero-orb::after {
+  content: "";
+  position: absolute;
+  inset: auto 16px 10px;
+  height: 10px;
+  border-radius: 999px;
+  background: rgba(93, 154, 255, 0.12);
+  filter: blur(8px);
+}
+
+.hero-orb--cloud {
+  left: 10%;
+  top: 28px;
+  height: 106px;
+  width: 126px;
+  transform: rotate(-10deg);
+}
+
+.hero-orb--shield {
+  left: 46%;
+  top: 64px;
+  height: 118px;
+  width: 120px;
+  transform: rotate(8deg);
+}
+
+.hero-orb--chart {
+  right: 4%;
+  top: 0;
+  height: 138px;
+  width: 150px;
+  transform: rotate(6deg);
+}
+
+.hero-cube {
+  background: linear-gradient(
+    180deg,
+    rgba(255, 255, 255, 0.94),
+    rgba(226, 239, 255, 0.9)
+  );
+  box-shadow: 0 18px 35px rgba(65, 122, 206, 0.1);
+}
+
+.hero-cube--one {
+  left: 26%;
+  top: -2px;
+  height: 46px;
+  width: 46px;
+  transform: rotate(36deg);
+}
+
+.hero-cube--two {
+  left: 33%;
+  top: 18px;
+  height: 34px;
+  width: 34px;
+  transform: rotate(32deg);
+}
+
+.hero-ring {
+  left: 13%;
+  top: 108px;
+  height: 42px;
+  width: 128px;
+  border-radius: 999px;
+  background: rgba(83, 146, 255, 0.1);
+  box-shadow: inset 0 0 0 10px rgba(238, 245, 255, 0.72);
+}
+
+.platform-block {
+  overflow: hidden;
+  background: linear-gradient(180deg, #dce9fb 0%, #d9e8fb 100%);
+  box-shadow: 0 12px 28px rgba(58, 110, 187, 0.08);
+}
+
+.platform-block__header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 44px;
+  padding: 0 24px 0 34px;
+  background: linear-gradient(90deg, #316fd8 0%, #82aef0 100%);
+}
+
+.platform-block__title-wrap {
+  display: flex;
+  align-items: baseline;
+  gap: 28px;
+}
+
+.platform-block__title {
+  color: #fff;
+  font-size: 18px;
+
+  letter-spacing: 0.03em;
+}
+
+.platform-block__subtitle {
+  color: rgba(255, 255, 255, 0.95);
+  font-size: 13px;
+  font-weight: 600;
+}
+
+.platform-block__watermark {
+  color: rgba(255, 255, 255, 0.4);
+  font-size: 44px;
+  font-weight: 800;
+  line-height: 1;
+  letter-spacing: 0.04em;
+}
+
+.platform-app {
+  display: flex;
+  min-height: 34px;
+  align-items: center;
+  justify-content: flex-start;
+  gap: 12px;
+  border-radius: 8px;
+  padding: 0 18px;
+  font-size: 14px;
+  cursor: pointer;
+
+  transition:
+    transform 0.2s ease,
+    box-shadow 0.2s ease,
+    background 0.2s ease;
+}
+
+.platform-app:hover {
+  transform: translateY(-1px);
+}
+
+.platform-app--active {
+  background: rgba(255, 255, 255, 0.66);
+  font-weight: bold;
+}
+
+.platform-app--ghost {
+  background: rgba(255, 255, 255, 0.66);
+  color: #53719b;
+}
+
+.platform-app__icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  color: currentColor;
+}
+
+.side-card {
+  overflow: hidden;
+  background: #e8f1f8;
+  /* box-shadow: 0 12px 28px rgba(58, 110, 187, 0.06); */
+}
+
+.side-card--content {
+  padding-top: 10px;
+}
+
+.side-card__header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 20px;
+  padding-right: 10px;
+
+  border-radius: 100px;
+  background: #d5e6fc;
+}
+
+.notice-badge {
+  position: relative;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  height: 25px;
+  min-width: 54px;
+  /* padding: 0 5px 0 10px; */
+  background: #004098;
+  color: #fff;
+  font-size: 14px;
+  font-weight: bold;
+  border-radius: 100px;
+}
+
+.notice-badge::after {
+  content: "";
+  position: absolute;
+  top: 0;
+  right: -15px;
+}
+
+.side-card__more {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  border: 0;
+  background: transparent;
+  color: #0f53b4;
+  cursor: pointer;
+  font-size: 12px;
+}
+
+.notice-item {
+  background: #fff;
+  padding: 8px 14px;
+  box-shadow: 0 8px 22px rgba(63, 107, 169, 0.06);
+  border-radius: 10px;
+}
+
+.notice-item__title,
+.notice-item__desc {
+  color: #507698;
+  font-size: 13px;
+  line-height: 1.5;
+}
+
+.panel-title {
+  padding: 0 16px;
+  color: #2f4d79;
+  font-size: 15px;
+  font-weight: 700;
+}
+
+.todo-item {
+  background: rgba(255, 255, 255, 0.88);
+  padding: 8px 14px;
+  box-shadow: 0 8px 22px rgba(63, 107, 169, 0.05);
+}
+
+.todo-item__tag {
+  display: inline-flex;
+  align-items: center;
+  border-radius: 999px;
+  padding: 2px 8px;
+  font-size: 11px;
+  font-weight: 600;
+  white-space: nowrap;
+}
+
+.news-mini {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  background: rgba(255, 255, 255, 0.88);
+  padding: 5px 12px;
+  box-shadow: 0 8px 22px rgba(63, 107, 169, 0.05);
+  border: none;
+}
+
+.placeholder-panel {
+  display: flex;
+  height: 100%;
+  /* align-items: center; */
+  padding: 16px;
+  justify-content: start;
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+  letter-spacing: 0.08em;
 }
 
-.slide-up-fade-in {
-  opacity: 0;
-  transform: translateY(20px);
-  animation: slideUpFadeIn 0.8s forwards ease-out;
+@media (max-width: 1279px) {
+  .hero-banner__inner {
+    grid-template-columns: 1fr;
+    padding: 28px 28px 20px;
+  }
+
+  .hero-script {
+    font-size: 52px;
+  }
+
+  .hero-text {
+    font-size: 18px;
+  }
+
+  .hero-progress {
+    margin-top: 28px;
+  }
+
+  .hero-visual {
+    min-height: 220px;
+  }
 }
 
-@keyframes slideUpFadeIn {
-  to {
-    opacity: 1;
-    transform: translateY(0);
+@media (max-width: 767px) {
+  .platform-block__header {
+    padding: 0 16px 0 18px;
+  }
+
+  .platform-block__title-wrap {
+    gap: 12px;
+    flex-wrap: wrap;
+  }
+
+  .platform-block__title {
+    font-size: 16px;
+  }
+
+  .platform-block__subtitle {
+    font-size: 12px;
+  }
+
+  .platform-block__watermark {
+    font-size: 34px;
+  }
+
+  .hero-banner__inner {
+    padding: 24px 18px 20px;
+  }
+
+  .hero-script {
+    font-size: 44px;
   }
+
+  .hero-progress {
+    margin-top: 22px;
+  }
+}
+
+.carousel-container {
+  position: relative;
+  height: 220px;
+  overflow: hidden;
+}
+
+.carousel-slide {
+  position: relative;
+  background-size: cover;
+  background-position: center;
+}
+
+.hero-visual img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.carousel-caption {
+  position: absolute;
+  left: 40px;
+  top: 40%;
+  z-index: 2;
+  transform: translateY(-50%);
+  text-align: left;
+}
+
+.carousel-indicators {
+  display: flex;
+  gap: 8px;
+  z-index: 30;
+}
+
+.carousel-indicators button {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.8);
 }
 </style>