init
This commit is contained in:
24
pc/layouts/components/header/admin.vue
Normal file
24
pc/layouts/components/header/admin.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<NuxtLink :to="appStore.getAdminUrl" target="_blank">
|
||||
<ElMenuItem :index="menuItem.path">
|
||||
<template #title>
|
||||
<span>
|
||||
{{ menuItem.name }}
|
||||
</span>
|
||||
</template>
|
||||
</ElMenuItem>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ElMenuItem } from 'element-plus'
|
||||
import { useAppStore } from '~~/stores/app'
|
||||
defineProps({
|
||||
menuItem: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
44
pc/layouts/components/header/index.vue
Normal file
44
pc/layouts/components/header/index.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<header class="layout-header text-white bg-primary">
|
||||
<div class="header-contain">
|
||||
<Logo class="flex-none mr-4" />
|
||||
<Navbar class="w-[600px]" />
|
||||
<div class="flex-1"></div>
|
||||
<Search class="mr-[40px] flex-none" />
|
||||
<User class="flex-none" />
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import User from './user.vue'
|
||||
import Search from './search.vue'
|
||||
import Logo from './logo.vue'
|
||||
import Navbar from './navbar.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-header {
|
||||
height: var(--header-height);
|
||||
border-bottom: 1px solid var(--el-border-color-extra-light);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1999;
|
||||
.header-contain {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
.navbar {
|
||||
--el-menu-item-font-size: var(--el-font-size-large);
|
||||
--el-menu-bg-color: var(--el-color-primary);
|
||||
--el-menu-active-color: var(--color-white);
|
||||
--el-menu-text-color: var(--color-white);
|
||||
--el-menu-item-hover-fill: var(--el-color-primary);
|
||||
--el-menu-hover-text-color: var(--color-white);
|
||||
--el-menu-hover-bg-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
47
pc/layouts/components/header/information.vue
Normal file
47
pc/layouts/components/header/information.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<el-dropdown :max-height="200" :disabled="!hasData">
|
||||
<span class="flex items-center text-white">
|
||||
<MenuItem :menu-item="menuItem" :route-path="menuItem.path" />
|
||||
<span class="ml-[-10px]" v-if="hasData">
|
||||
<Icon name="el-icon-ArrowDown" />
|
||||
</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<NuxtLink
|
||||
:to="{
|
||||
path: '/information/search',
|
||||
query: {
|
||||
cid: item.id,
|
||||
name: item.name
|
||||
}
|
||||
}"
|
||||
v-for="item in data"
|
||||
:key="item.id"
|
||||
>
|
||||
<el-dropdown-item> {{ item.name }} </el-dropdown-item>
|
||||
</NuxtLink>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ElDropdown, ElDropdownItem, ElDropdownMenu } from 'element-plus'
|
||||
import { getArticleCate } from '~~/api/news'
|
||||
import MenuItem from '../menu/menu-item.vue'
|
||||
defineProps({
|
||||
menuItem: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const { data } = await useAsyncData(() => getArticleCate())
|
||||
const hasData = computed(() => {
|
||||
return data.value && data.value.length
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
11
pc/layouts/components/header/logo.vue
Normal file
11
pc/layouts/components/header/logo.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<NuxtLink v-if="appStore.getWebsiteConfig.pcLogo" class="flex" to="/">
|
||||
<img :src="appStore.getWebsiteConfig.pcLogo" class="h-[26px]" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '~~/stores/app'
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
58
pc/layouts/components/header/mobile.vue
Normal file
58
pc/layouts/components/header/mobile.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<ElMenuItem :index="menuItem.path" @click="showMobilePopup = true">
|
||||
<template #title>
|
||||
<span>
|
||||
{{ menuItem.name }}
|
||||
</span>
|
||||
</template>
|
||||
</ElMenuItem>
|
||||
<ClientOnly>
|
||||
<ElDialog
|
||||
v-model="showMobilePopup"
|
||||
@close="showMobilePopup = false"
|
||||
:width="700"
|
||||
>
|
||||
<div class="text-center text-tx-primary">
|
||||
<div class="text-4xl font-medium">移动端演示</div>
|
||||
<div class="flex my-[40px] justify-around">
|
||||
<div v-if="oa">
|
||||
<img :src="oa" class="w-[180px] h-[180px]" alt="" />
|
||||
<div class="mt-2.5">微信公众号演示</div>
|
||||
</div>
|
||||
<div v-if="mnp">
|
||||
<img
|
||||
:src="mnp"
|
||||
class="w-[180px] h-[180px]"
|
||||
alt=""
|
||||
/>
|
||||
<div class="mt-2.5">微信小程序演示</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!mnp && !oa"
|
||||
class="w-[180px] h-[180px] flex items-center justify-center"
|
||||
>
|
||||
暂无演示
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElDialog>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ElMenuItem, ElDialog } from 'element-plus'
|
||||
import { useAppStore } from '~~/stores/app'
|
||||
defineProps({
|
||||
menuItem: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const appStore = useAppStore()
|
||||
const mnp = computed(() => appStore.getQrcodeConfig.mnp)
|
||||
const oa = computed(() => appStore.getQrcodeConfig.oa)
|
||||
const showMobilePopup = ref(false)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
55
pc/layouts/components/header/navbar.vue
Normal file
55
pc/layouts/components/header/navbar.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<nav>
|
||||
<Menu
|
||||
class="navbar"
|
||||
:menu="menu"
|
||||
:default-active="activeMenu"
|
||||
mode="horizontal"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<MenuItem
|
||||
v-if="!item.component"
|
||||
:menu-item="item"
|
||||
:route-path="item.path"
|
||||
/>
|
||||
<div v-else>
|
||||
<template v-if="item.component == 'information'">
|
||||
<Information :menu-item="item" />
|
||||
</template>
|
||||
<template v-if="item.component == 'mobile'">
|
||||
<Mobile :menu-item="item" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</Menu>
|
||||
</nav>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import Menu from '../menu/index.vue'
|
||||
import MenuItem from '../menu/menu-item.vue'
|
||||
import Admin from './admin.vue'
|
||||
import Information from './information.vue'
|
||||
import Mobile from './mobile.vue'
|
||||
const route = useRoute()
|
||||
const activeMenu = computed<string>(() => route.path)
|
||||
const { menu } = useMenu()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
--el-menu-item-font-size: var(--el-font-size-large);
|
||||
--el-menu-bg-color: var(--el-color-primary);
|
||||
--el-menu-active-color: var(--color-white);
|
||||
--el-menu-text-color: var(--color-white);
|
||||
--el-menu-item-hover-fill: var(--el-color-primary);
|
||||
--el-menu-hover-text-color: var(--color-white);
|
||||
--el-menu-hover-bg-color: var(--el-color-primary);
|
||||
:deep() {
|
||||
& > .el-sub-menu {
|
||||
.el-sub-menu__title:hover {
|
||||
background-color: var(--el-menu-bg-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
50
pc/layouts/components/header/search.vue
Normal file
50
pc/layouts/components/header/search.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="w-[250px] search">
|
||||
<ElInput
|
||||
v-model.trim="searchKeyword"
|
||||
placeholder="请输入关键词"
|
||||
:suffix-icon="Search"
|
||||
@keyup.enter="handleToSearch"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ElInput } from 'element-plus'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import feedback from '~~/utils/feedback'
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const searchKeyword = ref()
|
||||
const handleToSearch = () => {
|
||||
if (!searchKeyword.value) return feedback.msgError('请输入关键词')
|
||||
router.push({
|
||||
path: '/information/search',
|
||||
query: {
|
||||
keywords: searchKeyword.value
|
||||
}
|
||||
})
|
||||
}
|
||||
watch(
|
||||
route,
|
||||
(routeNew) => {
|
||||
if (routeNew.path == '/information/search') {
|
||||
searchKeyword.value = routeNew.query.keywords
|
||||
} else {
|
||||
searchKeyword.value = ''
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search {
|
||||
:deep(.el-input) {
|
||||
.el-input__wrapper {
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
65
pc/layouts/components/header/user.vue
Normal file
65
pc/layouts/components/header/user.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div>
|
||||
<ElDropdown v-if="userStore.isLogin" @command="handleCommand">
|
||||
<div class="flex items-center">
|
||||
<ElAvatar :size="25" :src="userStore.userInfo.avatar" />
|
||||
<div class="ml-1 text-white text-lg flex">
|
||||
<span class="mr-2">个人中心</span>
|
||||
<ElIcon><ArrowDown /></ElIcon>
|
||||
</div>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<NuxtLink to="/user/info">
|
||||
<ElDropdownItem command="user">个人信息</ElDropdownItem>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/user/collection">
|
||||
<ElDropdownItem command="collect">
|
||||
我的收藏
|
||||
</ElDropdownItem>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/account/security">
|
||||
<ElDropdownItem command="account">
|
||||
账号安全
|
||||
</ElDropdownItem>
|
||||
</NuxtLink>
|
||||
<ElDropdownItem command="logout">退出登录</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
</ElDropdown>
|
||||
|
||||
<div v-else class="cursor-pointer text-lg" @click="handleToLogin">
|
||||
登录/注册
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ElAvatar,
|
||||
ElDropdown,
|
||||
ElDropdownMenu,
|
||||
ElDropdownItem,
|
||||
ElIcon
|
||||
} from 'element-plus'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { PopupTypeEnum, useAccount } from '../account/useAccount'
|
||||
import feedback from '~~/utils/feedback'
|
||||
const { setPopupType, toggleShowPopup } = useAccount()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const handleToLogin = () => {
|
||||
setPopupType(PopupTypeEnum.LOGIN)
|
||||
toggleShowPopup(true)
|
||||
}
|
||||
|
||||
const handleCommand = async (command: string) => {
|
||||
switch (command) {
|
||||
case 'logout':
|
||||
await feedback.confirm('确定退出登录吗?')
|
||||
userStore.logout()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
Reference in New Issue
Block a user