fix: layouts name fix (#24)

This commit is contained in:
PY 2021-12-12 16:01:18 +08:00 committed by GitHub
parent baf43fb503
commit 7fe368c874
3 changed files with 671 additions and 0 deletions

View File

@ -0,0 +1,351 @@
<template>
<div :class="layoutCls">
<t-head-menu :class="menuCls" :theme="theme" expand-type="popup" :value="active">
<template #logo>
<span v-if="showLogo" class="header-logo-container" @click="goHome">
<tLogoFull class="t-logo" />
</span>
<div v-else class="header-operate-left">
<t-button theme="default" shape="square" variant="text" @click="changeCollapsed">
<t-icon v-show="isSidebarCompact" class="collapsed-icon" name="menu-fold" />
<t-icon v-show="!isSidebarCompact" class="collapsed-icon" name="menu-unfold" />
</t-button>
<search :layout="layout" />
</div>
</template>
<sub-menu v-show="layout !== 'side'" class="header-menu" :nav-data="menu" />
<template #operations>
<div class="operations-container">
<!-- 搜索框 -->
<search v-if="layout !== 'side'" :layout="layout" />
<t-dropdown :min-column-width="135" trigger="click">
<template #dropdown>
<t-dropdown-menu>
<t-dropdown-item class="operations-dropdown-container-item" @click="handleNav('/user/index')">
<t-icon name="user-circle"></t-icon>
</t-dropdown-item>
<t-dropdown-item class="operations-dropdown-container-item" @click="handleLogout">
<t-icon name="poweroff"></t-icon>退
</t-dropdown-item>
</t-dropdown-menu>
</template>
<t-button class="header-user-btn" theme="default" variant="text">
<template #icon>
<t-icon class="header-user-avatar" name="user-circle" />
</template>
<div class="header-user-account">
Tencent
<t-icon name="chevron-down" />
</div>
</t-button>
</t-dropdown>
<!-- 全局通知 -->
<notice />
<t-tooltip placement="bottom" content="代码仓库">
<t-button theme="default" shape="square" variant="text" @click="navToGitHub">
<t-icon name="logo-github" />
</t-button>
</t-tooltip>
<t-tooltip placement="bottom" content="帮助文档">
<t-button theme="default" shape="square" variant="text" @click="navToHelper">
<t-icon name="help-circle" />
</t-button>
</t-tooltip>
<t-tooltip placement="bottom" content="系统设置">
<t-button theme="default" shape="square" variant="text">
<t-icon name="setting" @click="toggleSettingPanel" />
</t-button>
</t-tooltip>
</div>
</template>
</t-head-menu>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed, ref } from 'vue';
import { useStore } from 'vuex';
import { useRouter, useRoute } from 'vue-router';
import { PREFIX } from '@/config/global';
import tLogoFull from '@/assets/assets-logo-full.svg?component';
import { MenuRoute } from '@/interface';
import Notice from './Notice.vue';
import Search from './Search.vue';
import SubMenu from './SubMenu';
export default defineComponent({
components: {
tLogoFull,
Notice,
Search,
SubMenu,
},
props: {
theme: {
type: String as PropType<string>,
default: '',
},
layout: {
type: String as PropType<string>,
default: 'top',
},
showLogo: {
type: Boolean as PropType<boolean>,
default: true,
},
menu: {
type: Array as PropType<MenuRoute[]>,
default: () => [],
},
isFixed: {
type: Boolean as PropType<boolean>,
default: false,
},
isCompact: {
type: Boolean as PropType<boolean>,
default: false,
},
maxLevel: {
type: Number as PropType<number>,
default: 3,
},
},
setup(props) {
const store = useStore();
const router = useRouter();
const toggleSettingPanel = () => {
store.commit('setting/toggleSettingPanel', true);
};
const goHome = () => {
router.push('/dashboard/base');
};
const active = computed(() => {
const route = useRoute();
if (!route.path) {
return '';
}
return route.path
.split('/')
.filter((item, index) => index <= props.maxLevel && index > 0)
.map((item) => `/${item}`)
.join('');
});
const showMenu = computed(() => !(props.layout === 'mix' && props.showLogo));
const layoutCls = computed(() => [`${PREFIX}-header-layout`]);
const menuCls = computed(() => {
const { isFixed, layout, isCompact } = props;
return [
{
[`${PREFIX}-header-menu`]: !isFixed,
[`${PREFIX}-header-menu-fixed`]: isFixed,
[`${PREFIX}-header-menu-fixed-side`]: layout === 'side' && isFixed,
[`${PREFIX}-header-menu-fixed-side-compact`]: layout === 'side' && isFixed && isCompact,
},
];
});
const userVisible = ref(false);
const userVisibleChange = (value: boolean) => {
userVisible.value = value;
};
const changeCollapsed = () => {
store.commit('setting/toggleSidebarCompact');
};
const isSidebarCompact = computed(() => store.state.setting.isSidebarCompact);
const handleNav = (url) => {
router.push(url);
};
const handleLogout = () => {
router.push(`/login?redirect=${router.currentRoute.value.fullPath}`);
};
const navToGitHub = () => {
window.open('https://github.com/TDesignOteam/tdesign-vue-next-starter');
};
const navToHelper = () => {
window.open('http://tdesign.tencent.com/starter/get-started.html');
};
return {
isSidebarCompact,
goHome,
toggleSettingPanel,
active,
showMenu,
layoutCls,
userVisible,
userVisibleChange,
menuCls,
changeCollapsed,
handleNav,
handleLogout,
navToGitHub,
navToHelper,
};
},
});
</script>
<style lang="less">
@import '@/style/variables.less';
.@{prefix}-header {
&-layout {
height: 64px;
}
&-menu-fixed {
position: fixed;
top: 0;
z-index: 10;
&-side {
left: 232px;
right: 0;
z-index: 10;
width: auto;
transition: all 0.3s;
&-compact {
left: 64px;
}
}
}
&-logo-container {
cursor: pointer;
display: inline-flex;
height: 64px;
}
.t-logo {
width: 32px;
&:hover {
cursor: pointer;
}
}
}
.header-menu {
flex: 1 1 1;
display: inline-flex;
}
.operations-container {
display: flex;
align-items: center;
margin-right: 12px;
.t-popup-reference {
display: flex;
align-items: center;
justify-content: center;
}
.t-button {
margin: 0 8px;
&.header-user-btn {
margin: 0;
}
}
.t-icon {
font-size: 20px;
&.general {
margin-right: 16px;
}
}
}
.header-operate-left {
display: flex;
margin-left: 20px;
align-items: center;
.collapsed-icon {
font-size: 20px;
}
}
.header-logo-container {
display: flex;
margin-left: 16px;
&:hover {
cursor: pointer;
}
}
.header-user-account {
display: inline-flex;
align-items: center;
color: @text-color-primary;
.t-icon {
margin-left: 4px;
font-size: 16px;
}
}
.t-menu--light {
.header-user-account {
color: @text-color-primary;
}
}
.t-menu--dark {
.header-user-account {
color: rgba(255, 255, 255, 0.55);
}
.t-button {
--ripple-color: var(--td-gray-color-10) !important;
&:hover {
background: var(--td-gray-color-12) !important;
}
}
}
.operations-dropdown-container-item {
width: 100%;
display: flex;
align-items: center;
.t-icon {
margin-right: 8px;
}
.t-dropdown__item {
.t-dropdown__item__content {
display: flex;
justify-content: center;
}
.t-dropdown__item__content__text {
display: flex;
align-items: center;
font-size: 14px;
}
}
.t-dropdown__item {
width: 100%;
margin-bottom: 0px;
}
&:last-child {
.t-dropdown__item {
margin-bottom: 8px;
}
}
}
</style>

