导航
This commit is contained in:
parent
5309d8d763
commit
2c819c7b69
@ -7,3 +7,26 @@ export function listNav() {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function addMenu(data) {
|
||||
return request({
|
||||
url: '/nav/menu',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function listClass(){
|
||||
return request({
|
||||
url: '/nav/class',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function addClass(data) {
|
||||
return request({
|
||||
url: '/nav/class',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
@ -3,7 +3,7 @@ const icons = import.meta.glob('./*.svg', {
|
||||
})
|
||||
|
||||
const icon = {}
|
||||
console.log(icons);
|
||||
// console.log(icons);
|
||||
for (const i in icons) {
|
||||
const t = i.split("/")
|
||||
const name = t[1].split('.')[0]
|
||||
|
1
src/icon/sciss.svg
Normal file
1
src/icon/sciss.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="7" r="3"></circle><circle cx="6" cy="17" r="3"></circle><path d="M8.6 8.6L19 19"></path><path d="M8.6 15.4L19 5"></path></g></svg>
|
After Width: | Height: | Size: 353 B |
@ -8,6 +8,15 @@ import { useDialog, useMessage, useNotification } from "naive-ui";
|
||||
window.$msg = useMessage();
|
||||
window.$dialog = useDialog();
|
||||
window.$note = useNotification();
|
||||
window.$vr = (res, sf, ff) => {
|
||||
if (res.code == 1) {
|
||||
window.$msg.success(res.msg)
|
||||
sf()
|
||||
} else {
|
||||
window.$msg.error(res.msg)
|
||||
ff()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -89,6 +89,15 @@ const router = createRouter({
|
||||
auth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/console/menu",
|
||||
component: () => import("@/views/console/menu/index.vue"),
|
||||
meta: {
|
||||
title: "控制台",
|
||||
name: "导航管理",
|
||||
auth: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -25,10 +25,15 @@ const options = [
|
||||
key: "3",
|
||||
icon: () => h(NIcon, { class: "orange" }, { default: () => h(article) }),
|
||||
},
|
||||
{
|
||||
label: () => h(RouterLink, { to: "/console/menu", class: "menu-item" }, { default: () => "导航管理" }),
|
||||
key: "4",
|
||||
icon: () => h(NIcon, { class: "orange" }, { default: () => h(article) }),
|
||||
},
|
||||
];
|
||||
|
||||
const consoleMenuInfo = {
|
||||
options,
|
||||
menuList: ["/console/home", "/console/profile", "/console/gallery", "/console/article"],
|
||||
menuList: ["/console/home", "/console/profile", "/console/gallery", "/console/article","/console/menu",],
|
||||
};
|
||||
export default consoleMenuInfo;
|
||||
|
@ -134,3 +134,19 @@ export function getDictValue(dict, ckey, cvalue,rkey) {
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
// 分割数组
|
||||
export function chunkArrayInGroups(arr, size) {
|
||||
return Array.from(
|
||||
{ length: Math.ceil(arr.length / size) },
|
||||
(_, i) => arr.slice(i * size, i * size + size)
|
||||
);
|
||||
}
|
||||
// 重置对象
|
||||
export function resetObject(obj) {
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
obj[key] = null;
|
||||
}
|
||||
}
|
||||
}
|
347
src/views/console/menu/index.vue
Normal file
347
src/views/console/menu/index.vue
Normal file
@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-8 mt-8 relative">
|
||||
<h1 class="text-center text-lg">导航管理</h1>
|
||||
<div class="absolute top-0 right-[5%]">
|
||||
<!-- <n-button class="mr-4" type="primary" @click="addClass">增加分类</n-button> -->
|
||||
<n-button type="primary" @click="addMenu">增加导航</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table w-[95%] mx-auto">
|
||||
<n-data-table :columns="columns" striped :single-line="false" :paginate-single-page="false"
|
||||
:pagination="pagination" :data="menuList">
|
||||
<template #empty>
|
||||
<div>
|
||||
<span>尚无导航,</span><span class="text-pp-200 hover:underline" @click="add">去增加导航 ></span>
|
||||
</div>
|
||||
</template>
|
||||
</n-data-table>
|
||||
</div>
|
||||
|
||||
<n-drawer v-model:show="mshow" :width="702" :mask-closable="false">
|
||||
<n-drawer-content title="新增导航" closable>
|
||||
<n-form class="mb-20 px-4" ref="menuForm" :model="menuValue" :rules="menuRules" label-placement="left"
|
||||
label-width="100">
|
||||
<n-form-item-row label="导航名称" path="menuName">
|
||||
<n-input v-model:value="menuValue.menuName" placeholder="请输入导航名称" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row label="导航类别" path="">
|
||||
<n-select v-model:value="menuValue.menuClassId" placeholder="请选择导航类别" :options="classList"
|
||||
@update:value="changeClass" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row label="导航链接" path="menuLink">
|
||||
<n-input v-model:value="menuValue.menuLink" placeholder="请输入导航链接" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row label="导航图标" path="menuIcon">
|
||||
<n-input v-model:value="menuValue.menuIcon" placeholder="请输入导航图标" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row label="导航描述" path="menuDesc">
|
||||
<n-input type="textarea" v-model:value="menuValue.menuDesc" placeholder="请输入导航描述" />
|
||||
</n-form-item-row>
|
||||
<div class="px-[10%] ">
|
||||
<n-button type="primary" block secondary strong @click="submitAddMenu">
|
||||
增加导航
|
||||
</n-button>
|
||||
</div>
|
||||
|
||||
</n-form>
|
||||
|
||||
<n-divider class="mt-4" title-placement="left">
|
||||
<n-icon size="20" color="#8A2BE2">
|
||||
<icon-sciss></icon-sciss>
|
||||
</n-icon>
|
||||
<span class="ml-2 text-[20px]">增加类别</span>
|
||||
</n-divider>
|
||||
|
||||
|
||||
<n-form class="px-4" :model="classValue" ref="classForm" :rules="classRules" label-placement="left"
|
||||
label-width="100">
|
||||
<n-form-item label="分类名称" path="menuClass">
|
||||
<n-input v-model:value="classValue.menuClass" placeholder="请输入分类名称" />
|
||||
</n-form-item>
|
||||
<n-form-item label="分类id" path="">
|
||||
<n-input-number v-model:value="classValue.menuClassId" placeholder="请输入分类id" />
|
||||
</n-form-item>
|
||||
<div class="px-[10%]">
|
||||
<n-button type="primary" block secondary strong @click="submitAddClass">创建分类</n-button>
|
||||
</div>
|
||||
</n-form>
|
||||
|
||||
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
|
||||
<n-modal v-model:show="showModal" preset="dialog" title="删除" content="是否永久删除该文章?" positive-text="确认"
|
||||
negative-text="取消" @positive-click="submitCallback" @negative-click="cancelCallback" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { resetObject } from '@/util/index.js';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
const menuValue = reactive({
|
||||
menuName: null,
|
||||
menuClassId: null,
|
||||
menuLink: null,
|
||||
menuIcon: null,
|
||||
menuClass: null,
|
||||
menuDesc: null,
|
||||
})
|
||||
|
||||
const classValue = reactive({
|
||||
menuClass: null,
|
||||
menuClassId: null,
|
||||
})
|
||||
|
||||
const mshow = ref(false)
|
||||
const showModal = ref(false)
|
||||
const menuList = ref([])
|
||||
const classList = ref([])
|
||||
const pagination = ref({
|
||||
pageSize: 10,
|
||||
})
|
||||
|
||||
const menuForm = ref(null)
|
||||
const classForm = ref(null)
|
||||
|
||||
const dtitle = ref('');
|
||||
const opId = ref(-1);
|
||||
const curId = ref(-1);
|
||||
const delId = ref(-1);
|
||||
const show = ref('')
|
||||
const columns = createColumns({
|
||||
view: (row) => {
|
||||
dshow.value = true
|
||||
dtitle.value = '文章预览'
|
||||
opId.value = row.id
|
||||
show.value = 'view'
|
||||
},
|
||||
edit: (row) => {
|
||||
dshow.value = true
|
||||
dtitle.value = '编辑文章'
|
||||
curId.value = row.id
|
||||
show.value = 'edit'
|
||||
},
|
||||
remove: (row) => {
|
||||
showModal.value = true
|
||||
delId.value = row.id
|
||||
}
|
||||
})
|
||||
|
||||
const menuRules = {
|
||||
menuName: {
|
||||
required: true,
|
||||
message: '请输入导航名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
menuClassId: {
|
||||
required: true,
|
||||
message: '请选择导航类别',
|
||||
trigger: 'change',
|
||||
},
|
||||
menuLink: {
|
||||
required: true,
|
||||
message: '请输入导航链接',
|
||||
trigger: 'blur',
|
||||
},
|
||||
menuIcon: {
|
||||
required: true,
|
||||
message: '请输入导航图标',
|
||||
trigger: 'blur',
|
||||
},
|
||||
}
|
||||
|
||||
const classRules = {
|
||||
menuClass: {
|
||||
required: true,
|
||||
message: '请输入分类名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
menuClassId: {
|
||||
required: true,
|
||||
message: '请输入分类id',
|
||||
trigger: 'blur',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
function createColumns({ edit, remove, view }) {
|
||||
return [
|
||||
{
|
||||
title: '序号',
|
||||
key: 'key',
|
||||
align: 'center',
|
||||
width: "80",
|
||||
render: (_, index) => {
|
||||
return `${index + 1}`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "导航名称",
|
||||
align: 'center',
|
||||
width: "300",
|
||||
key: "menuName"
|
||||
},
|
||||
{
|
||||
title: "导航分类",
|
||||
align: 'center',
|
||||
width: "200",
|
||||
key: "menuClass"
|
||||
},
|
||||
{
|
||||
title: "导航简介",
|
||||
align: 'center',
|
||||
key: "menuDesc",
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
align: 'center',
|
||||
key: "actions",
|
||||
width: "200",
|
||||
render(row) {
|
||||
return h(
|
||||
'div',
|
||||
[
|
||||
h(NButton,
|
||||
{
|
||||
type: "primary",
|
||||
text: true,
|
||||
size: "small",
|
||||
onClick: () => view(row)
|
||||
},
|
||||
{ default: () => "预览" }
|
||||
),
|
||||
h(NButton,
|
||||
{
|
||||
type: "primary",
|
||||
text: true,
|
||||
size: "small",
|
||||
style: "margin-left: 10px",
|
||||
onClick: () => edit(row)
|
||||
},
|
||||
{ default: () => "编辑" }
|
||||
),
|
||||
h(NButton,
|
||||
{
|
||||
type: "primary",
|
||||
text: true,
|
||||
style: "margin-left: 10px",
|
||||
size: "small",
|
||||
onClick: () => remove(row)
|
||||
},
|
||||
{ default: () => "删除" }
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
};
|
||||
function addMenu() {
|
||||
mshow.value = true
|
||||
}
|
||||
onMounted(() => {
|
||||
getClass()
|
||||
getList()
|
||||
})
|
||||
async function getList() {
|
||||
// 获取文章列表
|
||||
const res = await $http.nav.listNav()
|
||||
|
||||
menuList.value = res.data
|
||||
}
|
||||
|
||||
async function getClass() {
|
||||
const res = await $http.nav.listClass()
|
||||
if (res.code == 1) {
|
||||
res.data.forEach(i => {
|
||||
i.value = i.menuClassId
|
||||
i.label = i.menuClass
|
||||
});
|
||||
classList.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function submitCallback() {
|
||||
const res = await $http.art.deleteArti(delId.value)
|
||||
if (res.code == 1) $msg.success(res.msg)
|
||||
else $msg.error(res.msg)
|
||||
showModal.value = false
|
||||
delId.value = -1
|
||||
getList()
|
||||
}
|
||||
|
||||
function cancelCallback() {
|
||||
showModal.value = false
|
||||
delId.value = -1
|
||||
}
|
||||
|
||||
async function submitAddMenu() {
|
||||
if (menuValue.menuClassId == null || !menuValue.menuClassId) {
|
||||
$msg.error('请选择导航类别')
|
||||
return
|
||||
}
|
||||
menuForm.value?.validate(async errors => {
|
||||
if (!errors) {
|
||||
const res = await $http.nav.addMenu(menuValue)
|
||||
if (res.code == 1) {
|
||||
$msg.success(res.msg);
|
||||
resetObject(menuValue)
|
||||
getList()
|
||||
} else {
|
||||
$msg.error(res.msg);
|
||||
}
|
||||
|
||||
} else {
|
||||
$msg.error(errors[0][0].message)
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitAddClass() {
|
||||
console.log(classValue);
|
||||
if (!classValue.menuClassId) {
|
||||
$msg.error('请输入分类id')
|
||||
return
|
||||
}
|
||||
classForm.value.validate(async errors => {
|
||||
if (!errors) {
|
||||
const res = await $http.nav.addClass(classValue)
|
||||
if (res.code == 1) {
|
||||
$msg.success(res.msg);
|
||||
resetObject(classValue)
|
||||
getClass()
|
||||
} else {
|
||||
$msg.error(res.msg);
|
||||
}
|
||||
} else {
|
||||
$msg.error(errors[0][0].message)
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function changeClass() {
|
||||
menuValue.menuClass = classList.value.find(item => item.menuClassId == menuValue.menuClassId).menuClass
|
||||
console.log(menuValue);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.n-divider__line) {
|
||||
background-color: @purple !important;
|
||||
height: 2px !important;
|
||||
}
|
||||
|
||||
:deep(.n-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
@ -1,37 +1,28 @@
|
||||
<template>
|
||||
<div class="box flex flex-wrap pl-[2%] mt-8">
|
||||
<div class="nav-card w-[300px] mx-[2%] mb-6 p-2 bg-[#fafbfc]" v-for="(item, index) in navList" :key="index">
|
||||
<div class="t" @click="expand(index)">
|
||||
<n-divider title-placement="center">
|
||||
<div class="flex items-center font-bold">
|
||||
{{ item.menuClass }}
|
||||
<n-icon size="24" class="tiao mx-2">
|
||||
<icon-more />
|
||||
</n-icon>
|
||||
<n-card title="" style="margin-bottom: 16px" v-for="(ii,iii) in navList">
|
||||
<n-tabs default-value="0" justify-content="space-evenly" type="line">
|
||||
<n-tab-pane class="flex flex-wrap" :name="index+''" :tab="item.menuClass" v-for="(item, index) in ii">
|
||||
<div class="card mr-4 mb-4 rounded-md py-4 flex justify-center items-center w-[320px] bg-[#f2e8fc]" @click="link(it.menuLink)" v-for="(it, idx) in item.children" :key="index">
|
||||
<div class="left mr-3">
|
||||
<img width="40" height="40" class="rounded-full bg-pp-400" :src="it.menuIcon" alt="">
|
||||
</div>
|
||||
<div class="right w-[224px]">
|
||||
<div class="text-[20px] text-pp-700 font-bold">{{ it.menuName }}</div>
|
||||
<div class="truncate">{{ it.menuDesc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-divider>
|
||||
</div>
|
||||
<div class="flex flex-wrap pt-3" v-show="item.exp">
|
||||
<div class="w-[45%] ml-[3%] mb-2" v-for="(it, idx) in item.children" :key="index">
|
||||
<!-- <n-popover trigger="hover"> -->
|
||||
<!-- <template #trigger> -->
|
||||
<n-button class="w-[100%] text-[12px] text-ellipsis hover:bg-gradient-to-r hover:from-pp-100 hover:to-pp-400 font-bold" ghost color="#B172EC" @click="link(it.menuLink)">
|
||||
<!-- <n-button class="w-[100%] text-[12px] text-ellipsis" ghost color="#ff69b4" @click="link(it.menuLink)"> -->
|
||||
{{ it.menuName }}
|
||||
</n-button>
|
||||
<!-- </template> -->
|
||||
<!-- <span class="text-[12px]">{{ it.menuName }}</span> -->
|
||||
<!-- </n-popover> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { chunkArrayInGroups } from '@/util/index.js';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
|
||||
//mark import
|
||||
|
||||
//mark data
|
||||
@ -54,23 +45,13 @@ function formatNav(list) {
|
||||
})
|
||||
})
|
||||
console.log(result);
|
||||
return result
|
||||
|
||||
|
||||
return chunkArrayInGroups(result, 4)
|
||||
|
||||
}
|
||||
|
||||
function expand(index) {
|
||||
console.log(index, active.value);
|
||||
if (active.value == index) {
|
||||
navList.value[index].exp = !navList.value[index].exp
|
||||
} else {
|
||||
navList.value.forEach(i => {
|
||||
i.exp = false
|
||||
})
|
||||
navList.value[index].exp = true
|
||||
}
|
||||
|
||||
active.value = index
|
||||
}
|
||||
function link(url) {
|
||||
window.open(url, "_blank")
|
||||
}
|
||||
@ -78,82 +59,17 @@ function link(url) {
|
||||
onMounted(async () => {
|
||||
const res = await $http.nav.listNav()
|
||||
navList.value = formatNav(res.data)
|
||||
console.log(navList.value);
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.nav-card {
|
||||
box-shadow: @ps;
|
||||
border-radius: 4px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.box {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
|
||||
0%,
|
||||
20%,
|
||||
50%,
|
||||
80%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.tiao {
|
||||
animation: bounce 1s infinite;
|
||||
/* 播放名为bounce的动画,循环无限次,每次动画时长1秒 */
|
||||
}
|
||||
|
||||
:deep(.n-divider) {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
:deep(.n-button__content) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:deep(.n-button):hover {
|
||||
// background-color: @purple !important;
|
||||
border: none !important;
|
||||
color: white !important;
|
||||
animation: swing .8s ease-in-out 3;
|
||||
}
|
||||
|
||||
@keyframes swing {
|
||||
|
||||
0%,
|
||||
20%,
|
||||
50%,
|
||||
80%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
:deep(.n-collapse-item__header) {
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user