yanghao před 1 dnem
rodič
revize
bda9d98d32
4 změnil soubory, kde provedl 212 přidání a 48 odebrání
  1. 1 0
      components.d.ts
  2. 7 0
      src/api/user.ts
  3. 202 46
      src/views/flow/index.vue
  4. 2 2
      src/views/login.vue

+ 1 - 0
components.d.ts

@@ -22,6 +22,7 @@ declare module 'vue' {
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElPopover: typeof import('element-plus/es')['ElPopover']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']

+ 7 - 0
src/api/user.ts

@@ -98,3 +98,10 @@ export const getOATasks = async (id) => {
     url: "/admin-api/portal/todo/oa?workcode=" + id,
   });
 };
+
+// crm待办任务
+export const getCRMTasks = async (id) => {
+  return await request.get({
+    url: "/admin-api/portal/todo/crm?workcode=" + id,
+  });
+};

+ 202 - 46
src/views/flow/index.vue

@@ -3,7 +3,7 @@
     <Header />
     <section class="hero">
       <div class="hero-inner">
-        <h1 class="hero-title">下午好,晓华</h1>
+        <h1 class="hero-title">下午好,{{ userStore.getUser.nickname }}</h1>
         <p class="hero-desc">
           今天是 {{ new Date().toLocaleDateString() }}。您有 5 条流程待处理。
         </p>
@@ -24,16 +24,43 @@
     <!-- 任务统计 -->
     <section class="total">
       <div class="total-card" v-for="(item, index) in stats" :key="index">
-        <div class="card-icon" :style="{ backgroundColor: item.bgcolor }">
-          <Icon :icon="item.icon" :color="item.color" />
-        </div>
-        <div class="card-content">
-          <p class="card-title">{{ item.title }}</p>
-          <p class="card-number">{{ oaTasks.todoCount }}</p>
-        </div>
-        <div v-if="item.extra" class="card-extra">
-          {{ item.extra }}
-        </div>
+        <el-popover
+          placement="top"
+          :width="200"
+          trigger="hover"
+          popper-class="glass-popover"
+          :disabled="getDetailList(index).length === 0"
+          raw-content
+          transition="el-zoom-in-top"
+        >
+          <template #reference>
+            <div class="card-wrapper">
+              <!-- ... 图标和内容 ... -->
+              <div class="card-icon" :style="{ backgroundColor: item.bgcolor }">
+                <Icon :icon="item.icon" :color="item.color" />
+              </div>
+              <div class="card-content">
+                <p class="card-title">{{ item.title }}</p>
+                <p class="card-number">{{ item.number }}</p>
+              </div>
+            </div>
+          </template>
+
+          <div class="detail-list">
+            <div
+              v-for="(task, idx) in getDetailList(index)"
+              :key="idx"
+              class="detail-item"
+              @click="handleDetailClick(task, item.title)"
+            >
+              <span class="detail-name">{{ task.name }}</span>
+              <span class="detail-val">{{ task.value }}</span>
+            </div>
+            <div v-if="getDetailList(index).length === 0" class="empty-tip">
+              暂无详细数据
+            </div>
+          </div>
+        </el-popover>
       </div>
     </section>
 
@@ -101,7 +128,7 @@ import Header from "@components/home/header.vue";
 import Footer from "@components/home/Footer.vue";
 import { computed, ref, onMounted, onBeforeUnmount, nextTick } from "vue";
 import { Icon } from "@iconify/vue";
-import { getFlows, ssoLogin, getOATasks } from "@/api/user";
+import { getFlows, ssoLogin, getOATasks, getCRMTasks } from "@/api/user";
 import { useUserStore } from "@/stores/useUserStore";
 import { getAccessToken } from "@/utils/auth";
 import * as echarts from "echarts";
@@ -158,6 +185,20 @@ const pieChartData = {
   ],
 };
 
+const getDetailList = (index) => {
+  switch (index) {
+    case 0: // 我的待办
+      return todoCount.value;
+    case 1: // 已办事项
+      return doneCount.value;
+    case 2: // 发起流程
+      return startCount.value;
+    case 3: // 草稿箱
+      return drafts.value;
+    default:
+      return [];
+  }
+};
 // 初始化图表
 const initLineChart = () => {
   const chartDom = lineChartRef.value;
@@ -289,39 +330,6 @@ const activeTab = computed(() => {
   );
 });
 
