All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m3s
407 lines
10 KiB
Vue
407 lines
10 KiB
Vue
<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 ref="dargBox" class="grid gridSpace gap-4 items-center justify-items-center" @dragstart="handdleDragstart"
|
||
@dragover="handdleDragover" @dragenter="haddleDragenter" @drop="handdleDrop">
|
||
<div :data-idx="i.menuSort" :draggable="i.menuSort > 0" class="drag-box w-[200px] my-4 sm:w-[150px] sm:my-1"
|
||
v-for="(i, ii) in navList">
|
||
<n-button :data-idx="i.menuSort" @click="link(i.menuLink)" @contextmenu="handdlemenutext($event, i)"
|
||
class="btn w-full hover:text-[white]" type="primary" ghost>
|
||
<template #icon>
|
||
<img :data-idx="i.menuSort" 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 :data-idx="i.menuSort" :style="{ backgroundColor: '#50C4D3' }"
|
||
class="w-[20px] h-[20px] text-[14px] text-[white] rounded-full" v-else>{{ i.menuName[0] }}</div>
|
||
</template>
|
||
<span :data-idx="i.menuSort" 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()
|
||
}
|
||
// console.log(res.data);
|
||
res.data.sort((a, b) => a.menuSort - b.menuSort)
|
||
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) {
|
||
if (i.belong == 'common') return
|
||
|
||
// 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,
|
||
},
|
||
])
|
||
}
|
||
const dragIdx = ref(-1)
|
||
function handdleDragstart(e) {
|
||
console.log(e.target.dataset.idx || 0);
|
||
dragIdx.value = parseInt(e.target.dataset.idx)
|
||
}
|
||
function handdleDragover(e) {
|
||
e.preventDefault();
|
||
}
|
||
function handdleDrop(e) {
|
||
e.preventDefault();
|
||
console.log(navList.value);
|
||
}
|
||
|
||
const dropIdx = ref(-1)
|
||
const dragBox = ref(null)
|
||
// 拖拽排序
|
||
function haddleDragenter(e) {
|
||
// 拖拽
|
||
e.preventDefault();
|
||
// console.log(e.target.dataset.idx == dropIdx.value);
|
||
|
||
if (e.target == dragBox.value || e.target.dataset.idx == dropIdx.value) {
|
||
return
|
||
}
|
||
// console.log(e.target.dataset);
|
||
if (e.target.dataset.idx) {
|
||
dropIdx.value = parseInt(e.target.dataset.idx)
|
||
}
|
||
if (dropIdx.value == 0) dropIdx.value = 1
|
||
const s = dragIdx.value, d = dropIdx.value
|
||
let si, di
|
||
navList.value.forEach((i, ii) => {
|
||
if (i.menuSort == s) {
|
||
si = ii
|
||
}
|
||
if (i.menuSort == d) {
|
||
di = ii
|
||
}
|
||
})
|
||
if (s > d) {
|
||
let temp = navList.value[si]
|
||
for (let i = si; i >= di; i--) {
|
||
navList.value[i] = navList.value[i - 1]
|
||
navList.value[i].menuSort++
|
||
}
|
||
navList.value[di] = temp
|
||
navList.value[di].menuSort = d
|
||
}
|
||
if (s < d) {
|
||
let temp = navList.value[si]
|
||
for (let i = si; i < di; i++) {
|
||
navList.value[i] = navList.value[i + 1]
|
||
navList.value[i].menuSort--
|
||
}
|
||
navList.value[di] = temp
|
||
navList.value[di].menuSort = d
|
||
}
|
||
|
||
}
|
||
|
||
//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;
|
||
}
|
||
|
||
.drag-box {
|
||
transition: transform 1s ease-in-out;
|
||
}
|
||
</style> |