Parcourir la source

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

yanghao il y a 2 semaines
Parent
commit
7e55859781

+ 2 - 0
.env

@@ -2,3 +2,5 @@ VITE_APP_TITLE='科瑞石油技术门户网站'
 
 VITE_DINGTALK_APP_ID='dingmr9ez0ecgbmscfeb'
 
+VITE_DD_CORPID='dingc0dfd4cc2e69187d35c2f4657eb6378f'
+VITE_DD_CLIENTID='dingh3nuuagvbkahgvjh'

+ 1 - 1
auto-imports.d.ts

@@ -6,5 +6,5 @@
 // biome-ignore lint: disable
 export {}
 declare global {
-
+  const ElLoading: typeof import('element-plus/es').ElLoading
 }

+ 3 - 0
components.d.ts

@@ -22,6 +22,9 @@ declare module 'vue' {
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
+    ElTag: typeof import('element-plus/es')['ElTag']
     Footer: typeof import('./src/components/home/Footer.vue')['default']
     Header: typeof import('./src/components/home/header.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
     "@iconify/vue": "^5.0.0",
     "@types/qs": "^6.14.0",
     "axios": "^1.13.1",
+    "dingtalk-jsapi": "^3.2.5",
     "element-plus": "^2.11.7",
     "jsencrypt": "^3.5.4",
     "motion-v": "^1.7.4",

+ 15 - 0
pnpm-lock.yaml

@@ -20,6 +20,9 @@ importers:
       axios:
         specifier: ^1.13.1
         version: 1.13.1
+      dingtalk-jsapi:
+        specifier: ^3.2.5
+        version: 3.2.5
       element-plus:
         specifier: ^2.11.7
         version: 2.11.7(vue@3.5.22(typescript@5.9.3))
@@ -914,6 +917,9 @@ packages:
     resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
     engines: {node: '>=8'}
 
+  dingtalk-jsapi@3.2.5:
+    resolution: {integrity: sha512-GHtDTmilJQhr07GNarjlzhvgUkPWc0+52zbN2ToW+JzkydaOwmhiJCTO42+BI+onAlhdfLUbtUnGsjQNDTrM1w==}
+
   dunder-proto@1.0.1:
     resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
     engines: {node: '>= 0.4'}
@@ -1358,6 +1364,9 @@ packages:
     resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
     engines: {node: ^10 || ^12 || >=14}
 
+  promise-polyfill@7.1.2:
+    resolution: {integrity: sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==}
+
   proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
 
@@ -2426,6 +2435,10 @@ snapshots:
 
   detect-libc@2.1.2: {}
 
+  dingtalk-jsapi@3.2.5:
+    dependencies:
+      promise-polyfill: 7.1.2
+
   dunder-proto@1.0.1:
     dependencies:
       call-bind-apply-helpers: 1.0.2
@@ -2817,6 +2830,8 @@ snapshots:
       picocolors: 1.1.1
       source-map-js: 1.2.1
 
+  promise-polyfill@7.1.2: {}
+
   proxy-from-env@1.1.0: {}
 
   qs@6.14.0:

+ 12 - 0
src/api/user.ts

@@ -47,3 +47,15 @@ export const ssoLogin = (data: any) => {
     data,
   });
 };
+
+export const dingTalkLogin = (data: {
+  code: string;
+  type: number;
+  state: number;
+}) => {
+  return request.post({
+    url: "/system/auth/h5SocialLogin",
+    data,
+    headers: { "tenant-id": 1 },
+  });
+};

BIN
src/assets/images/youcnag.png


+ 35 - 30
src/components/home/CardItem.vue

@@ -103,6 +103,7 @@ import thinkimage from "@/assets/images/think.png"; // 智能决策
 import aiimage from "@/assets/images/ai.png"; // AI大模型
 import agentimage from "@/assets/images/agent.jpeg"; // ai智能体
 import videoimage from "@/assets/images/video.png"; // ai智能体
+import youimage from "@/assets/images/youcnag.png"; // ai智能体
 
 const userStore = useUserStore();
 const router = useRouter();
