'admin-21.01.31:处理菜单搜索、消息通知、适配手机等'
This commit is contained in:
parent
0c732bc543
commit
c3a8f42155
@ -528,7 +528,7 @@ export function formatTwoStageRoutes(arr: any) {
|
||||
return newArr
|
||||
}
|
||||
|
||||
// 缓存多级嵌套数组处理后的一维数组(tagsView中使用)
|
||||
// 缓存多级嵌套数组处理后的一维数组(tagsView、菜单过滤中使用:未过滤隐藏的(isHide))
|
||||
export function setCacheTagsViewRoutes() {
|
||||
store.dispatch('setTagsViewRoutes', formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children)
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ body,
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(238, 238, 238, 0.6);
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
.layout-el-aside-br-color {
|
||||
border-right: 1px solid rgb(238, 238, 238);
|
||||
@ -91,6 +91,10 @@ body,
|
||||
.layout-hide {
|
||||
display: none;
|
||||
}
|
||||
// 去掉手机版菜单导航背景
|
||||
.el-drawer {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* element plus 全局样式
|
||||
|
@ -256,6 +256,10 @@
|
||||
.el-input-number__decrease:hover {
|
||||
color: set-color(primary);
|
||||
}
|
||||
// 菜单搜索
|
||||
.el-autocomplete-suggestion__wrap {
|
||||
max-height: 280px !important;
|
||||
}
|
||||
|
||||
/* Select 选择器
|
||||
------------------------------- */
|
||||
@ -364,6 +368,14 @@
|
||||
color: set-color(primary);
|
||||
}
|
||||
|
||||
/* Form 表单
|
||||
------------------------------- */
|
||||
.el-form {
|
||||
.el-form-item:last-of-type {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 表格
|
||||
------------------------------- */
|
||||
.el-table .descending .sort-caret.descending {
|
||||
|
13
vue-admin-wonderful-next/src/theme/media/examples.scss
Normal file
13
vue-admin-wonderful-next/src/theme/media/examples.scss
Normal file
@ -0,0 +1,13 @@
|
||||
@import './index.scss';
|
||||
|
||||
/* 页面宽度小于576px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: $xs) {
|
||||
// tagsView 操作
|
||||
}
|
||||
|
||||
/* 页面宽度小于768px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: $sm) {
|
||||
// tagsView 操作
|
||||
}
|
21
vue-admin-wonderful-next/src/theme/media/form.scss
Normal file
21
vue-admin-wonderful-next/src/theme/media/form.scss
Normal file
@ -0,0 +1,21 @@
|
||||
@import './index.scss';
|
||||
|
||||
/* 页面宽度小于576px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: $xs) {
|
||||
}
|
||||
|
||||
/* 页面宽度小于768px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: $sm) {
|
||||
.el-form {
|
||||
.el-select {
|
||||
width: 100%;
|
||||
}
|
||||
.el-input,
|
||||
.el-textarea {
|
||||
width: 100%;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
/* 页面宽度小于576px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: $xs) {
|
||||
// MessageBox 弹框
|
||||
.el-message-box {
|
||||
width: 80% !important;
|
||||
}
|
||||
@ -11,7 +12,44 @@
|
||||
/* 页面宽度小于768px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: $sm) {
|
||||
// Breadcrumb 面包屑
|
||||
.layout-navbars-breadcrumb-hide {
|
||||
display: none;
|
||||
}
|
||||
// 外链视图
|
||||
.layout-view-link {
|
||||
a {
|
||||
max-width: 80%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
// 菜单搜索
|
||||
.layout-search-dialog {
|
||||
.el-autocomplete {
|
||||
width: 80% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 页面宽度小于1000px
|
||||
------------------------------- */
|
||||
@media screen and (max-width: 1000px) {
|
||||
// 布局配置
|
||||
.layout-drawer-content-flex {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '手机版不支持切换布局';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
height: 140px;
|
||||
line-height: 140px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import './login.scss';
|
||||
@import './error.scss';
|
||||
@import './layout.scss';
|
||||
@import './form.scss';
|
||||
@import './examples.scss';
|
||||
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<el-card shadow="hover" header="tagsView 功能演示">
|
||||
<el-form :inline="true" :model="formInline" size="small" label-width="60px" style="margin-bottom:1px;">
|
||||
<div>
|
||||
<el-card shadow="hover" header="tagsView 非当前页演示">
|
||||
<el-form :model="formInline" size="small" label-width="60px" style="margin-bottom:1px;">
|
||||
<el-form-item label="功能:">
|
||||
<el-select v-model="formInline.selectId" placeholder="请选择">
|
||||
<el-option v-for="item in selectOptions" :key="item.value" :label="item.label" :value="item.value">
|
||||
@ -11,16 +12,38 @@
|
||||
<el-input v-model="formInline.path" placeholder="路径如:/fun/tagsView" style="max-width:203px;"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onImplementClick">点击执行</el-button>
|
||||
<el-button type="primary" @click="onImplementClick" icon="el-icon-thumb">点击执行</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-divider></el-divider>
|
||||
</el-card>
|
||||
<el-card shadow="hover" header="tagsView 当前页演示" class="mt15">
|
||||
<div class="flex-warp">
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="primary" size="small" icon="el-icon-refresh-right" @click="refreshCurrentTagsView">刷新当前页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="info" size="small" icon="el-icon-close" @click="closeCurrentTagsView">关闭当前页</el-button>
|
||||
<el-button type="warning" size="small" icon="el-icon-circle-close" @click="closeOtherTagsView">关闭其它</el-button>
|
||||
<el-button type="danger" size="small" icon="el-icon-folder-delete" @click="closeAllTagsView">全部关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="warning" size="small" icon="el-icon-circle-close" @click="closeOtherTagsView">关闭其它
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-warp-item">
|
||||
<div class="flex-warp-item-box">
|
||||
<el-button type="danger" size="small" icon="el-icon-folder-delete" @click="closeAllTagsView">全部关闭
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -104,7 +127,17 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -160,11 +160,13 @@ export default {
|
||||
store.state.themeConfig.lockScreenTime = 30;
|
||||
setLocalThemeConfig();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initGetElement();
|
||||
initSetTime();
|
||||
initLockScreen();
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
clearInterval(state.setIntervalTime);
|
||||
clearInterval(state.isShowLockScreenIntervalTime);
|
||||
|
@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="layout-search-dialog">
|
||||
<el-dialog v-model="isShowSearch" width="300px" destroy-on-close :modal="false" fullscreen>
|
||||
<el-autocomplete v-model="menuQuery" :fetch-suggestions="menuSearch" placeholder="菜单搜索:支持中文、路由路径"
|
||||
prefix-icon="el-icon-search" ref="layoutMenuAutocompleteRef" @select="onHandleSelect">
|
||||
<template #default="{ item }">
|
||||
<div><i :class="item.meta.icon" class="mr10"></i>{{ item.meta.title }}</div>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent, ref, nextTick } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useStore } from "/@/store/index.ts";
|
||||
export default defineComponent({
|
||||
name: "layoutBreadcrumbSearch",
|
||||
setup() {
|
||||
const layoutMenuAutocompleteRef = ref();
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
isShowSearch: false,
|
||||
menuQuery: "",
|
||||
tagsViewList: [],
|
||||
});
|
||||
// 搜索弹窗打开
|
||||
const openSearch = () => {
|
||||
state.menuQuery = "";
|
||||
state.isShowSearch = true;
|
||||
initTageView();
|
||||
nextTick(() => {
|
||||
layoutMenuAutocompleteRef.value.focus();
|
||||
});
|
||||
};
|
||||
// 搜索弹窗关闭
|
||||
const closeSearch = () => {
|
||||
state.isShowSearch = false;
|
||||
};
|
||||
// 菜单搜索数据过滤
|
||||
const menuSearch = (queryString, cb) => {
|
||||
let results = queryString
|
||||
? state.tagsViewList.filter(createFilter(queryString))
|
||||
: state.tagsViewList;
|
||||
cb(results);
|
||||
};
|
||||
// 菜单搜索过滤
|
||||
const createFilter = (queryString) => {
|
||||
return (restaurant) => {
|
||||
return (
|
||||
restaurant.path.toLowerCase().indexOf(queryString.toLowerCase()) >
|
||||
-1 ||
|
||||
restaurant.meta.title
|
||||
.toLowerCase()
|
||||
.indexOf(queryString.toLowerCase()) > -1
|
||||
);
|
||||
};
|
||||
};
|
||||
// 初始化菜单数据
|
||||
const initTageView = () => {
|
||||
if (state.tagsViewList.length > 0) return false;
|
||||
store.state.tagsViewRoutes.map((v) => {
|
||||
if (!v.meta.isHide) state.tagsViewList.push({ ...v });
|
||||
});
|
||||
};
|
||||
// 当前菜单选中时
|
||||
const onHandleSelect = (item) => {
|
||||
let { path, redirect } = item;
|
||||
if (item.meta.isLink && !item.meta.isIframe)
|
||||
window.open(item.meta.isLink);
|
||||
else if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
closeSearch();
|
||||
};
|
||||
return {
|
||||
layoutMenuAutocompleteRef,
|
||||
openSearch,
|
||||
closeSearch,
|
||||
menuSearch,
|
||||
onHandleSelect,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-search-dialog {
|
||||
::v-deep(.el-dialog) {
|
||||
box-shadow: unset !important;
|
||||
border-radius: 0 !important;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
::v-deep(.el-autocomplete) {
|
||||
width: 560px;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
::v-deep(.el-dialog__headerbtn) {
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
.el-dialog__close {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,12 +1,21 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user" :style="setFlexAutoStyle">
|
||||
<div class="layout-navbars-breadcrumb-user-icon">
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
|
||||
<i class="el-icon-search" title="菜单搜索"></i>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
|
||||
<i class="icon-skin iconfont" title="布局配置"></i>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon"><i class="el-icon-bell" title="消息"></i></div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onUserNewsPopoverClick">
|
||||
<el-popover placement="bottom" trigger="click" v-model:visible="isShowUserNewsPopover" :width="300">
|
||||
<template #reference>
|
||||
<el-badge :is-dot="true">
|
||||
<i class="el-icon-bell" title="消息"></i>
|
||||
</el-badge>
|
||||
</template>
|
||||
<UserNews />
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick"><i class="iconfont"
|
||||
:title="isScreenfull ? '开全屏' : '关全屏'" :class="!isScreenfull?'icon-fullscreen':'icon-tuichuquanping'"></i></div>
|
||||
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
||||
@ -25,6 +34,7 @@
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<Search ref="searchRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -36,6 +46,7 @@ import {
|
||||
reactive,
|
||||
toRefs,
|
||||
toRef,
|
||||
ref,
|
||||
} from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessageBox, ElMessage } from "element-plus";
|
||||
@ -43,14 +54,19 @@ import screenfull from "screenfull";
|
||||
import { resetRoute } from "/@/router/index.ts";
|
||||
import { useStore } from "/@/store/index.ts";
|
||||
import { clearSession } from "/@/utils/storage.ts";
|
||||
import UserNews from "/@/views/layout/navBars/breadcrumb/userNews.vue";
|
||||
import Search from "/@/views/layout/navBars/breadcrumb/search.vue";
|
||||
export default {
|
||||
name: "layoutBreadcrumbUser",
|
||||
components: { UserNews, Search },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance();
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const searchRef = ref();
|
||||
const state = reactive({
|
||||
isScreenfull: false,
|
||||
isShowUserNewsPopover: false,
|
||||
});
|
||||
// 设置布局
|
||||
const setFlexAutoStyle = computed(() => {
|
||||
@ -113,11 +129,22 @@ export default {
|
||||
router.push(path);
|
||||
}
|
||||
};
|
||||
// 菜单搜索点击
|
||||
const onSearchClick = () => {
|
||||
searchRef.value.openSearch();
|
||||
};
|
||||
// 我的消息点击
|
||||
const onUserNewsPopoverClick = () => {
|
||||
state.isShowUserNewsPopover = !state.isShowUserNewsPopover;
|
||||
};
|
||||
return {
|
||||
setFlexAutoStyle,
|
||||
onLayoutSetingClick,
|
||||
onHandleCommandClick,
|
||||
onScreenfullClick,
|
||||
onSearchClick,
|
||||
onUserNewsPopoverClick,
|
||||
searchRef,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
@ -146,6 +173,8 @@ export default {
|
||||
color: var(--bg-topBarColor);
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
i {
|
||||
@ -157,5 +186,14 @@ export default {
|
||||
::v-deep(.el-dropdown) {
|
||||
color: var(--bg-topBarColor);
|
||||
}
|
||||
::v-deep(.el-badge) {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
::v-deep(.el-badge__content.is-fixed) {
|
||||
top: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="head-box">
|
||||
<div class="head-box-title">通知</div>
|
||||
<div class="head-box-btn" v-if="newsList.length > 0" @click="onAllReadClick">全部已读</div>
|
||||
</div>
|
||||
<div class="content-box">
|
||||
<template v-if="newsList.length > 0">
|
||||
<div class="content-box-item" v-for="(v,k) in newsList" :key="k">
|
||||
<div>{{v.label}}</div>
|
||||
<div class="content-box-msg">
|
||||
{{v.value}}</div>
|
||||
<div class="content-box-time">{{v.time}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty description="暂无通知" v-else></el-empty>
|
||||
</div>
|
||||
<div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">前往通知中心</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs } from "vue";
|
||||
export default {
|
||||
name: "layoutBreadcrumbUserNews",
|
||||
setup() {
|
||||
const state = reactive({
|
||||
newsList: [
|
||||
{
|
||||
label: "关于版本开发的通知",
|
||||
value:
|
||||
"vue-admin-wonderful-next,基于 vue3 + CompositionAPI + typescript + vite + element plus,正在抓紧时间开发!",
|
||||
time: "2020-12-08",
|
||||
},
|
||||
{
|
||||
label: "关于学习交流的通知",
|
||||
value: "QQ群号码 665452019,欢迎小伙伴入群学习交流探讨!",
|
||||
time: "2020-12-08",
|
||||
},
|
||||
],
|
||||
});
|
||||
// 全部已读点击
|
||||
const onAllReadClick = () => {
|
||||
state.newsList = [];
|
||||
};
|
||||
// 前往通知中心点击
|
||||
const onGoToGiteeClick = () => {
|
||||
window.open("https://gitee.com/lyt-top/vue-admin-wonderful-next");
|
||||
};
|
||||
return {
|
||||
onAllReadClick,
|
||||
onGoToGiteeClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-navbars-breadcrumb-user-news {
|
||||
.head-box {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
box-sizing: border-box;
|
||||
color: #333333;
|
||||
justify-content: space-between;
|
||||
height: 35px;
|
||||
align-items: center;
|
||||
.head-box-btn {
|
||||
color: var(--color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-box {
|
||||
font-size: 13px;
|
||||
.content-box-item {
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 12px 0;
|
||||
&:last-of-type {
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
.content-box-msg {
|
||||
color: #999999;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.content-box-time {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
.foot-box {
|
||||
height: 35px;
|
||||
color: var(--color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top: 1px solid #ebeef5;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
::v-deep(.el-empty__description p) {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="layout-scrollbar">
|
||||
<div class="layout-view-bg-white flex">
|
||||
<div class="layout-view-bg-white flex layout-view-link">
|
||||
<a :href="currentRouteMeta.isLink" target="_blank"
|
||||
class="flex-margin">{{currentRouteMeta.title}}:{{currentRouteMeta.isLink}}</a>
|
||||
</div>
|
||||
|
@ -6,8 +6,8 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input type="password" placeholder="密码:123456" prefix-icon="el-icon-lock" v-model="ruleForm.password" clearable
|
||||
autocomplete="off">
|
||||
<el-input type="password" placeholder="密码:123456" prefix-icon="el-icon-lock" v-model="ruleForm.password"
|
||||
autocomplete="off" show-password>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
|
Loading…
Reference in New Issue
Block a user