improve: 整理优化

This commit is contained in:
From-wh 2023-05-04 10:02:28 +08:00
parent 161b960f3d
commit f47cda65ea
49 changed files with 25 additions and 2583 deletions

View File

@ -84,7 +84,7 @@ export default {
this.openSetingsDrawer();
this.getLayoutThemeConfig();
this.$nextTick((e) => {
this.getVersion();
// this.getVersion();
});
},

View File

@ -1,2 +0,0 @@
import ABackTop from './index.vue';
export default ABackTop;

View File

@ -1,90 +0,0 @@
<template>
<div :class="classes" :style="styles" @click="back">
<slot>
<div :class="innerClasses">
<i class="ivu-icon ivu-icon-ios-arrow-up"></i>
</div>
</slot>
</div>
</template>
<script>
import { scrollTop } from '@/libs/util';
import { on, off } from '@/libs/tools';
const prefixCls = 'ivu-back-top';
export default {
name: 'ABackTop',
props: {
height: {
type: Number,
default: 400,
},
bottom: {
type: Number,
default: 30,
},
right: {
type: Number,
default: 30,
},
duration: {
type: Number,
default: 1000,
},
container: {
type: null,
default: window,
},
},
data() {
return {
backTop: false,
};
},
mounted() {
// window.addEventListener('scroll', this.handleScroll, false)
// window.addEventListener('resize', this.handleScroll, false)
on(this.containerEle, 'scroll', this.handleScroll);
on(this.containerEle, 'resize', this.handleScroll);
},
beforeDestroy() {
// window.removeEventListener('scroll', this.handleScroll, false)
// window.removeEventListener('resize', this.handleScroll, false)
off(this.containerEle, 'scroll', this.handleScroll);
off(this.containerEle, 'resize', this.handleScroll);
},
computed: {
classes() {
return [
`${prefixCls}`,
{
[`${prefixCls}-show`]: this.backTop,
},
];
},
styles() {
return {
bottom: `${this.bottom}px`,
right: `${this.right}px`,
};
},
innerClasses() {
return `${prefixCls}-inner`;
},
containerEle() {
return this.container === window ? window : document.querySelector(this.container);
},
},
methods: {
handleScroll() {
this.backTop = this.containerEle.scrollTop >= this.height;
},
back() {
let target = typeof this.container === 'string' ? this.containerEle : document.documentElement || document.body;
const sTop = target.scrollTop;
scrollTop(this.containerEle, sTop, 0, this.duration);
this.$emit('on-click');
},
},
};
</script>

View File

@ -1,65 +0,0 @@
<template></template>
<!--<template>-->
<!--<Tooltip :content="tipContent" transfer>-->
<!--<div class="error-store">-->
<!--<Badge dot :count="lengthError?lengthError:0">-->
<!--<div @click="openErrorLoggerPage">-->
<!--<Icon :size="20" type="ios-locate-outline"/>-->
<!--</div>-->
<!--</Badge>-->
<!--</div>-->
<!--</Tooltip>-->
<!--</template>-->
<!--<script>-->
<!--import { mapGetters } from 'vuex'-->
<!--export default {-->
<!--name: 'ErrorStore',-->
<!--props: {-->
<!--count: {-->
<!--type: Number,-->
<!--default: 0-->
<!--},-->
<!--hasRead: {-->
<!--type: Boolean,-->
<!--default: false-->
<!--}-->
<!--},-->
<!--computed: {-->
<!--...mapGetters('error', [-->
<!--'length',-->
<!--'lengthError'-->
<!--]),-->
<!--tipContent () {-->
<!--if (!this.length) {-->
<!--return '没有日志或异常'-->
<!--} else {-->
<!--let text = `${this.length} 条日志`-->
<!--if (this.lengthError) text += ` | 包含 ${this.lengthError} 个异常`-->
<!--return text-->
<!--}-->
<!--}-->
<!--},-->
<!--methods: {-->
<!--openErrorLoggerPage () {-->
<!--this.$router.push('/admin/system/log')-->
<!--}-->
<!--}-->
<!--}-->
<!--</script>-->
<!--<style lang="less">-->
<!--.error-store:hover{-->
<!--color: #2D8cF0;-->
<!--}-->
<!--.error-store{-->
<!--margin-right: 21px;-->
<!--cursor: pointer;-->
<!--.ivu-badge-dot{-->
<!--top: 20px;-->
<!--}-->
<!--.ivu-btn.ivu-btn-text{-->
<!--padding: 5px 1px 6px;-->
<!--}-->
<!--}-->
<!--</style>-->

View File

@ -1,2 +0,0 @@
import ErrorStore from './error-store.vue';
export default ErrorStore;

View File

@ -1,97 +0,0 @@
<template>
<div v-if="showFullScreenBtn" class="full-screen-btn-con">
<Tooltip :content="value ? '退出全屏' : '全屏'" placement="bottom">
<Icon @click.native="handleChange" :type="value ? 'ios-contract' : 'ios-qr-scanner'" :size="22"></Icon>
</Tooltip>
</div>
</template>
<script>
export default {
name: 'Fullscreen',
computed: {
showFullScreenBtn() {
return window.navigator.userAgent.indexOf('MSIE') < 0;
},
},
props: {
value: {
type: Boolean,
default: false,
},
},
methods: {
handleFullscreen() {
let main = document.body;
if (this.value) {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
} else {
if (main.requestFullscreen) {
main.requestFullscreen();
} else if (main.mozRequestFullScreen) {
main.mozRequestFullScreen();
} else if (main.webkitRequestFullScreen) {
main.webkitRequestFullScreen();
} else if (main.msRequestFullscreen) {
main.msRequestFullscreen();
}
}
},
handleChange() {
this.handleFullscreen();
},
},
mounted() {
let isFullscreen =
document.fullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement ||
document.fullScreen ||
document.mozFullScreen ||
document.webkitIsFullScreen;
isFullscreen = !!isFullscreen;
document.addEventListener('fullscreenchange', () => {
this.$emit('input', !this.value);
this.$emit('on-change', !this.value);
});
document.addEventListener('mozfullscreenchange', () => {
this.$emit('input', !this.value);
this.$emit('on-change', !this.value);
});
document.addEventListener('webkitfullscreenchange', () => {
this.$emit('input', !this.value);
this.$emit('on-change', !this.value);
});
document.addEventListener('msfullscreenchange', () => {
this.$emit('input', !this.value);
this.$emit('on-change', !this.value);
});
this.$emit('input', isFullscreen);
},
};
</script>
<style lang="less">
.full-screen-btn-con {
margin-right: 17px !important;
}
.full-screen-btn-con .ivu-tooltip-rel i {
font-size: 20px !important;
color: rgba(0, 0, 0, 0.65);
}
.full-screen-btn-con .ivu-tooltip-rel {
height: 50px;
line-height: 50px;
i {
cursor: pointer;
}
}
</style>

View File

@ -1,2 +0,0 @@
import Fullscreen from './fullscreen.vue';
export default Fullscreen;

View File

@ -1,5 +0,0 @@
.custom-bread-crumb {
display: inline-block;
vertical-align: top;
color: #fff;
}

View File

@ -1,78 +0,0 @@
<template>
<div class="custom-bread-crumb">
<Breadcrumb :style="{ fontSize: `${fontSize}px` }">
<BreadcrumbItem
v-for="(item, index) in list"
:key="index"
:to="index !== 1 ? item.path : ''"
v-if="listLast[0].path !== homePath"
>
<common-icon style="margin-right: 4px" :type="item.icon || ''" />
{{ item.title }}
</BreadcrumbItem>
<BreadcrumbItem :to="listLast[0].path" v-if="listLast[0].path === homePath">
<common-icon style="margin-right: 4px" :type="listLast[0].icon || ''" />
{{ listLast[0].title }}
</BreadcrumbItem>
<BreadcrumbItem v-else :to="listLast[0].path">
<common-icon style="margin-right: 4px" :type="listLast[0].icon || ''" />
{{ listLast[0].title }}
</BreadcrumbItem>
</Breadcrumb>
</div>
</template>
<script>
import { showTitle } from '@/libs/util';
import CommonIcon from '_c/common-icon';
import settings from '@/setting';
import './custom-bread-crumb.less';
export default {
name: 'customBreadCrumb',
components: {
CommonIcon,
},
props: {
list: {
type: Array,
default: () => [],
},
listLast: {
type: Array,
default: () => [],
},
collapsed: Boolean,
fontSize: {
type: Number,
default: 14,
},
showIcon: {
type: Boolean,
default: false,
},
},
data() {
return {
homePath: settings.routePre + '/home',
};
},
methods: {
showTitle(item) {
return showTitle(item, this);
},
isCustomIcon(iconName) {
return iconName.indexOf('_') === 0;
},
getCustomIconName(iconName) {
return iconName.slice(1);
},
},
};
</script>
<style lang="less">
.ivu-breadcrumb-item-link {
font-weight: 400;
.ivu-icon {
line-height: 14px;
}
}
</style>

