Browse Source

perf: 同步主分支代码

i18n
xiaoxian521 3 years ago
parent
commit
c2aa7d9428
  1. 9
      src/layout/components/navbar.vue
  2. 146
      src/layout/components/notice/data.ts
  3. 80
      src/layout/components/notice/index.vue
  4. 168
      src/layout/components/notice/noticeItem.vue
  5. 23
      src/layout/components/notice/noticeList.vue
  6. 3
      src/layout/components/sidebar/horizontal.vue
  7. 27
      src/plugins/element-plus/index.ts
  8. 9
      src/style/sidebar.scss

9
src/layout/components/navbar.vue

@ -5,6 +5,7 @@ import Hamburger from "./sidebar/hamBurger.vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { storageSession } from "/@/utils/storage"; import { storageSession } from "/@/utils/storage";
import Breadcrumb from "./sidebar/breadCrumb.vue"; import Breadcrumb from "./sidebar/breadCrumb.vue";
import Notice from "./notice/index.vue";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import { unref, watch, getCurrentInstance } from "vue"; import { unref, watch, getCurrentInstance } from "vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
@ -70,6 +71,8 @@ function translationEn() {
<Breadcrumb class="breadcrumb-container" /> <Breadcrumb class="breadcrumb-container" />
<div class="vertical-header-right"> <div class="vertical-header-right">
<!-- 通知 -->
<Notice />
<!-- 全屏 --> <!-- 全屏 -->
<screenfull v-show="!deviceDetection()" /> <screenfull v-show="!deviceDetection()" />
<!-- 国际化 --> <!-- 国际化 -->
@ -156,6 +159,12 @@ function translationEn() {
color: #000000d9; color: #000000d9;
justify-content: flex-end; justify-content: flex-end;
:deep(.dropdown-badge) {
&:hover {
background: #f6f6f6;
}
}
.screen-full { .screen-full {
cursor: pointer; cursor: pointer;

146
src/layout/components/notice/data.ts

@ -0,0 +1,146 @@
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"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
title: "左侧图标用于区分不同的类型",
datetime: "一年前",
description: "",
type: "1"
},
{
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png",
title: "左侧图标用于区分不同的类型",
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"
}
]
}
];

80
src/layout/components/notice/index.vue

@ -0,0 +1,80 @@
<script setup lang="ts">
import { ref } from "vue";
import NoticeList from "./noticeList.vue";
import { noticesData } from "./data";
const activeName = ref(noticesData[0].name);
const notices = ref(noticesData);
let noticesNum = ref(0);
notices.value.forEach(notice => {
noticesNum.value += notice.list.length;
});
</script>
<template>
<el-dropdown trigger="click" placement="bottom-end">
<span class="dropdown-badge">
<el-badge :value="noticesNum" :max="99">
<el-icon class="header-notice-icon"><bell /></el-icon>
</el-badge>
</span>
<template #dropdown>
<el-dropdown-menu>
<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"
>
<el-scrollbar max-height="330px">
<div class="noticeList-container">
<NoticeList :list="item.list" />
</div>
</el-scrollbar>
</el-tab-pane>
</template>
</el-tabs>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<style lang="scss" scoped>
.dropdown-badge {
display: flex;
align-items: center;
justify-content: center;
height: 48px;
width: 60px;
cursor: pointer;
.header-notice-icon {
font-size: 18px;
}
}
.dropdown-tabs {
width: 336px;
background-color: #fff;
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
border-radius: 4px;
:deep(.el-tabs__header) {
margin: 0;
}
:deep(.el-tabs__nav-scroll) {
display: flex;
justify-content: center;
}
:deep(.el-tabs__nav-wrap)::after {
height: 1px;
}
:deep(.noticeList-container) {
padding: 15px 24px 0 24px;
}
}
</style>

168
src/layout/components/notice/noticeItem.vue