-const stats = [
-  {
-    icon: "mdi:clock-outline",
-    title: "我的待办",
-    number: "05",
-    extra: "+2 今日",
-    bgcolor: "#fff7ed",
-    color: "#f59e0b",
-  },
-  {
-    icon: "mdi:check-circle-outline",
-    title: "已办事项",
-    number: "128",
-    bgcolor: "#eff6ff",
-    color: "#2563eb",
-  },
-  {
-    icon: "mdi:arrow-right-bold-box-outline",
-    title: "发起流程",
-    number: "42",
-    extra: "85% 准时",
-    bgcolor: "#eff6ff",
-    color: "#2563eb",
-  },
-  {
-    icon: "mdi:file-document-outline",
-    title: "草箱箱",
-    number: "03",
-    bgcolor: "#f8fafc",
-    color: "#475569",
-  },
-];
-
 const getAll = async () => {
   const res = await getFlows({
     pageNo: 1,
@@ -374,7 +382,73 @@ const go = async (item) => {
   }
 };
 
+const handleDetailClick = (task, categoryTitle) => {
+  console.log(`点击了 ${categoryTitle} 中的 ${task.name}: ${task.value}`);
+  // 示例:根据类型跳转
+  // if (categoryTitle === '我的待办') {
+  //   router.push({ path: '/flow/todo', query: { type: task.name.toLowerCase() } });
+  // }
+};
+
 let oaTasks = ref([]);
+let crmTasks = ref([]);
+
+const stats = ref([
+  {
+    icon: "mdi:clock-outline",
+    title: "我的待办",
+    number: 0,
+    extra: "+2 今日",
+    bgcolor: "#fff7ed",
+    color: "#f59e0b",
+  },
+  {
+    icon: "mdi:check-circle-outline",
+    title: "已办事项",
+    number: 0,
+    bgcolor: "#eff6ff",
+    color: "#2563eb",
+  },
+  {
+    icon: "mdi:arrow-right-bold-box-outline",
+    title: "发起流程",
+    number: 0,
+    extra: "85% 准时",
+    bgcolor: "#eff6ff",
+    color: "#2563eb",
+  },
+  {
+    icon: "mdi:file-document-outline",
+    title: "草稿箱",
+    number: 0,
+    bgcolor: "#f8fafc",
+    color: "#475569",
+  },
+]);
+
+const todoCount = ref([
+  { name: "OA", value: 0 },
+  { name: "CRM", value: 0 },
+]);
+
+// 已办事项
+const doneCount = ref([
+  { name: "OA", value: 0 },
+  { name: "CRM", value: 0 },
+]);
+
+// 发起流程
+const startCount = ref([
+  { name: "OA", value: 0 },
+  { name: "CRM", value: 0 },
+]);
+
+// 草稿箱
+const drafts = ref([
+  { name: "OA", value: 0 },
+  { name: "CRM", value: 0 },
+]);
+
 onMounted(async () => {
   getAll();
   // 等待 DOM 与样式生效,避免移动端首屏尺寸为 0
@@ -396,7 +470,18 @@ onMounted(async () => {
   if (userStore.getUser.username) {
     const res = await getOATasks(userStore.getUser.username);
     oaTasks.value = res;
+
+    const crmRes = await getCRMTasks(userStore.getUser.username);
+    crmTasks.value = crmRes;
   }
+
+  stats.value[0].number =
+    Number(oaTasks.value.todoCount) + Number(crmTasks.value.todoCount);
+
+  todoCount.value = [
+    { name: "OA", value: oaTasks.value.todoCount },
+    { name: "CRM", value: crmTasks.value.todoCount },
+  ];
 });
 
 // 组件卸载时移除监听,防止内存泄漏
@@ -710,11 +795,13 @@ onBeforeUnmount(() => {
   cursor: pointer;
   /* border: 1px solid #e5e7eb; */
   /* border-top: solid 5px #02409b; */
+  overflow: visible;
 }
 
 .total-card:hover {
   transform: translateY(-4px);
   box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
+  color: #02409b !important;
 }
 
 .card-icon {
@@ -748,7 +835,7 @@ onBeforeUnmount(() => {
 .card-number {
   font-size: 28px;
   font-weight: 600;
-  color: #111827;
+  /* color: #111827; */
 }
 
 .card-extra {
@@ -780,4 +867,73 @@ onBeforeUnmount(() => {
   overflow: hidden;
   position: relative;
 }
+
+:global(.glass-popover) {
+  background: rgba(0, 0, 0, 0.6) !important;
+  backdrop-filter: blur(10px);
+  -webkit-backdrop-filter: blur(10px);
+  height: 200px !important;
+  width: 100px !important;
+  overflow-y: auto !important;
+  box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1) !important;
+  border-radius: 8px !important;
+  padding: 10px !important;
+  color: #fff !important;
+  z-index: 2000 !important; /* 确保在最上层 */
+  top: 15% !important; /* 根据需要调整位置 */
+}
+
+:global(.glass-popover .el-popper__arrow::before) {
+  background: transparent !important;
+  /* border: 1px solid rgba(255, 255, 255, 0.1); */
+  border: none !important;
+}
+
+.detail-list {
+  padding: 5px 0;
+}
+
+.detail-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+  font-size: 14px;
+  color: #e0e0e0; /* 浅色文字 */
+  line-height: 1.5;
+  padding: 6px 8px;
+  border-radius: 4px;
+  cursor: pointer; /* 鼠标变成手型 */
+  transition: background-color 0.2s ease;
+}
+
+.detail-item:last-child {
+  margin-bottom: 0;
+}
+
+/* 鼠标悬浮背景变灰 */
+.detail-item:hover {
+  background-color: rgba(
+    255,
+    255,
+    255,
+    0.15
+  ); /* 半透明白色,视觉上为灰色高亮 */
+}
+
+.detail-name {
+  color: #ccc;
+}
+
+.detail-val {
+  font-weight: bold;
+  color: #fff;
+}
+
+.empty-tip {
+  text-align: center;
+  color: #aaa;
+  font-size: 12px;
+  padding: 10px 0;
+}
 </style>

+ 2 - 2
src/views/login.vue

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