mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	perf: headerNotice
This commit is contained in:
		
							parent
							
								
									c4a6a337a3
								
							
						
					
					
						commit
						5d9638758b
					
				
							
								
								
									
										130
									
								
								src/layout/components/notice/data.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								src/layout/components/notice/data.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,130 @@
 | 
			
		||||
export interface ListItem {
 | 
			
		||||
  avatar: string;
 | 
			
		||||
  title: string;
 | 
			
		||||
  datetime: string;
 | 
			
		||||
  type: string;
 | 
			
		||||
  description: string;
 | 
			
		||||
  status?: "" | "success" | "warning" | "info" | "danger";
 | 
			
		||||
  extra?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TabItem {
 | 
			
		||||
  key: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  list: ListItem[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const noticesData: TabItem[] = [
 | 
			
		||||
  {
 | 
			
		||||
    key: "1",
 | 
			
		||||
    name: "通知",
 | 
			
		||||
    list: [
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
 | 
			
		||||
        title: "你收到了 12 份新周报",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        description: "",
 | 
			
		||||
        type: "1"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
 | 
			
		||||
        title: "你推荐的 前端高手 已通过第三轮面试",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        description: "",
 | 
			
		||||
        type: "1"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
 | 
			
		||||
        title: "这种模板可以区分多种通知类型",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        description: "",
 | 
			
		||||
        type: "1"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
 | 
			
		||||
        title:
 | 
			
		||||
          "展示标题内容超过一行后的处理方式,如果内容超过1行将自动截断并支持tooltip显示完整标题。",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        description: "",
 | 
			
		||||
        type: "1"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    key: "2",
 | 
			
		||||
    name: "消息",
 | 
			
		||||
    list: [
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
 | 
			
		||||
        title: "李白 评论了你",
 | 
			
		||||
        description: "长风破浪会有时,直挂云帆济沧海",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        type: "2"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
 | 
			
		||||
        title: "李白 回复了你",
 | 
			
		||||
        description: "行路难,行路难,多歧路,今安在。",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        type: "2"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar:
 | 
			
		||||
          "https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg",
 | 
			
		||||
        title: "标题",
 | 
			
		||||
        description:
 | 
			
		||||
          "请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容",
 | 
			
		||||
        datetime: "一年前",
 | 
			
		||||
        type: "2"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    key: "3",
 | 
			
		||||
    name: "代办",
 | 
			
		||||
    list: [
 | 
			
		||||
      {
 | 
			
		||||
        avatar: "",
 | 
			
		||||
        title: "任务名称",
 | 
			
		||||
        description: "任务需要在 2021-11-16 20:00 前启动",
 | 
			
		||||
        datetime: "",
 | 
			
		||||
        extra: "未开始",
 | 
			
		||||
        status: "info",
 | 
			
		||||
        type: "3"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar: "",
 | 
			
		||||
        title: "第三方紧急代码变更",
 | 
			
		||||
        description:
 | 
			
		||||
          "一拳提交于 2021-11-16,需在 2021-11-18 前完成代码变更任务",
 | 
			
		||||
        datetime: "",
 | 
			
		||||
        extra: "马上到期",
 | 
			
		||||
        status: "danger",
 | 
			
		||||
        type: "3"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar: "",
 | 
			
		||||
        title: "信息安全考试",
 | 
			
		||||
        description: "指派小仙于 2021-12-12 前完成更新并发布",
 | 
			
		||||
        datetime: "",
 | 
			
		||||
        extra: "已耗时 8 天",
 | 
			
		||||
        status: "warning",
 | 
			
		||||
        type: "3"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        avatar: "",
 | 
			
		||||
        title: "vue-pure-admin 版本发布",
 | 
			
		||||
        description: "vue-pure-admin 版本发布",
 | 
			
		||||
        datetime: "",
 | 
			
		||||
        extra: "进行中",
 | 
			
		||||
        type: "3"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
@ -1,74 +1,30 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import NoticeList from "./noticeList.vue";
 | 
			
		||||
import { noticesData } from "./data";
 | 
			
		||||
 | 
			
		||||
const loading = ref(false);
 | 
			
		||||
const activeName = ref("first");
 | 
			
		||||
 | 
			
		||||
function visibleChange(val) {
 | 
			
		||||
  if (loading.value) {
 | 
			
		||||
    loading.value = false;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!val) return; //防止加载完成后再次点击出现loading
 | 
			
		||||
  loading.value = true;
 | 
			
		||||
  setTimeout(() => {
 | 
			
		||||
    loading.value = false;
 | 
			
		||||
  }, 1000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const noticeList = [
 | 
			
		||||
  {
 | 
			
		||||
    imgUrl:
 | 
			
		||||
      "https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png",
 | 
			
		||||
    title: "你收到了 12 份新周报",
 | 
			
		||||
    description: "一年前"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    imgUrl:
 | 
			
		||||
      "https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png",
 | 
			
		||||
    title: "你推荐的 前端高手 已通过第三轮面试",
 | 
			
		||||
    description: "一年前"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    imgUrl:
 | 
			
		||||
      "https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png",
 | 
			
		||||
    title: "这种模板可以区分多种通知类型",
 | 
			
		||||
    description: "一年前"
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
const newsList = [];
 | 
			
		||||
const agencyList = [];
 | 
			
		||||
const activeName = ref(noticesData[0].name);
 | 
			
		||||
const notices = ref(noticesData);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <el-dropdown
 | 
			
		||||
    trigger="click"
 | 
			
		||||
    placement="bottom-end"
 | 
			
		||||
    @visible-change="visibleChange"
 | 
			
		||||
  >
 | 
			
		||||
  <el-dropdown trigger="click" placement="bottom-end">
 | 
			
		||||
    <span class="dropdown-badge">
 | 
			
		||||
      <el-badge :value="12" :max="99">
 | 
			
		||||
      <el-badge :value="10" :max="99">
 | 
			
		||||
        <el-icon class="header-notice-icon"><bell /></el-icon>
 | 
			
		||||
      </el-badge>
 | 
			
		||||
    </span>
 | 
			
		||||
    <template #dropdown>
 | 
			
		||||
      <el-dropdown-menu>
 | 
			
		||||
        <el-tabs
 | 
			
		||||
          v-model="activeName"
 | 
			
		||||
          v-loading="loading"
 | 
			
		||||
          class="dropdown-tabs"
 | 
			
		||||
          :style="{ width: '297px' }"
 | 
			
		||||
        >
 | 
			
		||||
          <el-tab-pane label="通知" name="first">
 | 
			
		||||
            <NoticeList :list="noticeList" />
 | 
			
		||||
          </el-tab-pane>
 | 
			
		||||
          <el-tab-pane label="消息" name="second">
 | 
			
		||||
            <NoticeList :list="newsList" />
 | 
			
		||||
          </el-tab-pane>
 | 
			
		||||
          <el-tab-pane label="代办" name="third">
 | 
			
		||||
            <NoticeList :list="agencyList" />
 | 
			
		||||
          </el-tab-pane>
 | 
			
		||||
        <el-tabs v-model="activeName" class="dropdown-tabs">
 | 
			
		||||
          <template v-for="item in notices" :key="item.key">
 | 
			
		||||
            <el-tab-pane
 | 
			
		||||
              :label="`${item.name}(${item.list.length})`"
 | 
			
		||||
              :name="item.name"
 | 
			
		||||
            >
 | 
			
		||||
              <NoticeList :list="item.list" />
 | 
			
		||||
            </el-tab-pane>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-tabs>
 | 
			
		||||
      </el-dropdown-menu>
 | 
			
		||||
    </template>
 | 
			
		||||
@ -90,6 +46,7 @@ const agencyList = [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dropdown-tabs {
 | 
			
		||||
  width: 336px;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
 | 
			
		||||
@ -1,31 +1,108 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { PropType } from "vue";
 | 
			
		||||
import { noticeItemType } from "../../types";
 | 
			
		||||
import { ListItem } from "./data";
 | 
			
		||||
import { ref, PropType, nextTick } from "vue";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  noticeItem: {
 | 
			
		||||
    type: Object as PropType<noticeItemType>,
 | 
			
		||||
    type: Object as PropType<ListItem>,
 | 
			
		||||
    default: () => {}
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const titleRef = ref(null);
 | 
			
		||||
const descriptionRef = ref(null);
 | 
			
		||||
const titleTooltip = ref(false);
 | 
			
		||||
const descriptionTooltip = ref(false);
 | 
			
		||||
 | 
			
		||||
function hoverTitle() {
 | 
			
		||||
  titleTooltip.value = false;
 | 
			
		||||
  nextTick(() => {
 | 
			
		||||
    titleRef.value?.scrollWidth > titleRef.value?.clientWidth
 | 
			
		||||
      ? (titleTooltip.value = true)
 | 
			
		||||
      : (titleTooltip.value = false);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hoverDescription(event, description) {
 | 
			
		||||
  // currentWidth 为文本在页面中所占的宽度,创建标签,加入到页面,获取currentWidth ,最后在移除
 | 
			
		||||
  let tempTag = document.createElement("span");
 | 
			
		||||
  tempTag.innerText = description;
 | 
			
		||||
  tempTag.className = "getDescriptionWidth";
 | 
			
		||||
  document.querySelector("body").appendChild(tempTag);
 | 
			
		||||
  let currentWidth = (
 | 
			
		||||
    document.querySelector(".getDescriptionWidth") as HTMLSpanElement
 | 
			
		||||
  ).offsetWidth;
 | 
			
		||||
  document.querySelector(".getDescriptionWidth").remove();
 | 
			
		||||
 | 
			
		||||
  // cellWidth为容器的宽度
 | 
			
		||||
  const cellWidth = event.target.offsetWidth;
 | 
			
		||||
 | 
			
		||||
  // 当文本宽度大于容器宽度两倍时,代表文本显示超过两行
 | 
			
		||||
  currentWidth > 2 * cellWidth
 | 
			
		||||
    ? (descriptionTooltip.value = true)
 | 
			
		||||
    : (descriptionTooltip.value = false);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="notice-container">
 | 
			
		||||
    <el-avatar
 | 
			
		||||
      v-if="props.noticeItem.avatar"
 | 
			
		||||
      :size="30"
 | 
			
		||||
      :src="props.noticeItem.imgUrl"
 | 
			
		||||
      :src="props.noticeItem.avatar"
 | 
			
		||||
      class="notice-container-avatar"
 | 
			
		||||
    ></el-avatar>
 | 
			
		||||
    <div class="notice-container-text">
 | 
			
		||||
      <div class="container-text-title">{{ props.noticeItem.title }}</div>
 | 
			
		||||
      <div class="container-text-description">
 | 
			
		||||
        {{ props.noticeItem.description }}
 | 
			
		||||
      <div class="notice-text-title">
 | 
			
		||||
        <el-tooltip
 | 
			
		||||
          popper-class="notice-title-popper"
 | 
			
		||||
          :disabled="!titleTooltip"
 | 
			
		||||
          :content="props.noticeItem.title"
 | 
			
		||||
          placement="top-start"
 | 
			
		||||
        >
 | 
			
		||||
          <div
 | 
			
		||||
            ref="titleRef"
 | 
			
		||||
            class="notice-title-content"
 | 
			
		||||
            @mouseover="hoverTitle"
 | 
			
		||||
          >
 | 
			
		||||
            {{ props.noticeItem.title }}
 | 
			
		||||
          </div>
 | 
			
		||||
        </el-tooltip>
 | 
			
		||||
        <el-tag
 | 
			
		||||
          v-if="props.noticeItem?.extra"
 | 
			
		||||
          :type="props.noticeItem?.status"
 | 
			
		||||
          size="small"
 | 
			
		||||
          class="notice-title-extra"
 | 
			
		||||
          >{{ props.noticeItem?.extra }}
 | 
			
		||||
        </el-tag>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <el-tooltip
 | 
			
		||||
        popper-class="notice-title-popper"
 | 
			
		||||
        :disabled="!descriptionTooltip"
 | 
			
		||||
        :content="props.noticeItem.description"
 | 
			
		||||
        placement="top-start"
 | 
			
		||||
      >
 | 
			
		||||
        <div
 | 
			
		||||
          ref="descriptionRef"
 | 
			
		||||
          class="notice-text-description"
 | 
			
		||||
          @mouseover="hoverDescription($event, props.noticeItem.description)"
 | 
			
		||||
        >
 | 
			
		||||
          {{ props.noticeItem.description }}
 | 
			
		||||
        </div>
 | 
			
		||||
      </el-tooltip>
 | 
			
		||||
      <div class="notice-text-datetime">
 | 
			
		||||
        {{ props.noticeItem.datetime }}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
.notice-title-popper {
 | 
			
		||||
  max-width: 238px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
.notice-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
@ -36,6 +113,7 @@ const props = defineProps({
 | 
			
		||||
 | 
			
		||||
  .notice-container-avatar {
 | 
			
		||||
    margin-right: 16px;
 | 
			
		||||
    background: #fff;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .notice-container-text {
 | 
			
		||||
@ -44,18 +122,47 @@ const props = defineProps({
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    flex: 1;
 | 
			
		||||
 | 
			
		||||
    .container-text-title {
 | 
			
		||||
      margin-bottom: 4px;
 | 
			
		||||
      color: rgba(0, 0, 0, 0.65);
 | 
			
		||||
    .notice-text-title {
 | 
			
		||||
      display: flex;
 | 
			
		||||
      margin-bottom: 8px;
 | 
			
		||||
      font-weight: 400;
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      line-height: 22px;
 | 
			
		||||
      line-height: 1.5715;
 | 
			
		||||
      color: rgba(0, 0, 0, 0.85);
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
 | 
			
		||||
      .notice-title-content {
 | 
			
		||||
        flex: 1;
 | 
			
		||||
        width: 200px;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
        text-overflow: ellipsis;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .notice-title-extra {
 | 
			
		||||
        float: right;
 | 
			
		||||
        margin-top: -1.5px;
 | 
			
		||||
        font-weight: 400;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .container-text-description {
 | 
			
		||||
    .notice-text-description,
 | 
			
		||||
    .notice-text-datetime {
 | 
			
		||||
      font-size: 12px;
 | 
			
		||||
      line-height: 1.5715;
 | 
			
		||||
      color: rgba(0, 0, 0, 0.45);
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      line-height: 22px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .notice-text-description {
 | 
			
		||||
      display: -webkit-box;
 | 
			
		||||
      text-overflow: ellipsis;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
      -webkit-line-clamp: 2;
 | 
			
		||||
      -webkit-box-orient: vertical;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .notice-text-datetime {
 | 
			
		||||
      margin-top: 4px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { PropType } from "vue";
 | 
			
		||||
import NoticeItem from "./noticeItem.vue";
 | 
			
		||||
import { noticeItemType } from "../../types";
 | 
			
		||||
import { ListItem } from "./data";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  list: {
 | 
			
		||||
    type: Array as PropType<Array<noticeItemType>>,
 | 
			
		||||
    type: Array as PropType<Array<ListItem>>,
 | 
			
		||||
    default: () => []
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -73,9 +73,3 @@ export type themeColorsType = {
 | 
			
		||||
  rgb: string;
 | 
			
		||||
  themeColor: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type noticeItemType = {
 | 
			
		||||
  imgUrl: string;
 | 
			
		||||
  title: string;
 | 
			
		||||
  description: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user