mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-12-23 02:15:57 +08:00
fix: layouts name fix (#24)
This commit is contained in:
parent
baf43fb503
commit
7fe368c874
351
src/layouts/components/Header.vue
Normal file
351
src/layouts/components/Header.vue
Normal 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>
|
207
src/layouts/components/Notice.vue
Normal file
207
src/layouts/components/Notice.vue
Normal 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>
|
113
src/layouts/components/Search.vue
Normal file
113
src/layouts/components/Search.vue
Normal 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>
|
Loading…
Reference in New Issue
Block a user