blog/src/views/home/navMenu.vue
youzi b34147a30b
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m41s
12-28
2024-12-28 14:07:33 +08:00

338 lines
8.2 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.

<template>
<div class="box flex flex-wrap px-8 sm:px-0">
<n-card :style="{ height: boxH + 'px' }">
<n-scrollbar :class="`h-[${boxH}px]`" :style="{ height: boxH - 40 + 'px' }">
<div class="grid gridSpace gap-4 items-center justify-items-center">
<div class="w-[200px] my-4 sm:w-[150px] sm:my-1" v-for="(i, ii) in navList">
<n-button @click="link(i.menuLink)" @contextmenu="handdlemenutext($event, i)"
class="btn w-full hover:text-[white]" type="primary" ghost>
<template #icon>
<img class="rounded-full mr-2" v-if="!i.exp" width="20" height="20"
:src="'https://favicon.im/' + i.menuLink.split('/')[2]" alt="" @error="i.exp = true">
<div :style="{ backgroundColor: '#50C4D3' }"
class="w-[20px] h-[20px] text-[14px] text-[white] rounded-full" v-else>{{ i.menuName[0] }}</div>
</template>
<span class="truncate">{{ i.menuName }}</span>
</n-button>
</div>
<div class="w-[200px] my-4 sm:w-[150px] sm:my-1">
<n-button @click="addMenu" class="btn w-full text-2xl hover:text-[white]" type="primary" ghost>
+
</n-button>
</div>
</div>
</n-scrollbar>
</n-card>
<n-modal v-model:show="showModal">
<n-card class="w-1/4">
<div class="title text-3xl text-center my-8 text-pp-700">
添加菜单
</div>
<n-form :model="formData" ref="formRef" label-width="80" label-placement="left">
<n-form-item-row class="mt-4" path="menuName">
<n-input v-model:value="formData.menuName" placeholder="导航名称" />
</n-form-item-row>
<n-form-item-row class="mt-4" path="menuLink">
<n-input v-model:value="formData.menuLink" placeholder="导航链接" />
</n-form-item-row>
<n-form-item-row v-if="formData.menuLink" class="mt-4">
<img class="mx-auto" width="20" height="20" :src="'https://favicon.im/' + formData.menuLink.split('/')[2]"
alt="" />
</n-form-item-row>
<n-form-item-row class="mt-4">
<n-button class="w-2/5 ml-[5%]" @click="onCancel">取消</n-button>
<n-button class="w-2/5 ml-[10%]" type="primary" @click="onSubmit">立即创建</n-button>
</n-form-item-row>
</n-form>
</n-card>
</n-modal>
<div class="munur bg-pp-900 py-2 px-1" v-if="menuShow"
:style="{ position: 'fixed', top: y + 'px', left: x + 'px' }">
<template v-for="i in menuList">
<n-button v-if="i.show" class="text-[white]" type="primary" :tertiary="!i.active"
@click="handdleclickmenu(i.id)" @mouseenter="handdleentermenu(i.id)" @mouseleave="handdleleavemenu(i.id)">{{
i.name
}}</n-button>
</template>
</div>
</div>
</template>
<script setup>
import { onMounted, reactive } from 'vue';
import cookie from "vue-cookies";
import { useRouter } from 'vue-router';
//mark import
//mark data
const navList = ref([])
const size = $store.size.usesizeStore()
const boxH = ref(0)
const token = cookie.get("token");
const userinfo = cookie.get("userinfo");
const router = useRouter()
const fullPath = router.currentRoute.value.fullPath
const { push } = router;
const showModal = ref(false)
const formData = reactive({
menuName: "",
menuLink: "",
menuIcon: "#"
})
const menuList = ref([
{
id: 1,
name: "删除导航",
active: false,
show: true,
},
])
const menuShow = ref(false)
const x = ref(0)
const y = ref(0)
const menuSelect = ref(0)
let imgTimer = null
//mark method
async function getNav() {
let res = {}
if (!!token && !!userinfo) {
res = await $http.nav.listNav({ username: userinfo.username })
}
else {
res = await $http.nav.listNav()
}
navList.value = res.data.map((item) => {
item.exp = false
item.active = false
item.show = true
return item
})
}
function addMenu() {
if (!!token && !!userinfo) {
showModal.value = true
}
else {
$dialog.success({
title: "该功能能需要登录账号",
content: "未登录账号,是否去登录?",
positiveText: "登录",
negativeText: "返回",
onPositiveClick: () => {
push({
path: "/login",
query: {
redirect: fullPath,
},
});
},
});
}
}
async function onSubmit() {
if (!formData.menuName) {
$msg.error("请输入导航名称");
return
}
if (!formData.menuLink) {
$msg.error('请输入导航链接');
return
}
const res = await $http.nav.addMenu(formData)
if (res.code == 1) {
$msg.success(res.msg);
onCancel()
getNav()
}
else {
$msg.error(res.msg);
}
}
function onCancel() {
formData.menuName = ""
formData.menuLink = ""
showModal.value = false
}
function link(url) {
window.open(url, "_blank")
}
function handdleclickmenu(id) {
switch (id) {
case 1:
deleteMenu()
break;
}
menuShow.value = false
resetMenuList()
}
function deleteMenu() {
$dialog.warning({
title: '删除',
content: '你确定删除此导航?',
positiveText: '确定',
negativeText: '取消',
maskClosable: true,
onPositiveClick: () => {
$http.nav.deleteMenu(menuSelect.value).then(res => {
if (res.code == 1) {
$msg.success(res.msg);
getNav()
}
else {
$msg.error(res.msg);
}
})
}
})
}
function handdleentermenu(id) {
menuList.value[id - 1].active = true;
imgTimer && clearTimeout(imgTimer)
}
function handdleleavemenu(id) {
menuList.value[id - 1].active = false;
imgTimer = setTimeout(() => {
menuShow.value = false
resetMenuList()
}, 2000);
}
function handdlemenutext(e, i) {
// console.log(e, i);
menuShow.value = false
const { clientX, clientY } = e;
x.value = clientX;
y.value = clientY;
menuShow.value = true;
menuSelect.value = i.id
console.log(i.id);
imgTimer && clearTimeout(imgTimer)
imgTimer = setTimeout(() => {
menuShow.value = false
resetMenuList()
}, 2000);
}
function resetMenuList() {
menuList.value = ([
{
id: 1,
name: "删除导航",
active: false,
show: true,
},
])
}
//mark 周期、内置函数等
onMounted(() => {
getNav()
// 获取屏幕高度
const screenHeight = document.documentElement.clientHeight
boxH.value = screenHeight - size.searchH - size.menuH - 24 - 66
console.log(66666, navList.value);
})
</script>
<style scoped lang="less">
.box {
align-items: start;
}
.gridSpace {
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
:deep(.n-collapse-item__header-main) {
font-size: 20px;
align-items: center;
// justify-content: center;
}
:deep(.n-collapse-item-arrow) {
align-items: center;
}
// 旋转动画
@keyframes rotate {
from {
transform: rotate(0deg);
/* 起始角度 */
}
to {
transform: rotate(360deg);
/* 结束角度 */
}
}
.zhuan {
// transform: rotate(0deg) !important;
animation: rotate 2s infinite;
/* 播放名为bounce的动画循环无限次每次动画时长1秒 */
}
.btn {
transition: transform .5s ease-in-out;
/* 指定过渡属性持续时长过渡曲线何时开始 */
&:hover {
transform: translateX(10px);
background: linear-gradient(to right, #dca4aa, #a81b2b, #dca4aa);
}
;
}
.extra_collapse {
transform: rotate(0deg);
}
.extra_collapsed {
transform: rotate(90deg);
}
:deep(.n-card) {
padding: 30px;
background: #f1f2f370;
}
@media screen and (max-width: 767px) {
:deep(.n-card) {
padding: 10px;
background: #f1f2f320;
border: none;
}
:deep(.n-card__content, ) {
padding: 0;
}
.gridSpace {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
}
.munur {
border-radius: 4px;
overflow: hidden;
z-index: 9999;
display: flex;
flex-direction: column;
background-color: #fff;
}
</style>