refactor: 重构layout文件命名规范,更易读 (#1110)

This commit is contained in:
xiaoming
2024-04-30 22:27:54 +08:00
committed by GitHub
parent 2f9bc7e187
commit b8159a0d73
53 changed files with 332 additions and 378 deletions

View 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>

View 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>

View 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")
}
];

View 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>