feat:搜索新增常用菜单&移除常用菜单
This commit is contained in:
parent
5e0cacb556
commit
b9ad9522b1
@ -116,3 +116,15 @@
|
|||||||
.el-menu--horizontal .el-sub-menu .el-sub-menu__icon-arrow {
|
.el-menu--horizontal .el-sub-menu .el-sub-menu__icon-arrow {
|
||||||
right: calc(0px - var(--el-menu-base-level-padding)) !important;
|
right: calc(0px - var(--el-menu-base-level-padding)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 弹出搜索框
|
||||||
|
.header-search-select {
|
||||||
|
.el-select-dropdown__item {
|
||||||
|
height: unset !important;
|
||||||
|
line-height: unset !important;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,8 @@
|
|||||||
filterable
|
filterable
|
||||||
default-first-option
|
default-first-option
|
||||||
remote
|
remote
|
||||||
class="header_search_select"
|
popper-class="header-search-select"
|
||||||
|
placement="bottom"
|
||||||
placeholder="菜单搜索,支持标题、URL模糊查询"
|
placeholder="菜单搜索,支持标题、URL模糊查询"
|
||||||
@change="change">
|
@change="change">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@ -20,8 +21,13 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')">
|
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')">
|
||||||
<span style="float: left">{{ option.item.title.join(' > ') }}</span>
|
<span style="float: left">
|
||||||
<span style="float: right; color: var(--el-text-color-secondary); font-size: 13px">{{ option.item.path }}</span>
|
<div>
|
||||||
|
{{ option.item.title.join(' > ') }}
|
||||||
|
<div class="path">{{ option.item.path }}</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span style="float: right" @click.stop="handleLove(option.item)"> <svg-icon color="#ccc" name="star" /></span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@ -33,7 +39,8 @@ import Fuse from 'fuse.js'
|
|||||||
import { getNormalPath } from '@/utils/ruoyi'
|
import { getNormalPath } from '@/utils/ruoyi'
|
||||||
import { isHttp } from '@/utils/validate'
|
import { isHttp } from '@/utils/validate'
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
|
import { findItem, color16 } from '@/utils/ruoyi'
|
||||||
|
const { proxy } = getCurrentInstance()
|
||||||
const search = ref('')
|
const search = ref('')
|
||||||
const options = ref([])
|
const options = ref([])
|
||||||
const searchPool = ref([])
|
const searchPool = ref([])
|
||||||
@ -108,12 +115,15 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
|||||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path
|
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path
|
||||||
const data = {
|
const data = {
|
||||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||||
title: [...prefixTitle]
|
title: [...prefixTitle],
|
||||||
|
icon: 'menu',
|
||||||
|
menuTitle: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.meta && r.meta.title) {
|
if (r.meta && r.meta.title) {
|
||||||
data.title = [...data.title, r.meta.title]
|
data.title = [...data.title, r.meta.title]
|
||||||
|
data.icon = r.meta.icon
|
||||||
|
data.menuTitle = r.meta.title
|
||||||
if (r.redirect !== 'noRedirect') {
|
if (r.redirect !== 'noRedirect') {
|
||||||
// only push the routes with title
|
// only push the routes with title
|
||||||
// special case: need to exclude parent router without redirect
|
// special case: need to exclude parent router without redirect
|
||||||
@ -138,6 +148,28 @@ function querySearch(query) {
|
|||||||
options.value = []
|
options.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 添加快捷菜单
|
||||||
|
* @param {*} item
|
||||||
|
*/
|
||||||
|
function handleLove(item) {
|
||||||
|
var arraryObjectLocal = proxy.$cache.local.getJSON('commonlyUseMenu') || []
|
||||||
|
|
||||||
|
var len = 12
|
||||||
|
if (arraryObjectLocal.length >= len) {
|
||||||
|
proxy.$modal.msgError(`最多可添加${len}个常用菜单`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let index = findItem(arraryObjectLocal, 'path', item.path)
|
||||||
|
if (index <= -1) {
|
||||||
|
arraryObjectLocal.push({ ...item, color: color16() })
|
||||||
|
proxy.$cache.local.setJSON('commonlyUseMenu', arraryObjectLocal)
|
||||||
|
proxy.$modal.msgSuccess('添加成功')
|
||||||
|
usePermissionStore().setCommonlyUsedRoutes()
|
||||||
|
} else {
|
||||||
|
proxy.$modal.msgError('该菜单已存在')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
searchPool.value = generateRoutes(routes.value)
|
searchPool.value = generateRoutes(routes.value)
|
||||||
@ -178,18 +210,14 @@ watch(searchPool, (list) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header_search_select {
|
|
||||||
height: 50px;
|
|
||||||
|
|
||||||
:deep(.el-input__wrapper) {
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.path {
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -31,9 +31,13 @@
|
|||||||
<el-switch v-model="isDark" class="mt-2" inline-prompt />
|
<el-switch v-model="isDark" class="mt-2" inline-prompt />
|
||||||
</span>
|
</span>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
<!-- <h3 class="drawer-title">
|
||||||
|
{{ $t('layout.themeColor') }}
|
||||||
|
</h3> -->
|
||||||
<div class="drawer-item">
|
<div class="drawer-item">
|
||||||
<span>{{ $t('layout.themeColor') }}</span>
|
<span>{{ $t('layout.themeColor') }}</span>
|
||||||
<span class="comp-style">
|
<span class="comp-style quick-color-wrap">
|
||||||
|
<!-- <span :style="{ 'background-color': item }" v-for="item in predefineColors" @change="themeChange(item)"></span> -->
|
||||||
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
|
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -329,5 +333,16 @@ defineExpose({
|
|||||||
float: right;
|
float: right;
|
||||||
margin: -3px 8px 0px 0px;
|
margin: -3px 8px 0px 0px;
|
||||||
}
|
}
|
||||||
|
.quick-color-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { getRouters } from '@/api/system/menu'
|
|||||||
import Layout from '@/layout/index'
|
import Layout from '@/layout/index'
|
||||||
import ParentView from '@/components/ParentView'
|
import ParentView from '@/components/ParentView'
|
||||||
import InnerLink from '@/layout/components/InnerLink'
|
import InnerLink from '@/layout/components/InnerLink'
|
||||||
|
import cache from '@/plugins/cache'
|
||||||
|
|
||||||
// 匹配views里面所有的.vue文件
|
// 匹配views里面所有的.vue文件
|
||||||
const modules = import.meta.glob('./../../views/**/*.vue')
|
const modules = import.meta.glob('./../../views/**/*.vue')
|
||||||
@ -12,7 +13,8 @@ const usePermissionStore = defineStore('permission', {
|
|||||||
routes: [],
|
routes: [],
|
||||||
defaultRoutes: [],
|
defaultRoutes: [],
|
||||||
topbarRouters: [],
|
topbarRouters: [],
|
||||||
sidebarRouters: []
|
sidebarRouters: [],
|
||||||
|
commonlyUsedRoutes: [] //常用路由
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
setRoutes(routes) {
|
setRoutes(routes) {
|
||||||
@ -43,9 +45,23 @@ const usePermissionStore = defineStore('permission', {
|
|||||||
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
||||||
this.setDefaultRoutes(sidebarRoutes)
|
this.setDefaultRoutes(sidebarRoutes)
|
||||||
this.setTopbarRoutes(defaultRoutes)
|
this.setTopbarRoutes(defaultRoutes)
|
||||||
|
this.setCommonlyUsedRoutes()
|
||||||
resolve(rewriteRoutes)
|
resolve(rewriteRoutes)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
// 设置常用路由
|
||||||
|
setCommonlyUsedRoutes() {
|
||||||
|
var arraryObjectLocal = cache.local.getJSON('commonlyUseMenu') || []
|
||||||
|
this.commonlyUsedRoutes = arraryObjectLocal
|
||||||
|
},
|
||||||
|
// 移除常用路由
|
||||||
|
removeCommonlyUsedRoutes(item) {
|
||||||
|
var routes = this.commonlyUsedRoutes
|
||||||
|
|
||||||
|
const fi = routes.findIndex((v) => v.path === item.path)
|
||||||
|
routes.splice(fi, 1)
|
||||||
|
cache.local.setJSON('commonlyUseMenu', routes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -84,7 +84,7 @@ service.interceptors.response.use(
|
|||||||
} else if (message.includes('Request failed with status code 429')) {
|
} else if (message.includes('Request failed with status code 429')) {
|
||||||
message = '请求过于频繁,请稍后再试'
|
message = '请求过于频繁,请稍后再试'
|
||||||
} else if (message.includes('Request failed with status code')) {
|
} else if (message.includes('Request failed with status code')) {
|
||||||
message = '系统接口' + message.substr(message.length - 3) + '异常'
|
message = '系统接口' + message.substr(message.length - 3) + '异常,请联系管理员'
|
||||||
}
|
}
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: message,
|
message: message,
|
||||||
|
|||||||
@ -257,3 +257,28 @@ export function isEmpty(obj) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找对象的唯一键值对(比如id)去判断是否存在某个数据中
|
||||||
|
* @param {*} arr 数组
|
||||||
|
* @param {*} key 对象键值名
|
||||||
|
* @param {*} val
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function findItem(arr, key, val) {
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
if (arr[i][key] == val) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export function color16() {
|
||||||
|
//十六进制颜色随机
|
||||||
|
const r = Math.floor(Math.random() * 256)
|
||||||
|
const g = Math.floor(Math.random() * 256)
|
||||||
|
const b = Math.floor(Math.random() * 256)
|
||||||
|
const color = `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|||||||
@ -1,33 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tool-wrap">
|
<div class="tool-wrap">
|
||||||
<template v-for="item in menuList">
|
<template v-for="item in commonRouters">
|
||||||
<div class="tool-item" v-if="checkPermi(item)">
|
<div class="tool-item">
|
||||||
|
<span class="close-used" @click="removeRoute(item)" v-if="showRemove">
|
||||||
|
<el-icon><CloseBold /></el-icon>
|
||||||
|
</span>
|
||||||
<router-link :to="item.path">
|
<router-link :to="item.path">
|
||||||
<svg-icon :name="item.name" class-name="card-panel-icon mb10" :color="item.color" />
|
<svg-icon :name="item.icon" class-name="card-panel-icon mb10" :color="item.color" />
|
||||||
<div>{{ item.title }}</div>
|
<div class="title">{{ item.menuTitle }}</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<el-empty :image-size="80" v-if="commonRouters && commonRouters.length <= 0" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
const { proxy } = getCurrentInstance()
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
const menuList = ref([
|
|
||||||
{ path: '/dashboard', title: '控制台', color: '#40c9c6', name: 'dashboard' },
|
|
||||||
{ path: '/tool/gen', title: '代码生成', color: '#40c9c6', name: 'code', perms: ['tool:gen:list'] },
|
|
||||||
{ path: '/tool/file', title: '文件存储', color: '#6A5ACD', name: 'upload', perms: ['tool:file:list'] },
|
|
||||||
// // { path: '/system/user', title: '角色管理', color: '#7FFF00', name: 'peoples' },
|
|
||||||
{ path: '/system/dict', title: '字典管理', color: '#B0E0E6', name: 'dict', perms: ['system:dict:list'] },
|
|
||||||
{ path: '/monitor/job', title: '定时任务', color: '#D2691E', name: 'job', perms: ['monitor:job:list'] },
|
|
||||||
{ path: '/system/log/operlog', title: '操作日志', color: '#D2691E', name: 'form', perms: ['monitor:operlog:list'] }
|
|
||||||
// { path: '/system/log/logininfor', title: '登录日志', color: '#D2691E', name: 'logininfor' }
|
|
||||||
])
|
|
||||||
|
|
||||||
function checkPermi(v) {
|
const props = defineProps({
|
||||||
if (v && v.perms) {
|
modelValue: {
|
||||||
return proxy.$auth.hasPermiOr(v.perms)
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
return true
|
})
|
||||||
|
const showRemove = ref(false)
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
showRemove.value = val
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const commonRouters = computed(() => usePermissionStore().commonlyUsedRoutes)
|
||||||
|
|
||||||
|
// const { proxy } = getCurrentInstance()
|
||||||
|
// const menuList = ref([
|
||||||
|
// { path: '/dashboard', title: '控制台', color: '#40c9c6', name: 'dashboard' },
|
||||||
|
// { path: '/tool/gen', title: '代码生成', color: '#40c9c6', name: 'code', perms: ['tool:gen:list'] },
|
||||||
|
// { path: '/tool/file', title: '文件存储', color: '#6A5ACD', name: 'upload', perms: ['tool:file:list'] },
|
||||||
|
// // // { path: '/system/user', title: '角色管理', color: '#7FFF00', name: 'peoples' },
|
||||||
|
// { path: '/system/dict', title: '字典管理', color: '#B0E0E6', name: 'dict', perms: ['system:dict:list'] },
|
||||||
|
// { path: '/monitor/job', title: '定时任务', color: '#D2691E', name: 'job', perms: ['monitor:job:list'] },
|
||||||
|
// { path: '/system/log/operlog', title: '操作日志', color: '#D2691E', name: 'form', perms: ['monitor:operlog:list'] }
|
||||||
|
// // { path: '/system/log/logininfor', title: '登录日志', color: '#D2691E', name: 'logininfor' }
|
||||||
|
// ])
|
||||||
|
|
||||||
|
// function checkPermi(v) {
|
||||||
|
// if (v && v.perms) {
|
||||||
|
// return proxy.$auth.hasPermiOr(v.perms)
|
||||||
|
// }
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
function removeRoute(item) {
|
||||||
|
usePermissionStore().removeCommonlyUsedRoutes(item)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -40,11 +67,22 @@ function checkPermi(v) {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.card-panel-icon {
|
.card-panel-icon {
|
||||||
width: 30px;
|
width: 25px;
|
||||||
height: 30px;
|
height: 25px;
|
||||||
}
|
}
|
||||||
|
.title {
|
||||||
|
color: #606266;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.close-used {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -51,9 +51,10 @@
|
|||||||
<el-card shadow="hover">
|
<el-card shadow="hover">
|
||||||
<template #header>
|
<template #header>
|
||||||
<span><svg-icon name="tool" /> 常用功能</span>
|
<span><svg-icon name="tool" /> 常用功能</span>
|
||||||
|
<el-button class="home-card-more" text @click="showEdit = !showEdit">{{ $t('btn.edit') }}</el-button>
|
||||||
</template>
|
</template>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<el-scrollbar wrap-class="scrollbar-wrapper"> <CommonMenu></CommonMenu></el-scrollbar>
|
<el-scrollbar wrap-class="scrollbar-wrapper"> <CommonMenu v-model="showEdit"></CommonMenu></el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -108,7 +109,7 @@ import dayjs from 'dayjs'
|
|||||||
|
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useSocketStore from '@/store/modules/socket'
|
import useSocketStore from '@/store/modules/socket'
|
||||||
|
const showEdit = ref(false)
|
||||||
const data = {
|
const data = {
|
||||||
newVisitis: {
|
newVisitis: {
|
||||||
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user