View File

@ -0,0 +1,207 @@
<template>
<t-popup expand-animation placement="bottom-right" trigger="click">
<template #content>
<div class="header-msg">
<div class="header-msg-top">
<p>通知</p>
<t-button v-if="unreadMsg.length > 0" class="clear-btn" variant="text" theme="primary" @click="setRead('all')"
>清空</t-button
>
</div>
<t-list v-if="unreadMsg.length > 0" class="narrow-scrollbar" :split="true">
<t-list-item v-for="(item, index) in unreadMsg" :key="index">
<div>
<p class="msg-content">{{ item.content }}</p>
<p class="msg-type">{{ item.type }}</p>
</div>
<p class="msg-time">{{ item.date }}</p>
<template #action>
<t-button size="small" variant="outline" @click="setRead('radio', item)"> 设为已读 </t-button>
</template>
</t-list-item>
</t-list>
<div v-else class="empty-list">
<img src="https://tdesign.gtimg.com/pro-template/personal/nothing.png" alt="空" />
<p>暂无通知</p>
</div>
<div class="header-msg-bottom">
<t-button
v-if="unreadMsg.length > 0"
class="header-msg-bottom-link"
variant="text"
theme="primary"
@click="goDetail"
>查看全部</t-button
>
</div>
</div>
</template>
<t-badge :count="unreadMsg.length" :offset="[15, 21]">
<t-button theme="default" shape="square" variant="text">
<t-icon name="mail" />
</t-button>
</t-badge>
</t-popup>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { NotificationItem } from '@/interface';
export default defineComponent({
setup() {
const store = useStore();
const { msgData } = store.state.notification;
const unreadMsg = computed(() => store.getters['notification/unreadMsg']);
const setRead = (type: string, item?: NotificationItem) => {
const changeMsg = msgData;
if (type === 'all') {
changeMsg.forEach((e: NotificationItem) => {
e.status = false;
});
} else {
changeMsg.forEach((e: NotificationItem) => {
if (e.id === item?.id) {
e.status = false;
}
});
}
store.commit('notification/setMsgData', changeMsg);
};
const goDetail = () => {
const router = useRouter();
router.push('/detail/secondary');
};
return {
goDetail,
unreadMsg,
setRead,
};
},
});
</script>
<style lang="less" scoped>
@import '@/style/variables.less';
.header-msg {
width: 400px;
height: 500px;
.empty-list {
height: calc(100% - 104px);
text-align: center;
padding-top: 135px;
font-size: 14px;
color: @text-color-secondary;
img {
width: 63px;
}
p {
margin-top: 30px;
}
}
&-top {
position: relative;
height: 56px;
font-size: 16px;
color: @text-color-primary;
text-align: center;
line-height: 56px;
border-bottom: 1px solid @component-border;
.clear-btn {
position: absolute;
top: 12px;
right: 24px;
}
}
&-bottom {
height: 48px;
align-items: center;
display: flex;
justify-content: center;
&-link {
text-decoration: none;
font-size: 14px;
color: @brand-color;
line-height: 48px;
cursor: pointer;
}
}
.t-list {
height: calc(100% - 104px);
}
.t-list-item {
overflow: hidden;
width: 100%;
padding: 16px 24px;
border-radius: @border-radius;
font-size: 14px;
color: @text-color-primary;
line-height: 22px;
cursor: pointer;
&:hover {
transition: background 0.2s ease;
background: @bg-color-container-hover;
.msg-content {
color: @brand-color-8;
}
.t-list-item__action {
button {
bottom: 16px;
opacity: 1;
}
}
.msg-time {
bottom: -6px;
opacity: 0;
}
}
.msg-content {
margin-bottom: 16px;
}
.msg-type {
color: @text-color-secondary;
}
.t-list-item__action {
button {
opacity: 0;
position: absolute;
right: 24px;
bottom: -6px;
}
}
.msg-time {
transition: all 0.2s ease;
opacity: 1;
position: absolute;
right: 24px;
bottom: 16px;
color: @text-color-secondary;
}
}
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div v-if="layout === 'side'" class="header-menu-search">
<t-input
:class="{ 'hover-active': isSearchFocus }"
placeholder="请输入搜索内容"
@blur="changeSearchFocus(false)"
@focus="changeSearchFocus(true)"
>
<template #prefix-icon>
<t-icon class="icon" name="search" size="16" />
</template>
</t-input>
</div>
<div v-else>
<t-button
:class="{ 'search-icon-hide': isSearchFocus }"
theme="default"
shape="square"
variant="text"
@click="changeSearchFocus(true)"
>
<t-icon name="search" />
</t-button>
<t-input
v-model="searchData"
:class="['header-search', { 'width-zero': !isSearchFocus }]"
placeholder="输入要搜索内容"
:autofocus="isSearchFocus"
@blur="changeSearchFocus(false)"
>
<template #prefix-icon>
<t-icon name="search" size="16" />
</template>
</t-input>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, PropType } from 'vue';
export default defineComponent({
props: {
layout: {
type: String as PropType<string>,
},
},
setup() {
const isSearchFocus = ref(false);
const searchData = ref('');
const changeSearchFocus = (value: boolean) => {
if (!value) {
searchData.value = '';
}
isSearchFocus.value = value;
};
return {
isSearchFocus,
searchData,
changeSearchFocus,
};
},
});
</script>
<style lang="less">
@import '@/style/variables.less';
.header-menu-search {
display: flex;
margin-left: 16px;
.hover-active {
.t-input__inner {
background: @bg-color-secondarycontainer;
}
}
.t-icon {
font-size: 20px !important;
color: @text-color-primary !important;
}
.t-input__inner {
border: none;
outline: none;
box-shadow: none;
transform: background @anim-duration-base linear;
&:hover {
background: @bg-color-secondarycontainer;
}
}
}
.header-search {
width: 200px;
transition: width @anim-duration-base @anim-time-fn-easing;
.t-input__inner {
border: 0;
padding-left: 40px;
&:focus {
box-shadow: none;
}
}
&.width-zero {
width: 0;
opacity: 0;
}
}
.t-button {
transition: opacity @anim-duration-base @anim-time-fn-easing;
}
.search-icon-hide {
opacity: 0;
}
</style>