View File

@ -1,2 +0,0 @@
import customBreadCrumb from './custom-bread-crumb.vue';
export default customBreadCrumb;

View File

@ -1,22 +0,0 @@
.header-bar {
width: 100%;
height: 100%;
position: relative;
color: #fff !important;
display: flex;
justify-content: space-between;
.header-l {
display: flex;
align-items: center;
}
.custom-content-con {
float: right;
height: auto;
padding-right: 20px;
line-height: 50px;
& > * {
float: right;
}
}
}

View File

@ -1,85 +0,0 @@
<template>
<div class="header-bar">
<div class="header-l">
<sider-trigger @on-change="handleCollpasedChange"></sider-trigger>
<!-- <span class="i-layout-header-trigger" @click="handleReload">
<Icon type="ios-refresh" color='#fff' />
</span> -->
<custom-bread-crumb show-icon :list="breadCrumbList" :listLast="crumbPast"></custom-bread-crumb>
</div>
<div class="custom-content-con">
<slot></slot>
</div>
</div>
</template>
<style scoped lang="less">
.i-layout-header-trigger {
position: absolute;
margin: 0 10px;
cursor: pointer;
font-size: 19px;
}
.i-layout-header-trigger:hover {
color: #2d8cf0;
}
</style>
<script>
import siderTrigger from './sider-trigger';
import customBreadCrumb from './custom-bread-crumb';
import { R } from '@/libs/util';
import { mapState } from 'vuex';
import './header-bar.less';
export default {
name: 'HeaderBar',
components: {
siderTrigger,
customBreadCrumb,
},
props: {
// collapsed: Boolean,
},
computed: {
...mapState('layout', ['menuCollapse']),
breadCrumbList() {
let openMenus = this.$store.state.menus.openMenus;
let menuList = this.$store.state.menus.menusName;
let allMenuList = R(menuList, []);
let selectMenu = [];
if (allMenuList.length > 0) {
openMenus.forEach((i) => {
allMenuList.forEach((a) => {
if (i === a.path) {
selectMenu.push(a);
}
});
});
}
return selectMenu;
},
crumbPast() {
let that = this;
let menuList = that.$store.state.menus.menusName;
let allMenuList = R(menuList, []);
let selectMenu = [];
if (allMenuList.length > 0) {
allMenuList.forEach((a) => {
if (that.$route.path === a.path) {
selectMenu.push(a);
}
});
}
return selectMenu;
},
},
mounted() {},
methods: {
handleCollpasedChange(state) {
this.$emit('on-coll-change', state);
},
handleReload() {
this.$emit('on-reload');
},
},
};
</script>

View File

@ -1,2 +0,0 @@
import HeaderBar from './header-bar';
export default HeaderBar;

View File

@ -1,2 +0,0 @@
import siderTrigger from './sider-trigger.vue';
export default siderTrigger;

View File

@ -1,19 +0,0 @@
.trans {
transition: transform 0.2s ease;
}
@size: 40px;
.sider-trigger-a {
display: inline-block;
text-align: center;
// color: #fff;
cursor: pointer;
z-index: 99;
.iconcaidanshouqi {
.trans;
vertical-align: top;
}
&.collapsed /deep/ .iconcaidanshouqi {
transform: rotateZ(90deg);
.trans;
}
}

View File

@ -1,55 +0,0 @@
<template>
<div class="sider-menu" @click="handleChange">
<a type="text" :class="['sider-trigger-a', menuCollapse ? 'menuCollapse' : '']"
><i class="iconfont" color="#fff" :class="menuCollapse ? 'iconcaidanzhankai' : 'iconcaidanshouqi'"></i
></a>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'siderTrigger',
props: {
// collapsed: Boolean,
icon: {
type: String,
default: 'navicon-round',
},
size: {
type: Number,
default: 18,
},
},
computed: {
...mapState('layout', ['menuCollapse']),
},
data() {
return {};
},
methods: {
handleChange() {
this.$emit('on-change', !this.menuCollapse);
this.$store.commit('layout/changeCol', !this.menuCollapse);
},
},
};
</script>
<style lang="less" scoped>
.sider-menu {
height: 50px;
// border-top: 1px solid #f2f2f2;
color: #515a6e;
padding: 0 16px;
display: flex;
justify-content: center;
align-items: center;
}
@import './sider-trigger.less';
.sider-trigger-a {
height: 50px;
color: rgba(0, 0, 0, 0.65);
}
.iconcaidanshouqi {
font-size: 16px;
}
</style>

View File

@ -1,181 +0,0 @@
<template>
<div class="header-notice">
<Dropdown @on-click="jumpUrl" :transfer="true">
<div>
<Badge dot :count="needList.length ? needList.length : 0">
<Icon class="icon" type="md-notifications-outline" size="20"></Icon>
</Badge>
</div>
<DropdownMenu slot="list" v-show="needList.length">
<DropdownItem :name="item.url" v-for="(item, index) in needList" :key="index"
><Icon :type="item.icon" :style="'background-color:' + item.iconColor" class="iconImg" />{{
item.title
}}</DropdownItem
>
</DropdownMenu>
</Dropdown>
</div>
</template>
<style scoped>
.ivu-dropdown-item {
display: flex;
align-items: center;
}
.icon {
color: rgba(0, 0, 0, 0.65);
}
.ivu-dropdown-item .iconImg {
margin-right: 10px;
}
</style>
<style lang="less">
.header-notice {
margin-right: 30px;
cursor: pointer;
}
.header-notice .ivu-dropdown-item {
font-size: 14px !important;
font-weight: 400;
line-height: 22px;
color: #515a6e;
}
.header-notice .ivu-dropdown-item ~ .ivu-dropdown-item {
border-top: 1px solid #e8eaec;
}
.header-notice .iconImg {
width: 32px;
height: 32px;
line-height: 32px;
border-radius: 50%;
color: rgba(0, 0, 0, 0.65);
font-size: 18px;
padding-right: 10px;
vertical-align: middle;
}
.header-notice .ivu-dropdown {
height: 30px;
line-height: 30px;
}
.header-notice .ivu-dropdown .ivu-select-dropdown {
margin-top: 22px;
}
.header-notice .ivu-badge-dot {
z-index: 0 !important;
top: 4px;
right: -4px;
}
</style>
<script>
let newOrderAudioLink = new Audio(require('@/assets/video/newOrderAudioLink.mp3'));
import { jnoticeRequest } from '@/api/common';
import { adminSocket } from '@/libs/socket';
import { getCookies, removeCookies, setCookies } from '@/libs/util';
export default {
name: 'User',
data() {
return {
needList: [],
newOrderAudioLink: null,
};
},
mounted() {
this.getNotict();
this.newOrderAudioLink = newOrderAudioLink;
adminSocket.then((ws) => {
ws.send({
type: 'login',
data: getCookies('token'),
});
let that = this;
ws.$on('ADMIN_NEW_PUSH', function (data) {
that.getNotict();
});
ws.$on('NEW_ORDER', function (data) {
that.$Notice.info({
title: '新订单',
duration: 8,
desc: '您有一个新的订单,ID为(' + data.order_id + '),请注意查看',
});
if (this.newOrderAudioLink) this.newOrderAudioLink.play();
that.messageList.push({
title: '新订单提醒',
icon: 'md-bulb',
iconColor: '#87d068',
time: 0,
read: 0,
});
});
ws.$on('NEW_REFUND_ORDER', function (data) {
that.$Notice.warning({
title: '退款订单提醒',
duration: 8,
desc: '您有一个订单申请退款,ID为(' + data.order_id + '),请注意查看',
});
if (window.newOrderAudioLink) this.newOrderAudioLink.play();
that.messageList.push({
title: '退款订单提醒',
icon: 'md-information',
iconColor: '#fe5c57',
time: 0,
read: 0,
});
});
ws.$on('WITHDRAW', function (data) {
that.$Notice.warning({
title: '提现提醒',
duration: 8,
desc: '有用户申请提现,编号为(' + data.id + '),请注意查看',
});
that.messageList.push({
title: '退款订单提醒',
icon: 'md-people',
iconColor: '#f06292',
time: 0,
read: 0,
});
});
ws.$on('STORE_STOCK', function (data) {
that.$Notice.warning({
title: '库存预警',
duration: 8,
desc: '商品ID为(' + data.id + ')的库存不足啦,请注意查看~',
});
that.messageList.push({
title: '库存预警',
icon: 'md-information',
iconColor: '#fe5c57',
time: 0,
read: 0,
});
});
ws.$on('PAY_SMS_SUCCESS', function (data) {
that.$Notice.info({
title: '短信充值成功',
duration: 8,
desc: '恭喜您充值' + data.price + '元,获得' + data.number + '条短信',
});
that.messageList.push({
title: '短信充值成功',
icon: 'md-bulb',
iconColor: '#87d068',
time: 0,
read: 0,
});
});
});
},
methods: {
getNotict() {
jnoticeRequest()
.then((res) => {
this.needList = res.data || [];
})
.catch(() => {});
},
jumpUrl(url) {
this.$router.push({ path: url });
},
},
};
</script>