@ -0,0 +1,168 @@
<script setup lang="ts">
import { ListItem } from "./data";
import { ref, PropType, nextTick } from "vue";
const props = defineProps({
noticeItem: {
type: Object as PropType<ListItem>,
default: () => {}
}
});
const titleRef = ref(null);
const descriptionRef = ref(null);
const titleTooltip = ref(false);
const descriptionTooltip = ref(false);
function hoverTitle() {
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.avatar"
class="notice-container-avatar"
></el-avatar>
<div class="notice-container-text">
<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;
align-items: flex-start;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
.notice-container-avatar {
margin-right: 16px;
background: #fff;
}
.notice-container-text {
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
.notice-text-title {
display: flex;
margin-bottom: 8px;
font-weight: 400;
font-size: 14px;
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;
}
}
.notice-text-description,
.notice-text-datetime {
font-size: 12px;
line-height: 1.5715;
color: rgba(0, 0, 0, 0.45);
}
.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;
}
}
}
</style>

23
src/layout/components/notice/noticeList.vue

@ -0,0 +1,23 @@
<script setup lang="ts">
import { PropType } from "vue";
import NoticeItem from "./noticeItem.vue";
import { ListItem } from "./data";
const props = defineProps({
list: {
type: Array as PropType<Array<ListItem>>,
default: () => []
}
});
</script>
<template>
<div v-if="props.list.length">
<NoticeItem
v-for="(item, index) in props.list"
:noticeItem="item"
:key="index"
></NoticeItem>
</div>
<el-empty v-else description="暂无数据"></el-empty>
</template>

3
src/layout/components/sidebar/horizontal.vue

@ -9,6 +9,7 @@ import {
} from "vue"; } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import Notice from "../notice/index.vue";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import SidebarItem from "./sidebarItem.vue"; import SidebarItem from "./sidebarItem.vue";
import { algorithm } from "/@/utils/algorithm"; import { algorithm } from "/@/utils/algorithm";
@ -138,6 +139,8 @@ onMounted(() => {
/> />
</el-menu> </el-menu>
<div class="horizontal-header-right"> <div class="horizontal-header-right">
<!-- 通知 -->
<Notice />
<!-- 全屏 --> <!-- 全屏 -->
<screenfull v-show="!deviceDetection()" /> <screenfull v-show="!deviceDetection()" />
<!-- 国际化 --> <!-- 国际化 -->

27
src/plugins/element-plus/index.ts

@ -23,7 +23,6 @@ import {
ElInput, ElInput,
ElForm, ElForm,
ElFormItem, ElFormItem,
ElLoading,
ElPopover, ElPopover,
ElPopper, ElPopper,
ElTooltip, ElTooltip,
@ -36,7 +35,15 @@ import {
ElDescriptions, ElDescriptions,
ElDescriptionsItem, ElDescriptionsItem,
ElBacktop, ElBacktop,
ElSwitch
ElSwitch,
ElBadge,
ElTabs,
ElTabPane,
ElAvatar,
ElEmpty,
// 指令
ElLoading,
ElInfiniteScroll
} from "element-plus"; } from "element-plus";
// https://element-plus.org/zh-CN/component/icon.html // https://element-plus.org/zh-CN/component/icon.html
@ -54,7 +61,8 @@ import {
RefreshRight, RefreshRight,
ArrowDown, ArrowDown,
Close, Close,
CloseBold
CloseBold,
Bell
} from "@element-plus/icons"; } from "@element-plus/icons";
const components = [ const components = [
@ -93,9 +101,13 @@ const components = [
ElDescriptions, ElDescriptions,
ElDescriptionsItem, ElDescriptionsItem,
ElBacktop, ElBacktop,
ElSwitch
ElSwitch,
ElBadge,
ElTabs,
ElTabPane,
ElAvatar,
ElEmpty
]; ];
// icon // icon
export const iconComponents = [ export const iconComponents = [
Check, Check,
@ -111,10 +123,11 @@ export const iconComponents = [
RefreshRight, RefreshRight,
ArrowDown, ArrowDown,
Close, Close,
CloseBold
CloseBold,
Bell
]; ];
const plugins = [ElLoading];
const plugins = [ElLoading, ElInfiniteScroll];
export function useElementPlus(app: App) { export function useElementPlus(app: App) {
components.push(...iconComponents); components.push(...iconComponents);

9
src/style/sidebar.scss

@ -212,6 +212,15 @@
color: $subMenuActiveText; color: $subMenuActiveText;
justify-content: flex-end; justify-content: flex-end;
.dropdown-badge {
height: 62px;
color: $subMenuActiveText;
&:hover {
background: $menuHover;
}
}
.screen-full { .screen-full {
cursor: pointer; cursor: pointer;

Loading…
Cancel
Save