369 lines
11 KiB
Vue
369 lines
11 KiB
Vue
<template>
|
|
<div ref="nav" class="main-nav hidden lg:flex justify-between bg-white">
|
|
<!-- 网站Logo -->
|
|
<div class="px-5 items-centerflex cursor-pointer w-1/5" slot="brand" @click="goHome">
|
|
<img :src="logo" alt="柚子的网站" class="h-9 align-middle" />
|
|
</div>
|
|
<!-- 主导航菜单 -->
|
|
<div class="w-2/5">
|
|
<n-menu :icon-size="12" v-model:value="activeKey" class="" mode="horizontal" :options="menuOptions" />
|
|
</div>
|
|
<!-- 用户区域 -->
|
|
<div class="!text-[#ec66ab] flex items-center cursor-pointer" @click="gotoHf">
|
|
<span class="flex items-center location-info truncate">
|
|
<n-icon class="mr-1">
|
|
<icon-loc class="w-[26px] h-[26px]"></icon-loc>
|
|
</n-icon>
|
|
{{ locationInfo }}
|
|
</span>
|
|
<span class="mx-3 text-gray-300">|</span>
|
|
<span class="weather-info mr-2 truncate">{{ wea }}</span>
|
|
<i :class="'qiIcon qi-' + weaIcon + '-fill'"></i>
|
|
<span class="weather-info ml-4">{{ temp }}°C</span>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-end mr-8 w-1/20">
|
|
<n-dropdown class="cursor-pointer w-[100px]" v-if="userinfo" :options="oprOp" @select="handleSelect"
|
|
trigger="hover">
|
|
<div class="flex items-center">
|
|
<n-avatar round :src="userinfo.ava_url" class="cursor-pointer" alt="用户的头" />
|
|
<div class="cursor-pointer ml-2 text-gray text-sm">{{ userinfo.nickname }}</div>
|
|
</div>
|
|
</n-dropdown>
|
|
<div v-else class="flex items-center" @click="toLogin">
|
|
<n-avatar round class="cursor-pointer"></n-avatar>
|
|
<div class="cursor-pointer ml-2 text-gray text-sm">登录</div>
|
|
</div>
|
|
</div>
|
|
<!-- 登录弹窗 -->
|
|
<masked :visible="visible" :setVisible="setVisible">
|
|
<loginModal :setVisible="setVisible" />
|
|
</masked>
|
|
|
|
</div>
|
|
<div class="flex justify-between bg-white lg:hidden">
|
|
<div class="pl-2 items-centerflex" slot="brand" @click="">
|
|
<img :src="logo" alt="柚子的网站" class="h-9 align-middle" />
|
|
</div>
|
|
<div class="flex items-center mr-2">
|
|
<div v-if="userinfo" class="flex items-center">
|
|
<n-avatar :src="userinfo.ava_url" round class="cursor-pointer" alt="用户的头" />
|
|
<div class="cursor-pointer ml-2 text-gray text-sm">{{ userinfo.nickname }}</div>
|
|
</div>
|
|
<div v-else class="flex items-center">
|
|
<n-avatar round class="cursor-pointer" @click="toLogin"></n-avatar>
|
|
<div class="cursor-pointer ml-2 text-gray text-sm" @click="toLogin">登录</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 修改头像弹窗 -->
|
|
<masked v-if="usrLog.isLogin" :visible="editModal" :setVisible="handdleItemCancel">
|
|
<div class="w-[500px] bg-white p-8 rounded-md">
|
|
<div class="text-center text-lg mb-8">修改头像</div>
|
|
<n-upload class="flex justify-center items-center" action="https://www.hxyouzi.com/api/user/avaupload"
|
|
:headers="{ Authorization: `Bearer ${token}` }" @finish="handleUpload" @before-upload="beforeUpload"
|
|
:show-file-list="false" accept="image/*">
|
|
<div class="flex flex-col justify-center items-center">
|
|
<n-avatar :size="80" :src="userinfo.ava_url" round class="cursor-pointer" alt="用户的头" />
|
|
<em class="cursor-pointer mt-4 text-gray text-sm">点击头像上传不超过3m的图片</em>
|
|
</div>
|
|
</n-upload>
|
|
|
|
<div class="mt-8 flex justify-between">
|
|
<n-button class="w-[98%]" secondary @click="handdleItemCancel">取消</n-button>
|
|
</div>
|
|
</div>
|
|
</masked>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// 从@/icon/menu引入所有的svg文件
|
|
import logo from '@/assets/images/logo.png';
|
|
import artiSvg from '@/icon/menu/arti.svg';
|
|
// import downSvg from '@/icon/menu/download.svg';
|
|
import homeSvg from '@/icon/menu/home.svg';
|
|
import linkSvg from '@/icon/menu/link.svg';
|
|
import picSvg from '@/icon/menu/pic.svg';
|
|
// import settingSvg from '@/icon/menu/setting.svg';
|
|
import loginModal from '@/components/Login.vue'
|
|
import masked from '@/components/mask.vue'
|
|
import type { UploadFileInfo } from 'naive-ui'
|
|
|
|
import { onMounted, ref, watch } from 'vue';
|
|
import { useRoute, useRouter, RouterLink } from 'vue-router';
|
|
|
|
const activeKey = ref('home')
|
|
const route = useRoute();
|
|
const visible = ref(false);
|
|
const router = useRouter();
|
|
const locationInfo = ref("获取位置中...");
|
|
const latitude = ref<number | null>(null);
|
|
const longitude = ref<number | null>(null);
|
|
const fxlink = ref<string>("#")
|
|
const wea = ref("")
|
|
const weaIcon = ref<string>("")
|
|
const temp = ref<number>(0);
|
|
const userinfo: any = ref(null)
|
|
const nav: any = useTemplateRef('nav')
|
|
const navx = $store.nav.useNavStore()
|
|
const usrLog = $store.log.useLogStore()
|
|
const menuOptions = ref([
|
|
{
|
|
label: () => h(RouterLink, { to: '/home', class: 'flex items-center justify-center' }, { default: () => '首页' }),
|
|
key: "home",
|
|
icon: () => h(homeSvg)
|
|
},
|
|
{
|
|
label: () => h(RouterLink, { to: '/gallery', class: 'flex items-center justify-center' }, { default: () => '画廊' }),
|
|
key: "gallery",
|
|
icon: () => h(picSvg)
|
|
},
|
|
{
|
|
label: () => h(RouterLink, { to: '/blog', class: 'flex items-center justify-center' }, { default: () => '文章' }),
|
|
key: "blog",
|
|
icon: () => h(artiSvg)
|
|
},
|
|
// {
|
|
// label: () => h(RouterLink, { to: '/widget', class: 'flex items-center justify-center' }, { default: () => '工具' }),
|
|
// key: "widget",
|
|
// icon: () => h(downSvg)
|
|
// },
|
|
// {
|
|
// label: () => h(RouterLink, { to: '/apps', class: 'flex items-center justify-center' }, { default: () => '软件' }),
|
|
// key: "apps",
|
|
// icon: () => h(settingSvg)
|
|
// },
|
|
{
|
|
label: () => h(RouterLink, { to: '/plink', class: 'flex items-center justify-center' }, { default: () => '友链' }),
|
|
key: "plink",
|
|
icon: () => h(linkSvg)
|
|
},
|
|
]);
|
|
const oprOp = ref([
|
|
{
|
|
key: 'logout',
|
|
label: '退出登录',
|
|
},
|
|
{
|
|
key: 'console',
|
|
label: '控制台',
|
|
},
|
|
{
|
|
key: 'avatar',
|
|
label: '更换头像',
|
|
}
|
|
])
|
|
|
|
const editModal = ref<boolean>(false)
|
|
const token = ref<string>("")
|
|
|
|
|
|
function beforeUpload(data: { file: UploadFileInfo }) {
|
|
console.log('>>> --> beforeUpload --> data:', data.file.file?.size)
|
|
const size = data.file.file?.size || 4 * 1024 * 1024
|
|
if (size > 3 * 1024 * 1024) {
|
|
$msg.error('上传的图片大小不能超过3M')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
function handleUpload(f: any) {
|
|
console.log('上传完成', JSON.parse(f.event.target.response));
|
|
const res = JSON.parse(f.event.target.response)
|
|
if (res.code === 200) {
|
|
// $msg.success(res.msg);
|
|
autoLogin()
|
|
} else {
|
|
$msg.error(res.msg);
|
|
}
|
|
editModal.value = false
|
|
}
|
|
|
|
async function autoLogin() {
|
|
const res = await $http.user.autologin()
|
|
if (res?.code !== 200) {
|
|
$msg.error('登录失败')
|
|
usrLog.setIsLogin(false)
|
|
$cookies.remove('token')
|
|
$cookies.remove('userinfo')
|
|
return
|
|
}
|
|
$cookies.set('token', res.data.token, '1d')
|
|
$cookies.set('userinfo', res.data.userinfo, '1d')
|
|
$msg.success(res.msg)
|
|
usrLog.setIsLogin(true)
|
|
userinfo.value = res.data.userinfo
|
|
}
|
|
|
|
function handdleItemCancel() {
|
|
editModal.value = false
|
|
}
|
|
|
|
|
|
function setVisible(v: any) {
|
|
visible.value = v
|
|
}
|
|
|
|
function handleSelect(key: string) {
|
|
console.log('>>> --> handleSelect --> key:', key)
|
|
if (key == 'logout') {
|
|
logout()
|
|
return
|
|
}
|
|
if (key == 'console') {
|
|
gotoConsole()
|
|
return
|
|
}
|
|
if (key == 'avatar') {
|
|
editModal.value = true
|
|
return
|
|
}
|
|
}
|
|
|
|
// 获取地理位置
|
|
const getLocation = () => {
|
|
if (!navigator.geolocation) {
|
|
locationInfo.value = "您的浏览器不支持地理位置定位";
|
|
return;
|
|
}
|
|
|
|
navigator.geolocation.getCurrentPosition(
|
|
async (position) => {
|
|
latitude.value = position.coords.latitude;
|
|
longitude.value = position.coords.longitude;
|
|
const jw = `${longitude.value.toFixed(2)},${latitude.value.toFixed(2)}`;
|
|
|
|
// 这里可以添加调用后端API获取具体位置名称的逻辑
|
|
handdleJw(jw);
|
|
|
|
|
|
},
|
|
async (error) => {
|
|
switch (error.code) {
|
|
case error.PERMISSION_DENIED:
|
|
locationInfo.value = "正在定位...";
|
|
break;
|
|
case error.POSITION_UNAVAILABLE:
|
|
locationInfo.value = "正在定位...";
|
|
break;
|
|
case error.TIMEOUT:
|
|
locationInfo.value = "正在定位...";
|
|
break;
|
|
}
|
|
const res = await $http.mix.getIp();
|
|
latitude.value = Number(res.data.data.lat);
|
|
longitude.value = Number(res.data.data.lng);
|
|
const jw = `${longitude.value?.toFixed(4)},${latitude.value?.toFixed(4)}`;
|
|
|
|
// 这里可以添加调用后端API获取具体位置名称的逻辑
|
|
handdleJw(jw);
|
|
},
|
|
{
|
|
enableHighAccuracy: false,
|
|
timeout: 5000,
|
|
maximumAge: 0
|
|
}
|
|
);
|
|
};
|
|
|
|
async function handdleJw(jw: string) {
|
|
const zxs = ['北京', '重庆', '天津', '上海']
|
|
// 根据经纬度获取物理位置
|
|
const loc = await $http.mix.getLocation({ location: jw });
|
|
console.log('>>> --> handdleJw --> loc:', loc)
|
|
fxlink.value = loc.data.fxLink
|
|
if (loc.code == 200) {
|
|
const data = loc.data;
|
|
if (zxs.includes(data.adm2)) {
|
|
locationInfo.value = `${data.adm1}${data.name}`;
|
|
} else {
|
|
locationInfo.value = `${data.adm1}${data.adm2}${data.name}`;
|
|
}
|
|
} else {
|
|
$msg.console.error(loc.msg);
|
|
return
|
|
}
|
|
const res = await $http.mix.getWeather({ location: jw });
|
|
console.log('>>> --> handdleJw --> res:', res)
|
|
if (res.code == 200) {
|
|
wea.value = res.data.text;
|
|
weaIcon.value = res.data.icon;
|
|
temp.value = res.data.temp;
|
|
} else {
|
|
$msg.console.error(loc.msg);
|
|
}
|
|
}
|
|
|
|
watch(() => route.name, (newVal) => {
|
|
activeKey.value = newVal as string
|
|
})
|
|
|
|
function goHome() {
|
|
if (route.name == 'home') return
|
|
router.push('/home');
|
|
}
|
|
|
|
function toLogin() {
|
|
console.log('>>> --> toLogin --> toLogin:', 'toLogin')
|
|
|
|
if ($cookies.get('token')) return
|
|
visible.value = true
|
|
}
|
|
|
|
function logout() {
|
|
console.log('>>> --> logout --> logout:', 'logout')
|
|
$cookies.remove('token');
|
|
$cookies.remove('userinfo');
|
|
usrLog.setIsLogin(false)
|
|
userinfo.value = null;
|
|
}
|
|
function gotoConsole() {
|
|
window.open("https://www.hxyouzi.com/console/home", "_BLACK")
|
|
}
|
|
|
|
function gotoHf() {
|
|
console.log('>>> --> gotoHf --> fxlink:', fxlink)
|
|
window.open(fxlink.value, "_BLACK")
|
|
|
|
}
|
|
onMounted(() => {
|
|
|
|
userinfo.value = $cookies.get('userinfo');
|
|
console.log('>>>>>>>>>>', userinfo.value);
|
|
setTimeout(() => {
|
|
console.log('>>>>>>>>>>', route)
|
|
activeKey.value = route.name as string;
|
|
}, 20);
|
|
console.log('>>> --> route.name:', route.name)
|
|
getLocation(); // 组件挂载时获取位置
|
|
|
|
const h: number = nav.value.clientHeight
|
|
if (h > 0) navx.setNavH(h)
|
|
token.value = $cookies.get('token')
|
|
|
|
|
|
});
|
|
onBeforeUpdate(() => {
|
|
userinfo.value = $cookies.get('userinfo');
|
|
activeKey.value = route.name as string;
|
|
const h: number = nav.value.clientHeight
|
|
// console.log('******>>> --> nav.value:', nav.value)
|
|
// console.log('()()()()()>>> --> h:', h)
|
|
if (h > 0) navx.setNavH(h)
|
|
})
|
|
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.main-nav {
|
|
box-shadow: 0 1px 15px 0 @primary;
|
|
margin-bottom: 1px;
|
|
}
|
|
|
|
:deep(.n-menu-item-content__icon) {
|
|
width: 16px !important;
|
|
height: 16px !important;
|
|
}
|
|
</style> |