yanghao 2 weken geleden
bovenliggende
commit
a957972859
4 gewijzigde bestanden met toevoegingen van 320 en 59 verwijderingen
  1. 118 0
      src/components/home/header.vue
  2. 190 54
      src/views/flow/index.vue
  3. 10 3
      src/views/index.vue
  4. 2 2
      src/views/login.vue

+ 118 - 0
src/components/home/header.vue

@@ -287,6 +287,124 @@
       </div>
 
       <div class="lg:hidden">
+        <el-dropdown trigger="click" placement="bottom-end">
+          <div class="flex items-center gap-2 cursor-pointer pr-6 pt-1">
+            <el-badge
+              :value="unreadMessageCount + oaUnreadCount"
+              class="item"
+              v-if="hasUnreadMessages || oaHasUnreadCount"
+            >
+              <Icon
+                icon="mdi:bell"
+                class="w-5 h-5 text-gray-600 hover:text-[#409EFF]"
+              />
+            </el-badge>
+            <Icon
+              v-else
+              icon="mdi:bell"
+              class="w-5 h-5 text-gray-600 hover:text-[#409EFF]"
+            />
+          </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="CRM" name="messages">
+                    <template #label>
+                      <span class="custom-tabs-label">
+                        <span>CRM</span>
+                        <el-badge
+                          :value="unreadMessageCount"
+                          class="item ml-1"
+                          v-if="hasUnreadMessages"
+                        ></el-badge>
+                      </span>
+                    </template>
+                    <div class="tab-content">
+                      <!-- 消息中心内容 -->
+                      <div>
+                        <span
+                          v-if="hasUnreadMessages"
+                          class="cursor-pointer text-blue-500"
+                          @click="markAllAsRead"
+                          >全部标为已读</span
+                        >
+                      </div>
+                      <div
+                        class="message-item"
+                        v-for="(item, index) in messages"
+                        :key="index"
+                      >
+                        <div class="message-icon"></div>
+                        <div class="message-text">
+                          <!-- 未读就显示小红点 -->
+
+                          <p class="message-title flex items-center gap-5">
+                            <span
+                              v-if="item.status === '0'"
+                              class="w-2 h-2 bg-[#f56c6c] rounded-full"
+                            ></span
+                            >{{ item.contentMajor }}
+                          </p>
+                          <p class="message-desc">
+                            {{ timestampToDateTime(item.createTime) }}
+                          </p>
+                        </div>
+                      </div>
+                      <div v-if="!messages.length" class="no-messages">
+                        暂无新消息
+                      </div>
+                    </div>
+                  </el-tab-pane>
+                  <el-tab-pane label="OA" name="tasks">
+                    <template #label>
+                      <span class="custom-tabs-label">
+                        <span>OA</span>
+                        <el-badge
+                          :value="oaUnreadCount"
+                          class="item ml-1"
+                          v-if="oaHasUnreadCount"
+                        ></el-badge>
+                      </span>
+                    </template>
+                    <div class="tab-content">
+                      <div>
+                        <span
+                          v-if="oaHasUnreadCount"
+                          class="cursor-pointer text-blue-500"
+                          @click="oaMarkAllAsRead"
+                          >全部标为已读</span
+                        >
+                      </div>
+                      <!-- OA消息 -->
+                      <div
+                        class="task-item"
+                        v-for="(task, index) in oaMessagesList"
+                        :key="index"
+                      >
+                        <div class="task-info">
+                          <p class="task-title">
+                            <span
+                              v-if="task.status === '0'"
+                              class="inline-block h-2 w-2 bg-[#f56c6c] rounded-full"
+                            ></span>
+                            {{ task.title }}
+                          </p>
+                          <p class="message-desc">
+                            <span>{{ task.oaCreateTime }}</span>
+                          </p>
+                        </div>
+                      </div>
+                      <div v-if="!oaMessagesList.length" class="no-tasks">
+                        暂无新消息
+                      </div>
+                    </div>
+                  </el-tab-pane>
+                </el-tabs>
+              </div>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
         <el-button link @click="drawer = true">
           <i class="el-icon" />
           <Icon icon="fa:bars" class="icon" />

+ 190 - 54
src/views/flow/index.vue

@@ -80,28 +80,54 @@
         </span>
       </div>
 
-      <div class="tabs-container" role="tablist" aria-label="EHR模块">
+      <div class="tabs-wrapper">
+        <!-- 左侧箭头 -->
         <button
-          class="el-tab-item"
-          type="button"
-          role="tab"
-          :class="{ 'is-active': activeKey === 'all' }"
-          :aria-selected="activeKey === 'all'"
-          @click="setAll"
+          class="scroll-arrow left"
+          @click="scrollTabs('left')"
+          :disabled="isLeftDisabled"
         >
-          <span class="tab-label">全部</span>
+          <Icon icon="mdi:chevron-left" />
         </button>
