mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-11-15 14:03:36 +08:00
refactor: 重构layout文件命名规范,更易读 (#1110)
This commit is contained in:
177
src/layout/components/lay-notice/components/NoticeItem.vue
Normal file
177
src/layout/components/lay-notice/components/NoticeItem.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<script setup lang="ts">
|
||||
import { ListItem } from "../data";
|
||||
import { ref, PropType, nextTick } from "vue";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import { deviceDetection } from "@pureadmin/utils";
|
||||
|
||||
const props = defineProps({
|
||||
noticeItem: {
|
||||
type: Object as PropType<ListItem>,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const titleRef = ref(null);
|
||||
const titleTooltip = ref(false);
|
||||
const descriptionRef = ref(null);
|
||||
const descriptionTooltip = ref(false);
|
||||
const { tooltipEffect } = useNav();
|
||||
const isMobile = deviceDetection();
|
||||
|
||||
function hoverTitle() {
|
||||
nextTick(() => {
|
||||
titleRef.value?.scrollWidth > titleRef.value?.clientWidth
|
||||
? (titleTooltip.value = true)
|
||||
: (titleTooltip.value = false);
|
||||
});
|
||||
}
|
||||
|
||||
function hoverDescription(event, description) {
|
||||
// currentWidth 为文本在页面中所占的宽度,创建标签,加入到页面,获取currentWidth ,最后在移除
|
||||
const tempTag = document.createElement("span");
|
||||
tempTag.innerText = description;
|
||||
tempTag.className = "getDescriptionWidth";
|
||||
document.querySelector("body").appendChild(tempTag);
|
||||
const 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 border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]"
|
||||
>
|
||||
<el-avatar
|
||||
v-if="noticeItem.avatar"
|
||||
:size="30"
|
||||
:src="noticeItem.avatar"
|
||||
class="notice-container-avatar"
|
||||
/>
|
||||
<div class="notice-container-text">
|
||||
<div class="notice-text-title text-[#000000d9] dark:text-white">
|
||||
<el-tooltip
|
||||
popper-class="notice-title-popper"
|
||||
:effect="tooltipEffect"
|
||||
:disabled="!titleTooltip"
|
||||
:content="noticeItem.title"
|
||||
placement="top-start"
|
||||
:enterable="!isMobile"
|
||||
>
|
||||
<div
|
||||
ref="titleRef"
|
||||
class="notice-title-content"
|
||||
@mouseover="hoverTitle"
|
||||
>
|
||||
{{ noticeItem.title }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tag
|
||||
v-if="noticeItem?.extra"
|
||||
:type="noticeItem?.status"
|
||||
size="small"
|
||||
class="notice-title-extra"
|
||||
>
|
||||
{{ noticeItem?.extra }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<el-tooltip
|
||||
popper-class="notice-title-popper"
|
||||
:effect="tooltipEffect"
|
||||
:disabled="!descriptionTooltip"
|
||||
:content="noticeItem.description"
|
||||
placement="top-start"
|
||||
>
|
||||
<div
|
||||
ref="descriptionRef"
|
||||
class="notice-text-description"
|
||||
@mouseover="hoverDescription($event, noticeItem.description)"
|
||||
>
|
||||
{{ noticeItem.description }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div class="notice-text-datetime text-[#00000073] dark:text-white">
|
||||
{{ 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: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.notice-text-title {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5715;
|
||||
cursor: pointer;
|
||||
|
||||
.notice-title-content {
|
||||
flex: 1;
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.notice-text-description {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.notice-text-datetime {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
24
src/layout/components/lay-notice/components/NoticeList.vue
Normal file
24
src/layout/components/lay-notice/components/NoticeList.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { PropType } from "vue";
|
||||
import { ListItem } from "../data";
|
||||
import NoticeItem from "./NoticeItem.vue";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
|
||||
defineProps({
|
||||
list: {
|
||||
type: Array as PropType<Array<ListItem>>,
|
||||
default: () => []
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="list.length">
|
||||
<NoticeItem v-for="(item, index) in list" :key="index" :noticeItem="item" />
|
||||
</div>
|
||||
<el-empty v-else :description="transformI18n(emptyText)" />
|
||||
</template>
|
||||
99
src/layout/components/lay-notice/data.ts
Normal file
99
src/layout/components/lay-notice/data.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { $t } from "@/plugins/i18n";
|
||||
|
||||
export interface ListItem {
|
||||
avatar: string;
|
||||
title: string;
|
||||
datetime: string;
|
||||
type: string;
|
||||
description: string;
|
||||
status?: "primary" | "success" | "warning" | "info" | "danger";
|
||||
extra?: string;
|
||||
}
|
||||
|
||||
export interface TabItem {
|
||||
key: string;
|
||||
name: string;
|
||||
list: ListItem[];
|
||||
emptyText: string;
|
||||
}
|
||||
|
||||
export const noticesData: TabItem[] = [
|
||||
{
|
||||
key: "1",
|
||||
name: $t("status.pureNotify"),
|
||||
list: [],
|
||||
emptyText: $t("status.pureNoNotify")
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
name: $t("status.pureMessage"),
|
||||
list: [
|
||||
{
|
||||
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile1.svg",
|
||||
title: "小铭 评论了你",
|
||||
description: "诚在于心,信在于行,诚信在于心行合一。",
|
||||
datetime: "今天",
|
||||
type: "2"
|
||||
},
|
||||
{
|
||||
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile2.svg",
|
||||
title: "李白 回复了你",
|
||||
description: "长风破浪会有时,直挂云帆济沧海。",
|
||||
datetime: "昨天",
|
||||
type: "2"
|
||||
},
|
||||
{
|
||||
avatar: "https://xiaoxian521.github.io/hyperlink/svg/smile5.svg",
|
||||
title: "标题",
|
||||
description:
|
||||
"请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容",
|
||||
datetime: "时间",
|
||||
type: "2"
|
||||
}
|
||||
],
|
||||
emptyText: $t("status.pureNoMessage")
|
||||
},
|
||||
{
|
||||
key: "3",
|
||||
name: $t("status.pureTodo"),
|
||||
list: [
|
||||
{
|
||||
avatar: "",
|
||||
title: "第三方紧急代码变更",
|
||||
description:
|
||||
"小林提交于 2024-05-10,需在 2024-05-11 前完成代码变更任务",
|
||||
datetime: "",
|
||||
extra: "马上到期",
|
||||
status: "danger",
|
||||
type: "3"
|
||||
},
|
||||
{
|
||||
avatar: "",
|
||||
title: "版本发布",
|
||||
description: "指派小铭于 2024-06-18 前完成更新并发布",
|
||||
datetime: "",
|
||||
extra: "已耗时 8 天",
|
||||
status: "warning",
|
||||
type: "3"
|
||||
},
|
||||
{
|
||||
avatar: "",
|
||||
title: "新功能开发",
|
||||
description: "开发多租户管理",
|
||||
datetime: "",
|
||||
extra: "进行中",
|
||||
type: "3"
|
||||
},
|
||||
{
|
||||
avatar: "",
|
||||
title: "任务名称",
|
||||
description: "任务需要在 2030-10-30 10:00 前启动",
|
||||
datetime: "",
|
||||
extra: "未开始",
|
||||
status: "info",
|
||||
type: "3"
|
||||
}
|
||||
],
|
||||
emptyText: $t("status.pureNoTodo")
|
||||
}
|
||||
];
|
||||
98
src/layout/components/lay-notice/index.vue
Normal file
98
src/layout/components/lay-notice/index.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref, computed } from "vue";
|
||||
import { noticesData } from "./data";
|
||||
import NoticeList from "./components/NoticeList.vue";
|
||||
import BellIcon from "@iconify-icons/ep/bell";
|
||||
|
||||
const { t } = useI18n();
|
||||
const noticesNum = ref(0);
|
||||
const notices = ref(noticesData);
|
||||
const activeKey = ref(noticesData[0]?.key);
|
||||
|
||||
notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
|
||||
const getLabel = computed(
|
||||
() => item =>
|
||||
t(item.name) + (item.list.length > 0 ? `(${item.list.length})` : "")
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dropdown trigger="click" placement="bottom-end">
|
||||
<span
|
||||
:class="[
|
||||
'dropdown-badge',
|
||||
'navbar-bg-hover',
|
||||
'select-none',
|
||||
Number(noticesNum) !== 0 && 'mr-[10px]'
|
||||
]"
|
||||
>
|
||||
<el-badge :value="Number(noticesNum) === 0 ? '' : noticesNum" :max="99">
|
||||
<span class="header-notice-icon">
|
||||
<IconifyIconOffline :icon="BellIcon" />
|
||||
</span>
|
||||
</el-badge>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-tabs
|
||||
v-model="activeKey"
|
||||
:stretch="true"
|
||||
class="dropdown-tabs"
|
||||
:style="{ width: notices.length === 0 ? '200px' : '330px' }"
|
||||
>
|
||||
<el-empty
|
||||
v-if="notices.length === 0"
|
||||
:description="t('status.pureNoMessage')"
|
||||
:image-size="60"
|
||||
/>
|
||||
<span v-else>
|
||||
<template v-for="item in notices" :key="item.key">
|
||||
<el-tab-pane :label="getLabel(item)" :name="`${item.key}`">
|
||||
<el-scrollbar max-height="330px">
|
||||
<div class="noticeList-container">
|
||||
<NoticeList :list="item.list" :emptyText="item.emptyText" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
</span>
|
||||
</el-tabs>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dropdown-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
|
||||
.header-notice-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-tabs {
|
||||
.noticeList-container {
|
||||
padding: 15px 24px 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__header) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap)::after {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap) {
|
||||
padding: 0 36px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user