View File

@ -1,80 +0,0 @@
<template>
<div class="search">
<Select
v-model="currentVal"
class="select"
placeholder="菜单搜索"
filterable
remote
clearable
:remote-method="remoteMethod"
:loading="loading"
@on-change="remoteMethod"
>
<Option
v-for="(option, index) in menusList"
:value="option.menu_path"
:key="index"
:disabled="option.type === 1"
>{{ option.menu_name }}</Option
>
</Select>
</div>
</template>
<style scoped>
.search /deep/ .ivu-select-selection {
margin-right: 20px;
border-radius: 30px;
border: none !important;
}
.search /deep/ .ivu-select-visible .ivu-select-selection {
box-shadow: unset !important;
border: none !important;
}
.search /deep/ .ivu-select-selection:hover,
.search /deep/ .ivu-select-selection:active {
border: none;
}
.search /deep/ li.ivu-select-item {
text-align: left;
}
.search /deep/ .select .ivu-select-input,
.search /deep/ .select .ivu-select-item {
font-size: 13px !important;
}
.search /deep/ .ivu-select-input {
padding-left: 19px;
border-radius: 30px;
background-color: #f8f8f9;
}
</style>
<script>
import { menusListApi } from '@/api/account';
export default {
name: 'iHeaderSearch',
data() {
return {
currentVal: '',
loading: false,
menusList: [],
};
},
computed: {},
created() {
this.getMenusList();
},
methods: {
getMenusList() {
this.loading = true;
menusListApi().then((res) => {
this.loading = false;
this.menusList = res.data;
});
},
remoteMethod() {
this.$router.push({ path: this.currentVal });
},
},
};
</script>

View File

@ -1,2 +0,0 @@
import Language from './language.vue';
export default Language;

View File

@ -1,51 +0,0 @@
<template>
<div>
<Dropdown trigger="click" @on-click="selectLang" :transfer="true">
<a href="javascript:void(0)">
{{ title }}
<Icon :size="18" type="md-arrow-dropdown" />
</a>
<DropdownMenu slot="list">
<DropdownItem v-for="(value, key) in localList" :name="key" :key="`lang-${key}`">{{ value }}</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</template>
<script>
export default {
name: 'Language',
props: {
lang: String,
},
data() {
return {
langList: {
'zh-CN': '语言',
'zh-TW': '語言',
'en-US': 'Lang',
},
localList: {
'zh-CN': '中文简体',
'zh-TW': '中文繁体',
'en-US': 'English',
},
};
},
watch: {
lang(lang) {
this.$i18n.locale = lang;
},
},
computed: {
title() {
return this.langList[this.lang];
},
},
methods: {
selectLang(name) {
this.$emit('on-lang-change', name);
},
},
};
</script>

View File

@ -1,2 +0,0 @@
import reload from './reload.vue';
export default reload;

View File

@ -1,41 +0,0 @@
<template>
<div class="refresh-btn-con" @click="handleReload">
<Tooltip content="刷新" placement="bottom">
<Icon class="icon" type="ios-refresh" />
</Tooltip>
</div>
</template>
<script>
import { R } from '@/libs/util';
export default {
name: 'reload',
mounted() {},
methods: {
handleReload() {
this.$emit('on-reload');
},
},
};
</script>
<style scoped lang="less">
.refresh-btn-con {
margin-right: 17px !important;
cursor: pointer;
font-size: 28px;
color: rgba(0, 0, 0, 0.65);
}
.icon {
font-size: 24px;
width: 20px;
color: rgba(0, 0, 0, 0.65);
}
// .icon:hover{
// animation: rotate 6s linear infinite;
// }
// @keyframes rotate {
// 0%{-webkit-transform:rotate(0deg);}
// 100%{-webkit-transform:rotate(360deg);}
// }
</style>

View File

@ -1,124 +0,0 @@
<template>
<Dropdown
ref="dropdown"
@on-click="handleClick"
:class="hideTitle ? '' : 'collased-menu-dropdown'"
:transfer="hideTitle"
:placement="placement"
transfer-class-name="dro-style"
>
<a
class="drop-menu-a"
type="text"
:class="{ on: parentItem.path == activeMenuPath }"
@mouseover="handleMousemove($event, children)"
:style="{ textAlign: !hideTitle ? 'left' : '' }"
@click="handClick(parentItem.path)"
><common-icon :type="parentItem.icon" /><Icon
style="float: right"
v-if="!hideTitle"
type="ios-arrow-forward"
:size="16"
/>
<span class="title">{{ parentItem.title }}</span></a
>
<DropdownMenu ref="dropdown" slot="list" v-if="children">
<div v-for="child in children">
<template v-if="child.auth === undefined">
<collapsed-menu
class="child-menu"
v-if="showChildren(child)"
:parent-item="child"
:key="`drop-${child.path}`"
></collapsed-menu>
<DropdownItem v-else :key="`drop-${child.path}`" :name="child.path"
><common-icon :type="child.icon" /><span class="menu-title">{{ child.title }}</span></DropdownItem
>
</template>
</div>
</DropdownMenu>
</Dropdown>
</template>
<script>
import mixin from './mixin';
import itemMixin from './item-mixin';
import { findNodeUpperByClasses } from '@/libs/util';
import settings from '@/setting';
export default {
name: 'CollapsedMenu',
mixins: [mixin, itemMixin],
props: {
hideTitle: {
type: Boolean,
default: false,
},
rootIconSize: {
type: Number,
default: 16,
},
activeMenuPath: {
type: String,
default: '',
},
},
data() {
return {
placement: 'right-start',
};
},
methods: {
handleClick(name) {
this.$emit('on-click', name, this.activeMenuPath);
},
handClick(name) {
this.$emit('on-click', name, this.activeMenuPath);
},
handleMousemove(event, children) {
const { pageY } = event;
const height = children.length * 38;
const isOverflow = pageY + height < window.innerHeight;
this.placement = isOverflow ? 'right-start' : 'right-end';
},
},
mounted() {
let dropdown = findNodeUpperByClasses(this.$refs.dropdown.$el, ['ivu-select-dropdown', 'ivu-dropdown-transfer']);
if (dropdown) dropdown.style.overflow = 'visible';
},
};
</script>
<style lang="less" scoped>
@import './side-menu.less';
/deep/ .collased-menu-dropdown {
width: @side-width;
}
.child-menu {
display: flex;
justify-content: space-between;
width: 100%;
}
.ivu-dropdown-menu {
padding: 5px 0px;
width: 140px !important;
}
.ivu-dropdown-menu /deep/ .ivu-dropdown-rel {
width: 100% !important;
font-size: 14px;
line-height: 14px;
padding: 14px 20px;
}
.ivu-dropdown-menu /deep/ .ivu-dropdown-item {
width: 140px !important;
padding: 14px 20px;
font-size: 14px !important;
line-height: 14px;
}
.drop-menu-a /deep/ .ivu-dropdown-rel {
width: 160px !important;
font-size: 14px;
line-height: 14px;
color: #fff;
}
</style>

View File

@ -1,2 +0,0 @@
import SideMenu from './side-menu.vue';
export default SideMenu;

View File