+
+        <!-- Tab 列表容器 -->
+        <div
+          class="tabs-container"
+          ref="tabsContainerRef"
+          role="tablist"
+          aria-label="EHR模块"
+        >
+          <button
+            class="el-tab-item"
+            type="button"
+            role="tab"
+            :class="{ 'is-active': activeKey === 'all' }"
+            :aria-selected="activeKey === 'all'"
+            @click="setAll"
+          >
+            <span class="tab-label">全部</span>
+          </button>
+          <button
+            v-for="tab in tabs"
+            :key="tab.groupName"
+            class="el-tab-item"
+            :class="{ 'is-active': tab.groupName === activeKey }"
+            type="button"
+            role="tab"
+            :aria-selected="tab.groupName === activeKey"
+            @click="getById(tab)"
+          >
+            <span class="tab-label">{{ tab.groupName }}</span>
+          </button>
+        </div>
+
+        <!-- 右侧箭头 -->
         <button
-          v-for="tab in tabs"
-          :key="tab.groupName"
-          class="el-tab-item"
-          :class="{ 'is-active': tab.groupName === activeKey }"
-          type="button"
-          role="tab"
-          :aria-selected="tab.groupName === activeKey"
-          @click="getById(tab)"
+          class="scroll-arrow right"
+          @click="scrollTabs('right')"
+          :disabled="!isLeftDisabled"
         >
-          <span class="tab-label">{{ tab.groupName }}</span>
+          <Icon icon="mdi:chevron-right" />
         </button>
       </div>
 
@@ -142,34 +168,6 @@
               </h3>
               <p class="item-desc">{{ item.remark || "暂无描述" }}</p>
             </div>
-
-            <!-- <div class="flex justify-between">
-              <div class="item-time flex items-center gap-2">
-                <svg
-                  xmlns="http://www.w3.org/2000/svg"
-                  width="1em"
-                  height="1em"
-                  viewBox="0 0 24 24"
-                >
-                  <g
-                    fill="none"
-                    stroke="#f97316"
-                    stroke-linecap="round"
-                    stroke-linejoin="round"
-                    stroke-width="1.5"
-                  >
-                    <path
-                      d="M15.362 5.214A8.252 8.252 0 0 1 12 21A8.25 8.25 0 0 1 6.038 7.047A8.3 8.3 0 0 0 9 9.601a8.98 8.98 0 0 1 3.361-6.867a8.2 8.2 0 0 0 3 2.48"
-                    ></path>
-                    <path
-                      d="M12 18a3.75 3.75 0 0 0 .495-7.468a6 6 0 0 0-1.925 3.547a6 6 0 0 1-2.133-1.001A3.75 3.75 0 0 0 12 18"
-                    ></path>
-                  </g>
-                </svg>
-                <span class="text-[12px] text-[#babdd1]">提交流程</span>
-              </div>
-              <Icon icon="mdi-light:chevron-right" class="item-arrow w-6 h-6" />
-            </div> -->
           </div>
         </div>
         <div v-else class="empty-state">
@@ -206,6 +204,47 @@ const pieChartInstance = ref(null);
 let chartResizeObserver = null;
 let chartInitTimer = null;
 
