'admin-21.01.31:处理菜单搜索、消息通知、适配手机等'

This commit is contained in:
lyt-Top 2021-01-31 02:13:22 +08:00
parent 0c732bc543
commit c3a8f42155
14 changed files with 420 additions and 31 deletions

View File

@ -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)
}

View File

@ -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 全局样式

View File

@ -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 {

View File

@ -0,0 +1,13 @@
@import './index.scss';
/* 页面宽度小于576px
------------------------------- */
@media screen and (max-width: $xs) {
// tagsView 操作
}
/* 页面宽度小于768px
------------------------------- */
@media screen and (max-width: $sm) {
// tagsView 操作
}

View 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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -1,3 +1,5 @@
@import './login.scss';
@import './error.scss';
@import './layout.scss';
@import './form.scss';
@import './examples.scss';

View File

@ -1,26 +1,49 @@
<template>
<el-card shadow="hover" header="tagsView 功能演示">
<el-form :inline="true" :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">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="路径:">
<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-form-item>
</el-form>
<el-divider></el-divider>
<el-button type="primary" size="small" icon="el-icon-refresh-right" @click="refreshCurrentTagsView">刷新当前页
</el-button>
<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>
</el-card>
<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">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="路径:">
<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" icon="el-icon-thumb">点击执行</el-button>
</el-form-item>
</el-form>
</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>
</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>

View File

@ -160,11 +160,13 @@ export default {
store.state.themeConfig.lockScreenTime = 30;
setLocalThemeConfig();
};
//
onMounted(() => {
initGetElement();
initSetTime();
initLockScreen();
});
//
onUnmounted(() => {
clearInterval(state.setIntervalTime);
clearInterval(state.isShowLockScreenIntervalTime);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>