@@ -131,37 +132,38 @@ const props = defineProps<{
 
 // 创建图标映射表
 const iconMap: Record<string, string> = {
-  "办公系统(OA)": oaimage,
-  "经营驾驶舱(OC)": driveimage,
-  "战略解码与执行(SDM)": zhanlueimage,
+  OA办公: oaimage,
+  "经营驾驶舱(MC)": driveimage,
+  战略解码与执行: zhanlueimage,
   "财务管理(FM)": erpimage,
-  "技术研发管理(RD)": jishuimage,
+  技术研发管理: jishuimage,
   "客户管理(CRM)": crmimage,
   "人力资源(EHR)": ehrimage,
-  "供应链管理(SCM)": scmimage,
-  "组织资产管理(AM)": zuzhiimage,
-  "风控合规管理(ECM)": safeimage,
-  "中航北斗(ZH)": zhonghangimage,
+  "供应商管理(SRM)": scmimage,
+  组织资产管理: zuzhiimage,
+  风控合规管理: safeimage,
+  中航北斗: zhonghangimage,
   "视频中心(VCS)": videoimage,
-  "智能钻井系统(IDS)": zuanjingimage,
-  "智能压裂系统(IFS)": yalieimage,
-  "智能注气系统(IGIS)": zhuqiimage,
-  "智能连油系统(ICOTS)": lianyouimage,
-  "安全应急(QHSE)": qhseimage,
-  "设备管理系统 (PMS)": pmsimage,
-  "项目管理 (PM)": pmimage,
-  "全局数据治理(DG)": dataimage,
-  "智能决策(DI)": thinkimage,
-  "行业AI大模型(LM)": aiimage,
-  "AI智能体(Agent)": agentimage,
+  智能钻井: zuanjingimage,
+  智慧压裂: yalieimage,
+  智慧注气: zhuqiimage,
+  智慧连油: lianyouimage,
+  "质量安全管理(QHSE)": qhseimage,
+  "设备管理(PMS)": pmsimage,
+  "项目管理(PM)": pmimage,
+  "全局数据治理(数据中台)": dataimage,
+  智能决策: thinkimage,
+  行业AI大模型: aiimage,
+  "AI智能体(智能交互)": agentimage,
+  数字油藏: youimage,
 };
 const isSpecialSystem = (label: string) => {
   const specialSystems = [
-    "办公系统(OA)",
+    "OA办公",
     "客户管理(CRM)",
-    "设备管理系统 (PMS)",
-    "中航北斗智慧管理系统",
-    "智能连油系统",
+    "设备管理(PMS)",
+    "中航北斗",
+    "智慧连油",
   ];
   return specialSystems.includes(label);
 };
@@ -182,7 +184,7 @@ const goDetail = () => {
 
 // 处理查看按钮点击事件
 const handleView = async (item: Item) => {
-  if (item.label === "办公系统(OA)") {
+  if (item.label === "OA办公") {
     if (userStore.getUser.username && getAccessToken()) {
       const res = await ssoLogin({
         username: userStore.getUser.username,
@@ -199,7 +201,7 @@ const handleView = async (item: Item) => {
     }
   }
 
-  if (item.label === "设备管理系统 (PMS)") {
+  if (item.label === "设备管理(PMS)") {
     if (userStore.getUser.username && getAccessToken()) {
       window.open(
         import.meta.env.VITE_PMS_URL +
@@ -212,7 +214,7 @@ const handleView = async (item: Item) => {
     }
   }
 
-  if (item.label === "中航北斗(ZH)") {
+  if (item.label === "中航北斗") {
     // window.open("https://zhbdgps.cn", "_blank");
     if (userStore.getUser.username && getAccessToken()) {
       window.open("https://zhbdgps.cn", "_blank");
@@ -222,15 +224,18 @@ const handleView = async (item: Item) => {
   }
 
   if (item.label === "客户管理(CRM)") {
-    // window.open("https://www.xiaoshouyi.com/sfa", "_blank");
     if (userStore.getUser.username && getAccessToken()) {
-      window.open("https://www.xiaoshouyi.com/sfa", "_blank");
+      window.open(
+        "https://crm-tencent.xiaoshouyi.com/global/sso/callback/00APEB9EEEA9B2E338B686B7ECFA8585808C.action?token=" +
+          getAccessToken(),
+        "_blank",
+      );
     } else {
       router.push({ path: "/login" });
     }
   }
 
-  if (item.label === "智能连油系统(ICOTS)") {
+  if (item.label === "智慧连油") {
     const extraParam = "source=zhly";
     if (userStore.getUser.username && getAccessToken()) {
       window.open(
@@ -246,7 +251,7 @@ const handleView = async (item: Item) => {
     }
   }
 
-  if (item.label === "智能注气系统(IGIS)") {
+  if (item.label === "智慧注气") {
     const extraParam = "source=znzq";
     if (userStore.getUser.username && getAccessToken()) {
       window.open(

+ 215 - 4
src/components/home/header.vue

@@ -24,10 +24,72 @@
       </nav>
 
       <div class="hidden lg:flex items-center gap-3 h-full">
-        <div class="flex items-center gap-2 cursor-pointer">
-          <Icon icon="mdi:server" class="w-4 h-4" />
-          <span class="text-sm">控制台</span>
-        </div>
+        <!-- 消息中心 -->
+        <el-dropdown trigger="click" placement="bottom-end">
+          <div class="flex items-center gap-2 cursor-pointer pr-2">
+            <Icon
+              icon="mdi:bell"
+              class="w-5 h-5 text-gray-600 hover:text-[#409EFF]"
+            />
+            <span class="text-sm text-gray-600">消息代办</span>
+          </div>
+          <template #dropdown>
+            <el-dropdown-menu class="notification-dropdown">
+              <div class="notification-tabs pl-2">
+                <el-tabs v-model="activeTab" class="demo-tabs">
+                  <el-tab-pane label="消息中心" name="messages">
+                    <div class="tab-content">
+                      <!-- 消息中心内容 -->
+                      <div
+                        class="message-item"
+                        v-for="(item, index) in messages"
+                        :key="index"
+                      >
+                        <div class="message-icon">
+                          <Icon
+                            :icon="item.icon"
+                            class="w-5 h-5"
+                            :class="item.typeClass"
+                          />
+                        </div>
+                        <div class="message-text">
+                          <p class="message-title">{{ item.title }}</p>
+                          <p class="message-desc">{{ item.desc }}</p>
+                          <p class="message-time">{{ item.time }}</p>
+                        </div>
+                      </div>
+                      <div v-if="!messages.length" class="no-messages">
+                        暂无新消息
+                      </div>
+                    </div>
+                  </el-tab-pane>
+                  <el-tab-pane label="待办任务" name="tasks">
+                    <div class="tab-content">
+                      <!-- 待办任务内容 -->
+                      <div
+                        class="task-item"
+                        v-for="(task, index) in tasks"
+                        :key="index"
+                      >
+                        <div class="task-info">
+                          <p class="task-title">{{ task.title }}</p>
+                          <p class="task-desc">{{ task.desc }}</p>
+                          <p class="task-time">截止时间: {{ task.dueTime }}</p>
+                        </div>
+                        <el-tag :type="task.priorityTag" size="small">{{
+                          task.priorityText
+                        }}</el-tag>
+                      </div>
+                      <div v-if="!tasks.length" class="no-tasks">
+                        暂无待办任务
+                      </div>
+                    </div>
+                  </el-tab-pane>
+                </el-tabs>
+              </div>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
 
         <template v-if="isLoggedIn">
           <el-dropdown @command="onUserCommand" trigger="click">
@@ -202,6 +264,56 @@ import person from "@/assets/images/person.png";
 import { useUserStoreWithOut } from "@/stores/useUserStore";
 const userStore = useUserStoreWithOut();
 
+// 新增消息中心状态
+const activeTab = ref("messages");
+const messages = ref([
+  {
+    title: "系统通知",
+    desc: "您的账户已成功激活",
+    time: "10分钟前",
+    icon: "mdi:message-text-outline",
+    typeClass: "text-blue-500",
+  },
+  {
+    title: "安全提醒",
+    desc: "检测到异地登录行为",
+    time: "30分钟前",
+    icon: "mdi:security",
+    typeClass: "text-red-500",
+  },
+  {
+    title: "更新提示",
+    desc: "系统将于今晚进行维护升级",
+    time: "1小时前",
+    icon: "mdi:update",
+    typeClass: "text-green-500",
+  },
+]);
+
+const tasks = ref([
+  {
+    title: "审批申请",
+    desc: "部门采购申请等待您审批",
+    dueTime: "今天 17:00",
+    priorityText: "高",
+    priorityTag: "danger",
+  },
+  {
+    title: "项目汇报",
+    desc: "月度项目进度报告待提交",
+    dueTime: "明天",
+    priorityText: "中",
+    priorityTag: "warning",
+  },
+  {
+    title: "会议安排",
+    desc: "准备下周团队会议材料",
+    dueTime: "后天",
+    priorityText: "低",
+    priorityTag: "info",
+  },
+]);
+
 const isLoggedIn = computed(
   () => !!userStore.isSetUser || !!userStore.user?.id,
 );
@@ -273,4 +385,103 @@ const onUserCommand = async (command: string) => {
     opacity: 0;
   }
 }
+
+.notification-dropdown {
+  width: 400px !important;
+  max-height: 500px;
+  overflow: hidden;
+}
+
+.notification-tabs .el-tabs__header {
+  margin-bottom: 0;
+  padding: 10px;
+  background-color: #f8f9fa;
+}
+
+.tab-content {
+  max-height: 400px;
+  overflow-y: auto;
+  padding: 10px;
+}
+
+.message-item {
+  display: flex;
+  align-items: flex-start;
+  padding: 12px 8px;
+  border-bottom: 1px solid #eee;
+}
+
+.message-item:last-child {
+  border-bottom: none;
+}
+
+.message-icon {
+  margin-right: 12px;
+  display: flex;
+  align-items: center;
+}
+
+.message-text {
+  flex: 1;
+}
+
+.message-title {
+  font-weight: 500;
+  color: #303133;
+  margin-bottom: 4px;
+}
+
+.message-desc {
+  font-size: 13px;
+  color: #909399;
+  line-height: 1.4;
+  margin-bottom: 4px;
+}
+
+.message-time {
+  font-size: 12px;
+  color: #c0c4cc;
+}
+
+.no-messages,
+.no-tasks {
+  text-align: center;
+  padding: 20px;
+  color: #909399;
+  font-style: italic;
+}
+
+.task-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px 8px;
+  border-bottom: 1px solid #eee;
+}
+
+.task-item:last-child {
+  border-bottom: none;
+}
+
+.task-info {
+  flex: 1;
+}
+
+.task-title {
+  font-weight: 500;
+  color: #303133;
+  margin-bottom: 4px;
+}
+
+.task-desc {
+  font-size: 13px;
+  color: #909399;
+  line-height: 1.4;
+  margin-bottom: 4px;
+}
+
+.task-time {
+  font-size: 12px;
+  color: #c0c4cc;
+}
 </style>

+ 95 - 20
src/views/index.vue

@@ -66,6 +66,11 @@
 </template>
 
 <script setup lang="ts">
+import { onMounted, ref } from "vue";
+import * as authUtil from "@/utils/auth";
+import * as dd from "dingtalk-jsapi";
+import { ElLoading } from "element-plus";
+import { dingTalkLogin } from "@/api/user";
 import Header from "@components/home/header.vue";
 import CardItem from "@components/home/CardItem.vue";
 import Footer from "@components/home/Footer.vue";
@@ -75,6 +80,8 @@ import img3 from "@/assets/images/3.jpg";
 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";
+const router = useRouter();
 
 type Card = {
   name: string;
@@ -93,18 +100,19 @@ const cards: Card[] = [
     id: "management",
     bg: img1,
     items: [
-      { label: "办公系统(OA)" },
+      { label: "OA办公" },
       { label: "客户管理(CRM)" },
       { label: "人力资源(EHR)" },
-      { label: "供应链管理(SCM)" },
+      { label: "供应商管理(SRM)" },
       { label: "财务管理(FM)" },
-      { label: "经营驾驶舱(OC)", tag: "新" },
-      { label: "战略解码与执行(SDM)" },
+      { label: "经营驾驶舱(MC)", tag: "新" },
+      { label: "项目管理 (PM)" },
 
-      { label: "技术研发管理(RD)" },
+      { label: "技术研发管理" },
 
-      { label: "组织资产管理(AM)" },
-      { label: "风控、合规管理(ECM)" },
+      { label: "组织资产管理" },
+      { label: "风控合规管理" },
+      { label: "战略解码与执行" },
     ],
   },
   {
@@ -114,16 +122,15 @@ const cards: Card[] = [
     id: "command",
     bg: img2,
     items: [
-      { label: "设备管理系统 (PMS)" },
-      { label: "中航北斗(ZH)" },
-      { label: "智能连油系统(ICOTS)" },
-      { label: "安全应急(QHSE)" },
+      { label: "设备管理(PMS)" },
+      { label: "中航北斗" },
+      { label: "质量安全管理(QHSE)" },
+      { label: "智慧连油" },
+      { label: "智慧注气" },
+      { label: "智能钻井" },
+      { label: "智慧压裂" },
+      { label: "数字油藏" },
       { label: "视频中心(VCS)" },
-      { label: "智能钻井系统(IDS)", tag: "热" },
-      { label: "智能压裂系统(IFS)" },
-      { label: "智能注气系统(IGIS)" },
-
-      { label: "项目管理 (PM)" },
     ],
   },
   {
@@ -133,13 +140,81 @@ const cards: Card[] = [
     id: "chatbi",
     bg: img3,
     items: [
-      { label: "全局数据治理(DG)", tag: "热" },
-      { label: "智能决策(DI)" },
-      { label: "行业AI大模型(LM)", tag: "新" },
-      { label: "AI智能体(Agent)", tag: "新" },
+      { label: "全局数据治理(数据中台)", tag: "热" },
+      { label: "智能决策" },
+      { label: "行业AI大模型", tag: "新" },
+      { label: "AI智能体(智能交互)", tag: "新" },
     ],
   },
 ];
+
+const loading2 = ref<any>(null);
+const redirect = ref<string>("");
+
+async function loginWithDingTalk() {
+  const ddCorpId = import.meta.env.VITE_DD_CORPID;
+  const ddClientId = import.meta.env.VITE_DD_CLIENTID;
+
+  if (!ddCorpId || !ddClientId) return;
+
+  dd.requestAuthCode({
+    corpId: ddCorpId,
+    clientId: ddClientId,
+    success: (res: any) => {
+      const { code } = res;
+      dingTalkLogin({ code, type: 10, state: new Date().getTime() }).then(
+        async (res) => {
+          console.log("res :>> ", res);
+          loading2.value = ElLoading.service({
+            lock: true,
+            text: "正在加载系统中...",
+            background: "rgba(0, 0, 0, 0.7)",
+          });
+
+          authUtil.setToken(res);
+
+          if (!redirect.value) {
+            redirect.value = "/";
+          }
+          if (redirect.value.indexOf("sso") !== -1) {
+            window.location.href = window.location.href.replace(
+              "/login?redirect=",
+              "",
+            );
+          } else {
+            router
+              .push({
+                path: redirect.value,
+              })
+              .then(() => {
+                loading2.value.close();
+              });
+          }
+        },
+      );
+    },
+    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")) return;
+
+  loginWithDingTalk();
+}
+
+onMounted(() => {
+  dingTalkAutoLogin();
+});
 </script>
 
 <style scoped></style>