+// 1. 定义 Ref
+const tabsContainerRef = ref(null);
+const isLeftDisabled = ref(true);
+const isRightDisabled = ref(true);
+
+// 2. 滚动逻辑
+const scrollTabs = (direction) => {
+  const container = tabsContainerRef.value;
+  if (!container) return;
+
+  // 每次滚动的距离,可以根据需求调整,例如 200px 或 container.clientWidth / 2
+  const scrollAmount = 200;
+
+  if (direction === "left") {
+    container.scrollBy({ left: -scrollAmount, behavior: "smooth" });
+  } else {
+    container.scrollBy({ left: scrollAmount, behavior: "smooth" });
+  }
+};
+
+// 3. 更新箭头禁用状态
+const updateArrowState = () => {
+  const container = tabsContainerRef.value;
+  if (!container) return;
+
+  const { scrollLeft, scrollWidth, clientWidth } = container;
+
+  // 如果滚动条在最左边(容差1px),禁用左箭头
+  isLeftDisabled.value = scrollLeft <= 1;
+
+  // 如果滚动条在最右边(容差1px),禁用右箭头
+  // Math.ceil 处理小数像素问题
+  isRightDisabled.value =
+    Math.ceil(scrollLeft + clientWidth) >= scrollWidth - 1;
+};
+
+// 4. 监听滚动事件
+const handleTabScroll = () => {
+  updateArrowState();
+};
+
 const initChartsSafe = (attempt = 0) => {
   const lineDom = lineChartRef.value;
   const pieDom = pieChartRef.value;
@@ -650,6 +689,11 @@ onMounted(async () => {
   getAll();
   // 等待 DOM 与样式生效,避免移动端首屏尺寸为 0
   await nextTick();
+  updateArrowState();
+  // 添加滚动监听
+  if (tabsContainerRef.value) {
+    tabsContainerRef.value.addEventListener("scroll", handleTabScroll);
+  }
   requestAnimationFrame(() => {
     initChartsSafe();
     // 添加监听
@@ -764,6 +808,10 @@ onBeforeUnmount(() => {
   // 可选:销毁 echarts 实例
   lineChartInstance.value?.dispose();
   pieChartInstance.value?.dispose();
+
+  if (tabsContainerRef.value) {
+    tabsContainerRef.value.removeEventListener("scroll", handleTabScroll);
+  }
 });
 </script>
 
@@ -878,15 +926,6 @@ onBeforeUnmount(() => {
   color: #64748b;
 }
 
-.tabs-container {
-  display: flex;
-  align-items: center;
-  border-bottom: 1px solid #e4e7ed; /* Element Plus 标准的分割线颜色 */
-  margin-bottom: 20px;
-  padding-left: 0;
-  overflow-x: auto; /* 防止Tab过多时溢出 */
-}
-
 .el-tab-item {
   position: relative;
   display: inline-flex;
@@ -1333,4 +1372,101 @@ onBeforeUnmount(() => {
   color: #02409b; /* 变蓝 (使用你主题中的蓝色) */
   transform: translateX(4px); /* 向右平移 4px */
 }
+
+.tabs-wrapper {
+  position: relative;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+/* 修改:原有的 .tabs-container 去掉 margin-bottom,因为现在由 wrapper 控制间距 */
+.tabs-container {
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid #e4e7ed;
+  padding-left: 0;
+  overflow-x: auto;
+  scroll-behavior: smooth;
+  scrollbar-width: none; /* Firefox */
+  -ms-overflow-style: none; /* IE 10+ */
+  flex: 1; /* 占据中间剩余空间 */
+  mask-image: linear-gradient(
+    to right,
+    transparent,
+    black 20px,
+    black 98%,
+    transparent
+  ); /* 可选:添加两侧渐变遮罩效果 */
+  -webkit-mask-image: linear-gradient(
+    to right,
+    transparent,
+    black 20px,
+    black 98%,
+    transparent
+  );
+}
+
+.tabs-container::-webkit-scrollbar {
+  display: none;
+}
+
+/* 新增:箭头按钮样式 */
+.scroll-arrow {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  z-index: 10;
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  background-color: #ffffff;
+  border: 1px solid #e2e8f0;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  color: #64748b;
+  transition: all 0.2s ease;
+  opacity: 0; /* 默认隐藏,鼠标悬停容器或需要时显示,或者始终显示但禁用时变灰 */
+  visibility: hidden;
+}
+
+/* 当容器被悬停时显示箭头,或者你可以去掉这个限制让它始终显示 */
+.tabs-wrapper:hover .scroll-arrow {
+  opacity: 1;
+  visibility: visible;
+}
+
+.scroll-arrow.left {
+  left: 0;
+}
+
+.scroll-arrow.right {
+  right: 0;
+}
+
+.scroll-arrow:hover:not(:disabled) {
+  background-color: #f1f5f9;
+  color: #02409b;
+  border-color: #cbd5e1;
+}
+
+.scroll-arrow:disabled {
+  background-color: #f8fafc;
+  color: #cbd5e1;
+  cursor: not-allowed;
+  box-shadow: none;
+  border-color: #f1f5f9;
+  opacity: 0.6;
+  visibility: visible; /* 禁用时也要可见以展示状态 */
+}
+
+/* 确保 Tab 项不会被箭头遮挡太多,可以在容器两侧加一点 padding */
+.tabs-container {
+  /* 在原有样式基础上增加左右 padding,防止第一个和最后一个 Tab 被箭头完全遮住 */
+  padding: 0 40px;
+}
 </style>

+ 10 - 3
src/views/index.vue

@@ -306,7 +306,7 @@ type NewsItem = {
 
 const router = useRouter();
 const userStore = useUserStore();
-const heroGreeting = "早上好,保持热爱,奔赴目标!";
+
 const noticeLabel = "公告";
 const todoPanelTitle = "待办中心";
 const newsPanelTitle = "新闻";
@@ -354,7 +354,7 @@ const portalSections: PortalSection[] = [
     ],
   },
   {
-    code: "CB",
+    code: "AI",
     title: "Chat BI平台",
     subtitle: "高效协同 · 战略洞察",
     height: "160px",
@@ -382,11 +382,18 @@ let boldLabes = ref([
   "经营驾驶舱(MC)",
 ]);
 
+const getGreeting = () => {
+  const hour = new Date().getHours();
+  if (hour < 12) return "早上好";
+  if (hour < 18) return "下午好";
+  return "晚上好";
+};
+
 // 添加轮播数据
 const slides = ref([
   {
     image: banner1,
-    text: "早上好,保持热爱,奔赴目标!",
+    text: getGreeting() + ",保持热爱,奔赴目标!",
   },
   {
     image: banner2,

+ 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">