@ -1,21 +0,0 @@
export default {
props: {
parentItem: {
type: Object,
default: () => {},
},
theme: String,
iconSize: Number,
},
computed: {
parentName() {
return this.parentItem.name;
},
children() {
return this.parentItem.children;
},
textColor() {
return this.theme === 'dark' ? '#fff' : '#495060';
},
},
};

View File

@ -1,18 +0,0 @@
import CommonIcon from '_c/common-icon';
import { showTitle } from '@/libs/util';
export default {
components: {
CommonIcon,
},
methods: {
showTitle(item) {
return showTitle(item, this);
},
showChildren(item) {
return item.children && item.children.length;
},
getNameOrHref(item, children0) {
return item.href ? `isTurnByHref_${item.href}` : children0 ? item.children[0].path : item.path;
},
},
};

View File

@ -1,31 +0,0 @@
<template>
<Submenu :name="parentItem.path">
<template slot="title">
<span>{{ parentItem.title }}</span>
</template>
<template v-for="item in children">
<template v-if="item.auth === undefined">
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu${item.path}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="item.path" :key="`menu${item.children[0].path}`"
><span class="title">{{ item.children[0].title }}</span></menu-item
>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu${item.path}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="item.path" :key="`menu${item.path}`"
><span class="title">{{ item.title }}</span></menu-item
>
</template>
</template>
</template>
</Submenu>
</template>
<script>
import mixin from './mixin';
import itemMixin from './item-mixin';
export default {
name: 'SideMenuItem',
mixins: [mixin, itemMixin],
};
</script>

View File

@ -1,57 +0,0 @@
@side-width: 80px;
@side-height: 50px;
@side-child-width: 160px;
@child-menu-height: 50px;
@side-dark-theme: #282c34;
@side-dark-theme-font-color: #fff;
.side-menu-wrapper {
user-select: none;
height: 100%;
::-webkit-scrollbar {
width: 0;
}
-ms-overflow-style: none;
.menu-collapsed {
height: 100%;
background-color: @side-dark-theme;
color: #fff !important;
overflow-y: scroll;
.ivu-dropdown {
width: 100%;
.ivu-dropdown-rel a {
width: 100%;
}
}
.ivu-tooltip {
width: 100%;
.ivu-tooltip-rel {
width: 100%;
}
.ivu-tooltip-popper .ivu-tooltip-content {
.ivu-tooltip-arrow {
border-right-color: #fff;
}
.ivu-tooltip-inner {
background: #fff;
color: #495060;
}
}
}
}
a.drop-menu-a {
display: flex;
align-items: center;
justify-content: center;
height: @side-height;
line-height: @side-height;
width: 100%;
text-align: center;
color: @side-dark-theme-font-color;
}
}
.menu-title {
// padding-left: 6px;
}

View File

