Files
cn-rdms-web/src/views/workbench/modules/workbench-banner.vue

303 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { computed, ref } from 'vue';
import dayjs from 'dayjs';
import { useAuthStore } from '@/store/modules/auth';
import { getGreeting } from '../homepage';
defineOptions({ name: 'WorkbenchBanner' });
interface NoticeRow {
id: string;
title: string;
timeLabel: string;
}
const authStore = useAuthStore();
const displayName = computed(() => authStore.userInfo.nickname || authStore.userInfo.userName || '同学');
const greeting = computed(() => getGreeting());
const weekdayNames = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日'];
const dateContext = computed(() => {
const now = dayjs();
return {
date: now.format('YYYY-MM-DD'),
weekday: weekdayNames[now.isoWeekday()] ?? '',
week: `${now.isoWeek()}`
};
});
// 公告 mockbanner 阶段本地维护,等公告中心接口落地再迁移至 mock.ts
const allNotices: NoticeRow[] = [
{ id: 'n1', title: '【运维】本周六 02:00-04:00 数据库主从切换', timeLabel: '2 天前' },
{ id: 'n2', title: '【HR】Q2 OKR 复盘截止 06-05', timeLabel: '3 天前' },
{ id: 'n3', title: '【流程】工单 SLA 新规则即将上线', timeLabel: '1 周前' },
{ id: 'n4', title: '【系统】新版本 25.06 发布日程公告', timeLabel: '2 周前' },
{ id: 'n5', title: '【行政】6 月端午节放假安排', timeLabel: '3 周前' },
{ id: 'n6', title: '【安全】禁止使用未受控外部 AI 工具处理客户数据', timeLabel: '1 个月前' }
];
const previewNotices = computed(() => allNotices.slice(0, 3));
const drawerOpen = ref(false);
function openDrawer() {
drawerOpen.value = true;
}
function closeDrawer() {
drawerOpen.value = false;
}
</script>
<template>
<section class="workbench-banner">
<div class="workbench-banner__identity">
<h1 class="workbench-banner__title">{{ greeting }}{{ displayName }}</h1>
<p class="workbench-banner__meta">
{{ dateContext.date }} {{ dateContext.weekday }}
<span class="workbench-banner__meta-dot">·</span>
{{ dateContext.week }}
</p>
</div>
<div class="workbench-banner__notice">
<header class="workbench-banner__notice-header">
<span class="workbench-banner__notice-title">
<SvgIcon icon="mdi:bullhorn-outline" class="workbench-banner__notice-icon" />
公告
<span class="workbench-banner__notice-count">{{ allNotices.length }}</span>
</span>
<button class="workbench-banner__notice-more" type="button" @click="openDrawer">
更多
<SvgIcon icon="mdi:arrow-right" />
</button>
</header>
<ul class="workbench-banner__notice-list">
<li v-for="row in previewNotices" :key="row.id" class="workbench-banner__notice-row">
<span class="workbench-banner__notice-row-title">{{ row.title }}</span>
<span class="workbench-banner__notice-row-time">{{ row.timeLabel }}</span>
</li>
</ul>
</div>
<ElDrawer v-model="drawerOpen" title="全部公告" size="480px">
<ElScrollbar>
<ul class="workbench-banner__drawer-list">
<li v-for="row in allNotices" :key="row.id" class="workbench-banner__drawer-row">
<div class="workbench-banner__drawer-row-title">{{ row.title }}</div>
<div class="workbench-banner__drawer-row-time">{{ row.timeLabel }}</div>
</li>
</ul>
</ElScrollbar>
<template #footer>
<ElButton @click="closeDrawer">关闭</ElButton>
</template>
</ElDrawer>
</section>
</template>
<style scoped>
.workbench-banner {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
align-items: stretch;
gap: 24px;
padding: 22px 28px;
border: 1px solid rgb(226 232 240 / 88%);
border-radius: 20px;
background-color: rgb(255 255 255 / 96%);
}
.workbench-banner__identity {
display: flex;
flex-direction: column;
justify-content: center;
gap: 8px;
min-width: 0;
}
.workbench-banner__title {
margin: 0;
color: rgb(15 23 42 / 98%);
font-size: 26px;
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.01em;
}
.workbench-banner__meta {
margin: 0;
color: rgb(100 116 139 / 92%);
font-size: 13px;
}
.workbench-banner__meta-dot {
margin: 0 6px;
color: rgb(203 213 225 / 96%);
}
.workbench-banner__notice {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 0;
padding-left: 24px;
border-left: 1px solid rgb(226 232 240 / 88%);
}
.workbench-banner__notice-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.workbench-banner__notice-title {
display: inline-flex;
align-items: center;
gap: 6px;
color: rgb(15 23 42 / 96%);
font-size: 14px;
font-weight: 600;
}
.workbench-banner__notice-icon {
color: rgb(14 116 144 / 92%);
font-size: 16px;
}
.workbench-banner__notice-count {
display: inline-flex;
align-items: center;
padding: 1px 8px;
border-radius: 999px;
background-color: rgb(241 245 249 / 96%);
color: rgb(71 85 105 / 96%);
font-size: 11px;
font-weight: 500;
}
.workbench-banner__notice-more {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 2px 6px;
border: none;
border-radius: 6px;
background-color: transparent;
color: rgb(14 116 144 / 96%);
font-size: 13px;
cursor: pointer;
transition:
background-color 160ms ease,
color 160ms ease;
}
.workbench-banner__notice-more:hover {
background-color: rgb(236 254 255 / 92%);
color: rgb(8 90 110 / 96%);
}
.workbench-banner__notice-more:focus-visible {
outline: 2px solid rgb(14 116 144 / 60%);
outline-offset: 2px;
}
.workbench-banner__notice-list {
display: flex;
flex-direction: column;
gap: 2px;
margin: 0;
padding: 0;
list-style: none;
max-height: 108px;
overflow-y: auto;
}
.workbench-banner__notice-list::-webkit-scrollbar {
width: 6px;
}
.workbench-banner__notice-list::-webkit-scrollbar-thumb {
background-color: rgb(203 213 225 / 70%);
border-radius: 3px;
}
.workbench-banner__notice-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
gap: 12px;
padding: 6px 0;
border-bottom: 1px dashed rgb(226 232 240 / 70%);
}
.workbench-banner__notice-row:last-child {
border-bottom: none;
}
.workbench-banner__notice-row-title {
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: rgb(30 41 59 / 96%);
font-size: 13px;
}
.workbench-banner__notice-row-time {
color: rgb(100 116 139 / 92%);
font-size: 12px;
white-space: nowrap;
}
.workbench-banner__drawer-list {
margin: 0;
padding: 0 4px 0 0;
list-style: none;
}
.workbench-banner__drawer-row {
padding: 12px 0;
border-bottom: 1px solid rgb(226 232 240 / 80%);
}
.workbench-banner__drawer-row:last-child {
border-bottom: none;
}
.workbench-banner__drawer-row-title {
color: rgb(15 23 42 / 96%);
font-size: 14px;
line-height: 1.5;
}
.workbench-banner__drawer-row-time {
margin-top: 4px;
color: rgb(100 116 139 / 92%);
font-size: 12px;
}
@media (width <= 1024px) {
.workbench-banner {
grid-template-columns: 1fr;
}
.workbench-banner__notice {
padding-left: 0;
padding-top: 16px;
border-left: none;
border-top: 1px solid rgb(226 232 240 / 88%);
}
}
@media (width <= 768px) {
.workbench-banner {
padding: 18px 20px;
}
.workbench-banner__title {
font-size: 22px;
}
}
</style>