@ -1,477 +0,0 @@
<template>
<div class="side-menu-wrapper">
<!-- <sider-trigger :collapsed="collapsed" @on-change="handleCollpasedChange"></sider-trigger> -->
<div class="side-menu-box" v-if="!menuCollapse">
<div class="parent-menu">
<slot></slot>
<Menu
ref="menu"
class="parent-menus"
:active-name="headerName"
:open-names="openedNames"
:accordion="accordion"
:theme="theme"
@on-open-change="openNameData"
@on-select="handleSelect"
>
<template v-for="item in header">
<template>
<menu-item :name="item.path" :key="`menu${item.path}`"
><common-icon :type="item.icon || ''" /><span class="title">{{ item.title }}</span></menu-item
>
</template>
</template>
</Menu>
</div>
<div class="child-menu" v-if="sider.length">
<div class="cat-name">{{ oneMenuName }}</div>
<Menu
ref="childMenu"
:active-name="activePath"
:open-names="openNames"
:accordion="accordion"
:theme="theme"
class="child-menus"
@on-open-change="openChildNameData"
@on-select="handleChildSelect"
>
<template v-for="item in sider">
<template v-if="item.auth === undefined">
<template v-if="item.children && item.children.length >= 1">
<side-menu-item
v-if="showChildren(item)"
:key="`menu${item.path}`"
:parent-item="item"
></side-menu-item>
<menu-item v-else :name="item.path" :key="`menu${item.path}`"
><span class="title">{{ item.children[0].title }}</span></menu-item
>
</template>
<template v-else>
<side-menu-item
v-if="showChildren(item)"
:key="`menu${item.path}`"
:parent-item="item"
></side-menu-item>
<menu-item v-else :name="item.path" :key="`menu${item.path}`"
><span class="title">{{ item.title }}</span></menu-item
>
</template>
</template>
</template>
</Menu>
</div>
</div>
<div class="menu-collapsed" v-if="menuCollapse">
<slot></slot>
<template v-for="item in menusName">
<collapsed-menu
@on-click="collHandleSelect"
:hide-title="true"
:activeMenuPath="headerName"
:root-icon-size="rootIconSize"
:icon-size="iconSize"
:theme="theme"
:parent-item="item"
:key="`drop-menu-${item.path}`"
></collapsed-menu>
<!-- <Tooltip transfer v-else :content="item.title" placement="right" :key="`drop-menu-${item.path}`">
<a
@click="collHandleSelect(item)"
class="drop-menu-a"
:class="{ on: item.path == headerName }"
:style="{ textAlign: 'center' }"
><common-icon :type="item.icon || (item.children && item.children[0].icon)" />
<span class="title">{{ item.title }}</span>
</a>
</Tooltip> -->
</template>
</div>
</div>
</template>
<script>
import SideMenuItem from './side-menu-item.vue';
import CollapsedMenu from './collapsed-menu.vue';
import { getUnion } from '@/libs/tools';
import { mapState, mapGetters } from 'vuex';
import mixin from './mixin';
import itemMixin from './item-mixin';
import { setCookies } from '@/libs/util';
import siderTrigger from '../header-bar/sider-trigger';
export default {
name: 'SideMenu',
mixins: [mixin, itemMixin],
components: {
SideMenuItem,
CollapsedMenu,
siderTrigger,
},
props: {
// collapsed: {
// type: Boolean,
// },
theme: {
type: String,
default: 'light',
},
rootIconSize: {
type: Number,
default: 20,
},
iconSize: {
type: Number,
default: 16,
},
accordion: Boolean,
// openNames: {
// type: Array,
// default: () => [],
// },
},
data() {
return {
openedNames: [],
childList: [],
activeChildName: '',
childOptions: [],
// activePath: '',
activeMenuPath: '',
catName: '',
};
},
computed: {
...mapState('menus', ['menusName']),
...mapState('menu', ['activePath', 'openNames', 'header', 'headerName', 'sider', 'oneMenuName']),
...mapGetters('menu', ['filterSider']),
...mapState('layout', ['menuCollapse']),
textColor() {
return this.theme === 'dark' ? '#fff' : '#495060';
},
},
watch: {
// openedNames() {
// this.$nextTick(() => {
// this.$refs.menu.updateOpened();
// this.$refs.childMenu.updateActiveName();
// });
// },
activePath() {
this.$nextTick(() => {
this.$refs.childMenu.updateOpened();
this.$refs.childMenu.updateActiveName();
});
},
menuCollapse(val) {
if (!val) {
this.$nextTick(() => {
this.$refs.menu.updateOpened();
this.$refs.childMenu.updateActiveName();
this.$refs.childMenu.updateOpened();
});
}
},
},
mounted() {},
methods: {
handleCollpasedChange(state) {
this.$emit('on-coll-change', state);
},
handleSelect(name, type) {
this.menusName.map((e) => {
if (e.path === name) {
this.turnToPage(this.getChilden(e));
}
});
},
// 递归处理顶部菜单问题
getChilden(data) {
if (data.children) {
return this.getChilden(data.children[0]);
}
return data.path;
},
handleChildSelect(name) {
this.turnToPage(name);
},
collHandleSelect(name) {
this.turnToPage(name);
},
turnToPage(route, all) {
let { path, name, params, query } = {};
if (typeof route === 'string' && !all) path = route;
else if (typeof route === 'string' && all) name = route;
else {
path = route.path;
name = route.name;
params = route.params;
query = route.query;
}
this.$router.push({
path,
name,
params,
query,
});
},
openNameData(n) {},
openChildNameData(e) {},
},
};
</script>
<style lang="less" scoped>
/deep/ .ivu-menu-vertical .ivu-menu-submenu-title-icon {
right: 8px;
}
</style>
<style lang="less">
@import './side-menu.less';
.ivu-layout-sider {
overflow: unset !important;
transition: all 0.3s ease !important;
}
.ivu-menu {
.side-menu-wrapper {
position: relative;
}
.side-menu-wrapper a.drop-menu-a {
padding: 1px !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer {
background: rgb(0, 21, 41) !important;
width: 170px !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .ivu-select-dropdown {
background: rgb(0, 21, 41) !important;
width: 170px !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .ivu-dropdown-menu {
min-width: unset !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .ivu-dropdown-menu .ivu-dropdown-item {
padding: 9px 0 9px 30px !important;
font-size: 13px !important;
text-align: left;
}
.ivu-select-dropdown.ivu-dropdown-transfer .ivu-dropdown-menu .ivu-dropdown-item:hover {
background-color: #2d8cf0 !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .menu-title {
padding-left: 0 !important;
color: rgba(225, 225, 225, 0.7) !important;
font-size: 13px !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .ivu-dropdown-menu .ivu-dropdown-item:hover .menu-title {
color: #fff !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .collased-menu-dropdown {
padding: 9px 0 9px 30px !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer .collased-menu-dropdown:hover {
background-color: #2d8cf0 !important;
color: #fff !important;
}
.ivu-select-dropdown.ivu-dropdown-transfer
.collased-menu-dropdown:hover
> .ivu-dropdown-rel
> .drop-menu-a
> .menu-title {
color: #fff !important;
font-size: 14px;
line-height: 14px;
}
.ivu-select-dropdown.ivu-dropdown-transfer
.collased-menu-dropdown:hover
> .ivu-dropdown-rel
> .drop-menu-a
> .ivu-icon {
color: #fff !important;
}
}
.side-menu-box {
display: flex;
.drop-menu-a {
font-size: 14px;
line-height: 14px;
display: flex;
align-items: center;
.ivu-icon {
font-size: 14px;
margin-right: 8px;
}
}
.parent-menu {
z-index: 9;
width: @side-width;
background-color: @side-dark-theme !important;
height: 100vh;
.parent-menus {
width: @side-width !important;
}
.ivu-menu {
color: #fff !important;
}
.ivu-menu-light {
background-color: unset;
}
.ivu-menu-item-selected {
background-color: #1890ff !important;
color: #fff !important;
}
.ivu-menu-vertical .ivu-menu-item:hover {
color: #1890ff !important;
background-color: rgba(24, 144, 255, 0.1) !important;
.ivu-icon {
color: #1890ff !important;
}
}
.ivu-menu-item.ivu-menu-item-active:hover {
background-color: #1890ff !important;
color: #fff !important;
.ivu-icon {
color: #fff !important;
}
}
.ivu-menu-vertical .ivu-menu-item {
padding: 18px 10px;
display: flex;
align-items: center;
justify-content: center;
.title {
font-size: 14px;
line-height: 14px;
}
}
.ivu-menu-vertical .ivu-menu-item:first-child {
// margin-top: 8px;
}
}
.child-menu {
z-index: 9;
box-shadow: 2px 0px 4px 0px rgba(0, 0, 0, 0.06);
transition: width 0.2s ease 0.2s;
overflow: hidden;
.child-menus {
width: @side-child-width !important;
}
.ivu-divider {
height: 50px;
line-height: 50px;
margin: 0;
width: @side-child-width !important;
}
.ivu-menu-vertical .ivu-menu-item {
height: @child-menu-height;
display: flex;
align-items: center;
.title {
font-size: 14px;
line-height: 14px;
}
}
.ivu-menu-submenu {
.ivu-menu-item {
padding-left: 38px !important;
}
.ivu-menu-submenu {
.ivu-menu-submenu-title {
padding-left: 38px !important;
}
.ivu-menu-item {
padding-left: 52px !important;
}
}
}
.ivu-menu-vertical .ivu-menu-submenu-title {
font-size: 14px;
padding: 16px 24px;
line-height: 16px;
}
.cat-name {
width: 100%;
font-size: 16px;
height: 50px;
line-height: 20px;
font-weight: 600;
color: #606266;
padding: 15px 23px;
text-align: left;
white-space: nowrap;
border-bottom: 1px solid #f2f2f2;
}
}
> .ivu-menu {
padding: 8px;
}
.ivu-menu-vertical.ivu-menu-light:after {
width: 0px !important;
}
.ivu-menu-light.ivu-menu-vertical .ivu-menu-item-active:not(.ivu-menu-submenu):after {
width: 0px;
}
}
/deep/ .ivu-select-dropdown {
left: 95px !important;
}
.menu-collapsed {
width: @side-width;
.side-menu-wrapper a.drop-menu-a {
display: flex;
align-items: center;
justify-content: center;
color: @side-dark-theme-font-color;
width: @side-width !important;
}
.drop-menu-a:hover {
color: #1890ff !important;
background-color: rgba(24, 144, 255, 0.1) !important;
.ivu-icon {
color: #1890ff !important;
}
}
.drop-menu-a.on:hover {
background-color: #1890ff !important;
color: #fff !important;
.ivu-icon {
color: #fff !important;
}
}
.drop-menu-a.on {
background-color: #1890ff !important;
color: #fff !important;
.ivu-icon {
color: #fff !important;
}
}
.drop-menu-a {
font-size: 14px;
line-height: 14px;
display: flex;
padding: 18px 0;
align-items: center;
width: @side-width;
flex-wrap: nowrap;
.ivu-icon {
font-size: 14px;
margin-right: 6px;
}
.title {
font-size: 14px;
line-height: 14px;
white-space: nowrap;
}
}
.drop-menu-a {
padding: 0;
}
.collased-menu-dropdown * {
font-size: 14px;
}
}
</style>

View File

@ -1,2 +0,0 @@
import TagsNav from './tags-nav.vue';
export default TagsNav;

View File

@ -1,104 +0,0 @@
.no-select {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.size {
width: 100%;
height: 100%;
}
.tag-nav-wrapper {
background: unset;
width: 100%;
margin: 0px;
//overflow: hidden;
padding: 0;
height: 42px;
background-color: #fff;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.06);
z-index: 8;
}
.tags-nav {
position: relative;
//margin-top: 3px;
// border-top: 1px solid #f0f0f0;
// border-bottom: 1px solid #f0f0f0;
.no-select;
.size;
.close-con {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 40px;
background: #fff;
text-align: center;
z-index: 10;
}
.btn-con {
position: absolute;
top: 0px;
height: 100%;
padding-top: 5px;
z-index: 10;
background-color: #fff;
button {
padding: 6px;
line-height: 14px;
text-align: center;
}
&.left-btn {
border-right: 1px solid #f0f0f0;
}
&.right-btn {
right: 40px;
border-right: 1px solid #f0f0f0;
border-left: 1px solid #f0f0f0;
}
}
.scroll-outer {
position: absolute;
left: 20px;
right: 21px;
top: 0;
bottom: 0;
// box-shadow: 0px 0 3px 2px rgba(100, 100, 100, 0.1) inset;
.scroll-body {
height: ~'calc(100% - 1px)';
display: inline-block;
padding: 3px 40px 0 20px;
position: absolute;
overflow: visible;
white-space: nowrap;
transition: left 0.3s ease;
.ivu-tag-dot-inner {
transition: background 0.2s ease;
}
.ivu-tag-dot {
border: none !important;
border-right: 1px solid #f2f2f2 !important;
}
}
}
.contextmenu {
position: absolute;
margin: 0;
padding: 5px 0px;
background: #fff;
z-index: 1000;
list-style-type: none;
border-radius: 4px;
box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.2);
li {
margin: 0;
padding: 5px 15px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
}

View File

@ -1,219 +0,0 @@
<template>
<div class="tags-nav">
<div class="close-con">
<Dropdown transfer @on-click="handleTagsOption" style="margin-top: 10px">
<Button size="small" type="text">
<Icon :size="18" type="ios-close-circle-outline" />
</Button>
<DropdownMenu slot="list" class="close">
<DropdownItem name="close-all">关闭所有</DropdownItem>
<DropdownItem name="close-others">关闭其他</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
<ul v-show="visible" :style="{ left: contextMenuLeft + 'px', top: contextMenuTop + 'px' }" class="contextmenu">
<li v-for="(item, key) of menuList" @click="handleTagsOption(key)" :key="key">{{ item }}</li>
</ul>
<div class="btn-con left-btn">
<Button type="text" @click="handleScroll(240)">
<Icon :size="18" type="ios-arrow-back" />
</Button>
</div>
<div class="btn-con right-btn">
<Button type="text" @click="handleScroll(-240)">
<Icon :size="18" type="ios-arrow-forward" />
</Button>
</div>
<div class="scroll-outer" ref="scrollOuter" @DOMMouseScroll="handlescroll" @mousewheel="handlescroll">
<div ref="scrollBody" class="scroll-body" :style="{ left: tagBodyLeft + 'px' }">
<transition-group name="taglist-moving-animation">
<Tag
type="dot"
v-for="(item, index) in list"
ref="tagsPageOpened"
:key="`tag-nav-${index}`"
:name="item.name"
:data-route-item="item"
@on-close="handleClose(item)"
@click.native="handleClick(item)"
:closable="item.name !== $config.homeName"
:color="isCurrentTag(item) ? 'primary' : 'default'"
@contextmenu.prevent.native="contextMenu(item, $event)"
>{{ showTitleInside(item) }}</Tag
>
<!-- <div class="tag" v-for="(item, index) in list" :key="`tag-nav-${index}`">{{ showTitleInside(item) }}</div> -->
</transition-group>
</div>
</div>
</div>
</template>
<script>
import { showTitle, routeEqual } from '@/libs/util';
import beforeClose from '@/router/before-close';
export default {
name: 'TagsNav',
props: {
value: Object,
list: {
type: Array,
default() {
return [];
},
},
},
data() {
return {
tagBodyLeft: 0,
rightOffset: 40,
outerPadding: 4,
contextMenuLeft: 0,
contextMenuTop: 0,
visible: false,
menuList: {
others: '关闭其他',
all: '关闭所有',
},
};
},
computed: {
currentRouteObj() {
const { name, params, query } = this.value;
return { name, params, query };
},
},
methods: {
handlescroll(e) {
var type = e.type;
let delta = 0;
if (type === 'DOMMouseScroll' || type === 'mousewheel') {
delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0) * 40;
}
this.handleScroll(delta);
},
handleScroll(offset) {
const outerWidth = this.$refs.scrollOuter.offsetWidth;
const bodyWidth = this.$refs.scrollBody.offsetWidth;
if (offset > 0) {
this.tagBodyLeft = Math.min(0, this.tagBodyLeft + offset);
} else {
if (outerWidth < bodyWidth) {
if (this.tagBodyLeft < -(bodyWidth - outerWidth)) {
this.tagBodyLeft = this.tagBodyLeft;
} else {
this.tagBodyLeft = Math.max(this.tagBodyLeft + offset, outerWidth - bodyWidth);
}
} else {
this.tagBodyLeft = 0;
}
}
},
handleTagsOption(type) {
if (type.includes('all')) {
// 关闭所有,除了home
let res = this.list.filter((item) => item.name === this.$config.homeName);
this.$emit('on-close', res, 'all');
} else if (type.includes('others')) {
// 关闭除当前页和home页的其他页
let res = this.list.filter(
(item) => routeEqual(this.currentRouteObj, item) || item.name === this.$config.homeName,
);
this.$emit('on-close', res, 'others', this.currentRouteObj);
setTimeout(() => {
this.getTagElementByRoute(this.currentRouteObj);
}, 100);
}
},
handleClose(current) {
if (current.meta && current.meta.beforeCloseName && current.meta.beforeCloseName in beforeClose) {
new Promise(beforeClose[current.meta.beforeCloseName]).then((close) => {
if (close) {
this.close(current);
}
});
} else {
this.close(current);
}
},
close(route) {
let res = this.list.filter((item) => !routeEqual(route, item));
this.$emit('on-close', res, undefined, route);
},
handleClick(item) {
this.$emit('input', item);
},
showTitleInside(item) {
return showTitle(item, this);
},
isCurrentTag(item) {
return routeEqual(this.currentRouteObj, item);
},
moveToView(tag) {
const outerWidth = this.$refs.scrollOuter.offsetWidth;
const bodyWidth = this.$refs.scrollBody.offsetWidth;
if (bodyWidth < outerWidth) {
this.tagBodyLeft = 0;
} else if (tag.offsetLeft < -this.tagBodyLeft) {
// 标签在可视区域左侧
this.tagBodyLeft = -tag.offsetLeft + this.outerPadding;
} else if (
tag.offsetLeft > -this.tagBodyLeft &&
tag.offsetLeft + tag.offsetWidth < -this.tagBodyLeft + outerWidth
) {
// 标签在可视区域
this.tagBodyLeft = Math.min(0, outerWidth - tag.offsetWidth - tag.offsetLeft - this.outerPadding);
} else {
// 标签在可视区域右侧
this.tagBodyLeft = -(tag.offsetLeft - (outerWidth - this.outerPadding - tag.offsetWidth));
}
},
getTagElementByRoute(route) {
this.$nextTick(() => {
this.refsTag = this.$refs.tagsPageOpened;
this.refsTag.forEach((item, index) => {
if (routeEqual(route, item.$attrs['data-route-item'])) {
let tag = this.refsTag[index].$el;
this.moveToView(tag);
}
});
});
},
contextMenu(item, e) {
if (item.name === this.$config.homeName) {
return;
}
this.visible = true;
const offsetLeft = this.$el.getBoundingClientRect().left;
this.contextMenuLeft = e.clientX - offsetLeft;
this.contextMenuTop = e.clientY - 34;
},
closeMenu() {
this.visible = false;
},
},
watch: {
$route(to) {
this.getTagElementByRoute(to);
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu);
} else {
document.body.removeEventListener('click', this.closeMenu);
}
},
},
mounted() {
setTimeout(() => {
this.getTagElementByRoute(this.$route);
}, 200);
},
};
</script>
<style lang="less">
.close .ivu-dropdown-item {
// color: rgba(255, 255, 255, 0.7) !important;
}
@import './tags-nav.less';
</style>

View File

@ -1,2 +0,0 @@
import User from './user.vue';
export default User;

View File

@ -1,12 +0,0 @@
.user {
&-avatar-dropdown {
cursor: pointer;
display: inline-block;
// height: 64px;
vertical-align: middle;
// line-height: 64px;
.ivu-badge-dot {
top: 16px;
}
}
}

View File

@ -1,109 +0,0 @@
<template>
<div class="user-avatar-dropdown">
<Dropdown @on-click="handleClick" :transfer="true">
<!--<Badge :dot="!!messageUnreadCount">-->
<!--<Avatar :src="avatars"/>-->
<!--</Badge>-->
<!--<Icon :size="18" type="md-arrow-dropdown"></Icon>-->
<span class="account">{{ info.account }}</span>
<DropdownMenu slot="list">
<!--<DropdownItem name="message">-->
<!--消息中心<Badge style="margin-left: 10px" :count="messageUnreadCount"></Badge>-->
<!--</DropdownItem>-->
<DropdownItem name="userCenter"><Icon type="ios-contact-outline" class="iconImg" />个人中心</DropdownItem>
<!-- <DropdownItem v-show = "info.level === 0" name="fileEdit"><Icon type="ios-document-outline" class="iconImg" />文件管理</DropdownItem> -->
<DropdownItem name="logout"><Icon type="ios-log-out" class="iconImg" />退出登录</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</template>
<style scoped lang="less">
.iconImg {
font-size: 18px;
margin-right: 6px;
margin-bottom: 2px;
}
.account {
color: rgba(0, 0, 0, 0.65);
}
.user-avatar-dropdown .ivu-dropdown-item ~ .ivu-dropdown-item {
border-top: 1px solid #e8eaec;
}
</style>
<script>
import './user.less';
import { AccountLogout } from '@/api/account';
import { removeCookies } from '@/libs/util';
import { Modal } from 'iview';
export default {
name: 'User',
data() {
return {
info: '',
};
},
props: {
userAvatar: {
type: String,
default: '',
},
messageUnreadCount: {
type: Number,
default: 0,
},
},
mounted() {
this.info = this.$store.state.userInfo.userInfo;
},
methods: {
logout() {
let that = this;
this.$Modal.confirm({
title: '退出登录确认',
content: '您确定退出当前账户吗?',
onOk() {
AccountLogout()
.then((res) => {
that.$Message.success('您已成功退出');
that.$router.replace({ name: 'login' });
localStorage.clear();
removeCookies('token');
removeCookies('expires_time');
removeCookies('uuid');
sessionStorage.clear();
// window.location.reload()
})
.catch((res) => {});
},
});
},
userCenter() {
this.$router.push({ name: 'systemUser' });
},
fileEdit() {
this.$router.push({ name: 'systemFiles' });
},
message() {
this.$router.push({
name: 'message_page',
});
},
handleClick(name) {
switch (name) {
case 'logout':
this.logout();
break;
case 'userCenter':
this.userCenter();
break;
case 'message':
this.message();
break;
case 'fileEdit':
this.fileEdit();
break;
}
},
},
};
</script>

View File

@ -1,2 +0,0 @@
import Main from './main.vue';
export default Main;

View File

@ -1,81 +0,0 @@
@bar-height: 50px;
.main {
.logo-con {
height: @bar-height;
img {
height: 44px;
width: auto;
display: block;
margin: 0 auto;
}
}
.header-con {
background: #fff;
padding: 0 20px;
width: 100%;
border-bottom: 1px solid #f2f2f2;
}
.ivu-layout-header {
height: @bar-height;
line-height: @bar-height;
}
.main-layout-con {
height: calc(~'100vh - 60px');
overflow: hidden;
}
.main-content-con {
height: ~'calc(100vh - 60px)';
overflow: hidden;
}
.content-wrapper {
padding: 0 16px;
height: ~'calc(100% - 80px)';
overflow: auto;
}
.left-sider {
.ivu-layout-sider-children {
overflow-y: scroll;
//margin-right: -16px;
overflow-x: hidden;
}
}
}
.ivu-menu-item > i {
margin-right: 6px !important;
}
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
margin-right: 6px !important;
}
.collased-menu-dropdown {
width: 100%;
margin: 0;
line-height: normal;
// padding: 7px 0 6px 16px;
clear: both;
font-size: 12px !important;
white-space: nowrap;
list-style: none;
cursor: pointer;
transition: background 0.2s ease-in-out;
&:hover {
background: rgba(100, 100, 100, 0.1);
}
& * {
color: #515a6e;
}
.ivu-menu-item > i {
margin-right: 12px !important;
}
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
margin-right: 8px !important;
}
}
.ivu-select-dropdown.ivu-dropdown-transfer {
max-height: 800px;
}
.dro-style {
// padding: 0 !important;
}

View File

@ -1,319 +0,0 @@
<template>
<Layout style="height: 100%" class="main">
<Sider
hide-trigger
collapsible
:width="sider.length ? 240 : 80"
:collapsed-width="isMobile ? 0 : 80"
v-model="menuCollapse"
:style="{ overflow: 'hidden' }"
v-if="!headMenuNoShow"
>
<side-menu
accordion
ref="sideMenu"
@on-coll-change="handleCollapsedChange"
:active-name="$route.path"
@on-select="turnToPage"
>
<!-- 需要放在菜单上面的内容,如Logo,写在side-menu标签内部,如下 -->
<div class="logo-con">
<img :src="minLogo" key="min-logo" />
</div>
</side-menu>
</Sider>
<Layout>
<Header class="header-con" v-if="!headMenuNoShow">
<header-bar @on-coll-change="handleCollapsedChange">
<user :message-unread-count="unreadCount" :user-avatar="userAvatar" />
<language v-if="$config.useI18n" @on-lang-change="setLocal" style="margin-right: 10px" :lang="local" />
<header-notice></header-notice>
<Reload @on-reload="handleReload"></Reload>
<fullscreen v-model="isFullscreen" style="margin-right: 10px" />
<error-store
v-if="$config.plugin['error-store'] && $config.plugin['error-store'].showInHeader"
:has-read="hasReadErrorPage"
:count="errorCount"
></error-store>
<header-search></header-search>
</header-bar>
</Header>
<Content class="main-content-con" :class="{ 'all-desk': headMenuNoShow }">
<Layout class="main-layout-con">
<div class="tag-nav-wrapper" v-if="!headMenuNoShow">
<tags-nav :value="$route" @input="handleClick" :list="tagNavList" @on-close="handleCloseTag" />
</div>
<Content class="content-wrapper">
<!-- <keep-alive :include="cacheList">
<router-view v-if="reload" style="min-height: 600px" />
</keep-alive> -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive && reload" class="main-warper"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive && reload" class="main-warper"></router-view>
<!-- <router-view v-if="reload" style="min-height: 600px" /> -->
<ABackTop
v-if="!headMenuNoShow"
:height="100"
:bottom="80"
:right="50"
container=".content-wrapper"
></ABackTop>
<i-copyright v-if="!headMenuNoShow" />
</Content>
</Layout>
</Content>
</Layout>
<!-- <div class="open-image" @click="clear" v-if="openImage">
<img src="@/assets/images/wechat_demo.png" alt="" />
</div> -->
</Layout>
</template>
<script>
import iCopyright from '@/components/copyright';
import SideMenu from './components/side-menu';
import HeaderBar from './components/header-bar';
import TagsNav from './components/tags-nav';
import User from './components/user';
import ABackTop from './components/a-back-top';
import Fullscreen from './components/fullscreen';
import Language from './components/language';
import ErrorStore from './components/error-store';
import HeaderSearch from './components/header-search';
import HeaderNotice from './components/header-notice';
import Reload from './components/reload';
import Setting from '@/setting';
import iView from 'iview';
import { mapMutations, mapActions, mapGetters, mapState } from 'vuex';
import { getNewTagList, routeEqual, getMenuopen, getCookies, setCookies } from '@/libs/util';
import { getLogo } from '@/api/common';
import routers from '@/router/routers';
import minLogo from '@/assets/images/logo-small.png';
import maxLogo from '@/assets/images/logo.png';
import './main.less';
export default {
name: 'Main',
components: {
SideMenu,
HeaderBar,
Language,
TagsNav,
Fullscreen,
ErrorStore,
User,
ABackTop,
iCopyright,
HeaderSearch,
HeaderNotice,
Reload,
},
data() {
return {
// collapsed: JSON.parse(getCookies('collapsed') || 'false'),
minLogo,
maxLogo,
isFullscreen: false,
reload: true,
screenWidth: '',
openImage: true,
headMenuNoShow: false,
};
},
watch: {
sider(val) {},
},
computed: {
...mapGetters(['errorCount']),
...mapState('menu', ['sider']),
...mapState('media', ['isMobile']),
...mapState('layout', ['menuCollapse']),
tagNavList() {
return this.$store.state.app.tagNavList;
},
tagRouter() {
return this.$store.state.app.tagRouter;
},
userAvatar() {
return this.$store.state.user.avatarImgPath;
},
cacheList() {
const list = [
'ParentView',
...(this.tagNavList.length
? this.tagNavList.filter((item) => !(item.meta && item.meta.notCache)).map((item) => item.name)
: []),
];
return list;
},
local() {
return this.$store.state.app.local;
},
hasReadErrorPage() {
return this.$store.state.app.hasReadErrorPage;
},
unreadCount() {
return this.$store.state.user.unreadCount;
},
},
methods: {
...mapMutations(['setBreadCrumb', 'setTagNavList', 'addTag', 'setLocal', 'setHomeRoute', 'closeTag']),
...mapActions(['handleLogin', 'getUnreadMessageCount']),
turnToPage(route, all) {
let { path, name, params, query } = {};
if (typeof route === 'string' && !all) path = route;
else if (typeof route === 'string' && all) name = route;
else {
path = route.path;
name = route.name;
params = route.params;
query = route.query;
}
this.$router.push({
path,
name,
params,
query,
});
},
handleCollapsedChange(state) {
// this.collapsed = state;
// setCookies('collapsed', state);
},
handleCloseTag(res, type, route) {
if (type !== 'others') {
if (type === 'all') {
this.turnToPage(this.$config.homeName, 'all');
} else {
if (routeEqual(this.$route, route)) {
this.closeTag(route);
}
}
}
if (res.length === 1 && res[0].name === this.$config.homeName) {
this.$router.push({ name: this.$config.homeName });
}
this.setTagNavList(res);
},
handleClick(item) {
this.turnToPage(item);
},
getLogo() {
let logo = this.$store.state.userInfo.logo;
let logoSmall = this.$store.state.userInfo.logoSmall;
this.maxLogo = logo || this.maxLogo;
this.minLogo = logoSmall || this.minLogo;
getLogo().then((res) => {
localStorage.setItem('ADMIN_TITLE', res.data.site_name);
this.minLogo = res.data.logo_square;
this.maxLogo = res.data.logo;
});
},
handleReload() {
this.reload = false;
// if (Setting.showProgressBar) iView.LoadingBar.start()
this.$nextTick(() => {
this.reload = true;
// if (Setting.showProgressBar) iView.LoadingBar.finish()
});
},
clear() {
this.openImage = false;
},
},
watch: {
$route(newRoute) {
this.headMenuNoShow = this.$route.meta.fullScreen;
const { name, query, params, meta } = newRoute;
this.addTag({
route: { name, query, params, meta },
type: 'push',
});
this.setBreadCrumb(newRoute);
this.setTagNavList(getNewTagList(this.tagNavList, newRoute));
this.$refs.sideMenu.updateOpenName(newRoute.path);
},
},
mounted() {
this.headMenuNoShow = this.$route.meta.fullScreen;
this.getLogo();
this.screenWidth = document.body.clientWidth;
window.onresize = () => {
return (() => {
this.screenWidth = document.body.clientWidth;
if (this.screenWidth <= 1060) {
// this.collapsed = true;
// setCookies('collapsed', true);
this.$store.commit('layout/changeCol', true);
} else {
// this.collapsed = false;
// setCookies('collapsed', false);
this.$store.commit('layout/changeCol', false);
}
})();
};
/**
* @description 初始化设置面包屑导航和标签导航
*/
this.setTagNavList();
this.setHomeRoute(routers);
const { name, params, query, meta } = this.$route;
this.addTag({
route: { name, params, query, meta },
});
this.setBreadCrumb(this.$route);
// 设置初始语言
this.setLocal(this.$i18n.locale);
// 如果当前打开页面不在标签栏中,跳到homeName页
if (!this.tagNavList.find((item) => item.name === this.$route.name)) {
this.$router.push({
name: this.$config.homeName,
});
}
// 获取未读消息条数
this.getUnreadMessageCount();
},
};
</script>
<style lang="less">
.main .header-con {
padding: 0 0px 0 0px;
display: flex;
background: #fff;
box-shadow: 1px 1px 4px rgba(49, 103, 154, 0.08);
}
.main .logo-con img {
height: 50px;
transition: all 1s;
}
.open-image {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
background-color: rgba(0, 0, 0, 0.6);
height: 100%;
width: 100%;
top: 0;
left: 0;
z-index: 1000;
img {
width: 800px;
}
}
.main-warper {
min-height: calc(~'100vh - 196px');
}
.all-desk {
height: 100vh !important;
padding: 0 !important;
.main-content-con,
.main-warper,
.main-layout-con,
.content-wrapper {
height: 100vh !important;
}
}
</style>

View File

@ -1,18 +1,18 @@
// 定义内容
export default {
login: {
placeholder1: 'The user name admin or not is test',
placeholder2: 'Password: 123456',
placeholder3: 'Please enter the verification code',
placeholder1: '',
placeholder2: '',
placeholder3: '',
btnText: 'Sign in',
link: {
one1: 'Third party login',
one2: 'Links',
one1: '',
one2: '',
},
signInText: 'welcome back!',
copyright: {
one5: 'Copyright: Shenzhen XXX Software Technology Co., Ltd',
two6: 'Copyright: Shenzhen XXX software technology Guangdong ICP preparation no.05010000',
one5: '',
two6: '',
},
},
};

View File

@ -7,15 +7,15 @@
// 定义内容
export default {
login: {
placeholder1: '用户名 admin 或不输均为 test',
placeholder2: '密码:123456',
placeholder3: '请输入验证码',
placeholder1: '',
placeholder2: '',
placeholder3: '',
btnText: '登 录',
link: {
one1: '第三方登录',
one2: '友情链接',
one1: '',
one2: '',
},
signInText: '欢迎回来!',
signInText: '',
copyright: {
one5: '',
two6: '',

View File

@ -1,18 +1,18 @@
// 定义内容
export default {
login: {
placeholder1: '用戶名admin或不輸均為test',
placeholder2: '密碼:123456',
placeholder3: '請輸入驗證碼',
placeholder1: '',
placeholder2: '',
placeholder3: '',
btnText: '登 录',
link: {
one1: '協力廠商登入',
one2: '友情連結',
one1: '',
one2: '',
},
signInText: '歡迎回來!',
signInText: '',
copyright: {
one5: '版權所有:深圳市xxx軟件科技有限公司',
two6: 'Copyright: Shenzhen XXX Software Technology 粵ICP備05010000號',
one5: '',
two6: '',
},
},
};

View File

@ -1,9 +1,3 @@
<!--
* @Author: From-wh from-wh@hotmail.com
* @Date: 2023-03-09 15:45:51
* @FilePath: /admin/src/layout/footer/index.vue
* @Description:
-->
<template>
<div class="layout-footer mt15">
<div class="layout-footer-warp">

View File

@ -10,7 +10,6 @@
import { Local } from '@/utils/storage.js';
import { mapMutations } from 'vuex';
import { getNewTagList } from '@/libs/util';
import Mains from '@/layout/component/main.vue';
export default {
name: 'layout',

View File

@ -275,7 +275,7 @@
</el-radio-group>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15" v-if="getThemeConfig.layout === 'columns'">
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fiveColumnsAsideStyle') }}</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-radio-group v-model="getThemeConfig.columnsAsideStyle" size="mini" @change="setLocalThemeConfig">

View File

@ -1,28 +1,5 @@
<template>
<div class="layout-navbars-breadcrumb-user" :style="{ flex: layoutUserFlexNum }">
<!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
<div class="layout-navbars-breadcrumb-user-icon">
<i class="el-icon-magic-stick" :title="$t('message.user.title0')"></i>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="" :disabled="disabledSize === ''">{{ $t('message.user.dropdownDefault') }}</el-dropdown-item>
<el-dropdown-item command="medium" :disabled="disabledSize === 'medium'">{{ $t('message.user.dropdownMedium') }}</el-dropdown-item>
<el-dropdown-item command="small" :disabled="disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
<el-dropdown-item command="mini" :disabled="disabledSize === 'mini'">{{ $t('message.user.dropdownMini') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown> -->
<!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
<div class="layout-navbars-breadcrumb-user-icon">
<i class="iconfont" :class="disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'" :title="$t('message.user.title1')"></i>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh-cn" :disabled="disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
<el-dropdown-item command="en" :disabled="disabledI18n === 'en'">English</el-dropdown-item>
<el-dropdown-item command="zh-tw" :disabled="disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> -->
<div class="layout-navbars-breadcrumb-user-icon" @click="refresh">
<i class="el-icon-refresh-right" :title="$t('message.user.title7')"></i>
</div>

View File

@ -97,7 +97,7 @@ export default {
created() {},
};
</script>
<style scoped>
<style lang="scss" scoped>
/deep/ .center {
text-align: center;
margin-right: 0 !important;

View File

@ -12,7 +12,7 @@
<div class="layout-link-msg">页面 "{{ $t(meta.title) }}" 已在新窗口中打开</div>
<el-button class="mt30" round size="small" @click="onGotoFullPage">
<i class="iconfont icon-lianjie"></i>
<span>立即前往体验</span>
<span>立即前往</span>
</el-button>
</div>
</div>

View File

@ -1,62 +0,0 @@
<template></template>
<script>
export default {
name: 'layoutSponsors',
data() {},
computed: {},
methods: {},
mounted() {
this.delayShow();
},
};
</script>
<style scoped lang="scss">
.sponsors-container {
position: fixed;
right: 15px;
bottom: 15px;
z-index: 3;
width: 200px;
background-color: var(--prev-bg-main-color);
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.12);
border-radius: 5px;
overflow: hidden;
cursor: pointer;
.sponsors-img {
width: 100%;
height: 80px;
}
.sponsors-text {
padding: 10px;
color: var(--prev-color-text-regular);
font-size: 14px;
}
.sponsors-close {
width: 60px;
height: 60px;
border-radius: 100%;
background: rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
position: absolute;
right: -35px;
bottom: -35px;
z-index: 5;
i {
position: absolute;
left: 9px;
top: 9px;
color: #afafaf;
transition: all 0.3s ease;
}
&:hover {
transition: all 0.3s ease;
i {
color: var(--prev-color-primary);
transition: all 0.3s ease;
}
}
}
}
</style>

View File

@ -1,5 +1,4 @@
/**
* 2020.05.28 by lyt 优化
* 修改一下配置时,需要每次都清理 `window.localStorage` 浏览器永久缓存,配置才会生效
*/
const themeConfigModule = {