Commit b61b627d authored by 周远喜's avatar 周远喜

iview界面整合。

parent 650f9a0e
<template>
<Breadcrumb class="i-layout-header-breadcrumb" v-if="!isLimit" ref="breadcrumb">
<BreadcrumbItem>
<i-menu-head-title :item="topItem" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
<BreadcrumbItem v-for="item in items" :key="item.path">
<i-menu-head-title :item="item" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
<BreadcrumbItem>
<i-menu-head-title :item="siderMenuObject[activePath]" :hide-icon="!showBreadcrumbIcon" />
</BreadcrumbItem>
</Breadcrumb>
</template>
<script>
import { mapState } from 'vuex';
import menuSider from '@/menu/sider';
import { flattenSiderMenu } from '@/libs/system';
import iMenuHeadTitle from '../menu-head/title';
import { on, off } from 'view-design/src/utils/dom';
import { findComponentUpward, getStyle } from 'view-design/src/utils/assist';
import { throttle } from 'lodash';
export default {
name: 'iHeaderBreadcrumb',
components: { iMenuHeadTitle },
computed: {
...mapState('admin/layout', [
'showBreadcrumbIcon',
'menuCollapse'
]),
...mapState('admin/menu', [
'openNames',
'activePath',
'header',
'headerName'
]),
siderMenuObject () {
let obj = {};
this.allSiderMenu.forEach(item => {
if ('path' in item) {
obj[item.path] = item;
}
});
return obj;
},
items () {
let items = [...this.openNames];
let newItems = [];
items.forEach(i => {
newItems.push(this.siderMenuObject[i]);
});
return newItems;
},
// 第一级,默认是 menu/header.js 中的第一项
topItem () {
return this.header.find(item => item.name === this.headerName);
}
},
data () {
return {
// 得到所有侧边菜单,并转为平级,查询图标及显示对应内容
allSiderMenu: flattenSiderMenu(menuSider, []),
handleResize: () => {},
isLimit: false,
maxWidth: 560,
breadcrumbWidth: 0
}
},
methods: {
handleCheckWidth () {
const $header = findComponentUpward(this, 'Header');
if ($header) {
const headerWidth = parseInt(getStyle($header.$el, 'width'));
this.$nextTick(() => {
this.isLimit = headerWidth - this.maxWidth <= this.breadcrumbWidth;
});
}
},
handleGetWidth () {
this.isLimit = false;
this.$nextTick(() => {
const $breadcrumb = this.$refs.breadcrumb;
if ($breadcrumb) {
this.breadcrumbWidth = parseInt(getStyle($breadcrumb.$el, 'width'));
}
});
}
},
watch: {
topItem: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
},
items: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
},
activePath: {
handler () {
this.handleGetWidth();
this.handleCheckWidth();
},
deep: true
}
},
mounted () {
this.handleResize = throttle(this.handleCheckWidth, 100, { leading: false });
on(window, 'resize', this.handleResize);
this.handleGetWidth();
this.handleCheckWidth();
},
beforeDestroy () {
off(window, 'resize', this.handleResize);
}
}
</script>
<template>
<span class="i-layout-header-trigger" :class="{ 'i-layout-header-trigger-min': showReload }" @click="handleToggleMenuSide">
<Icon custom="i-icon i-icon-menu-unfold" v-show="menuCollapse || isMobile" />
<Icon custom="i-icon i-icon-menu-fold" v-show="!menuCollapse && !isMobile" />
</span>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'iHeaderCollapse',
computed: {
...mapState('admin/layout', [
'isMobile',
'isTablet',
'isDesktop',
'menuCollapse',
'showReload'
])
},
methods: {
...mapMutations('admin/layout', [
'updateMenuCollapse'
]),
// 展开/收起侧边栏
handleToggleMenuSide (state) {
if (this.isMobile) {
this.updateMenuCollapse(false);
this.$emit('on-toggle-drawer', state);
} else {
this.updateMenuCollapse(!this.menuCollapse);
}
}
},
watch: {
// 切换页面时,在移动端自动收起侧边栏
// 强行传参 false 是因为有的路由不是在菜单栏发生的,toggle 会使其显示
'$route' () {
if (this.isMobile) this.handleToggleMenuSide(false);
},
// 在平板时自动收起菜单
isTablet (state) {
if (!this.isMobile && state) this.updateMenuCollapse(true);
},
// 在桌面时自动展开菜单
isDesktop (state) {
if (!this.isMobile && state) this.updateMenuCollapse(false);
}
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min" @click="toggleFullscreen">
<Icon custom="i-icon i-icon-full-screen" v-show="!isFullscreen" />
<Icon custom="i-icon i-icon-exit-full-screen" v-show="isFullscreen" />
</span>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderFullscreen',
computed: {
...mapState('admin/layout', [
'isFullscreen'
])
},
methods: {
...mapActions('admin/layout', [
'toggleFullscreen'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown :trigger="isMobile ? 'click' : 'hover'" class="i-layout-header-i18n" :class="{ 'i-layout-header-user-mobile': isMobile }" @on-click="handleClick">
<Icon type="md-globe" />
<DropdownMenu slot="list">
<DropdownItem v-for="(item, key) in languages" :key="key" :name="key" :selected="locale === key">
<span>{{ item.language }}</span>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import Languages from '@/i18n/locale';
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderI18n',
data () {
return {
languages: Languages
}
},
computed: {
...mapState('admin/i18n', [
'locale'
]),
...mapState('admin/layout', [
'isMobile'
])
},
methods: {
...mapActions('admin/i18n', [
'setLocale'
]),
handleClick (locale) {
if (locale === this.locale) return;
this.setLocale({ locale, vm: this });
}
}
}
</script>
<template>
<Tooltip :content="tooltipContent" transfer>
<span class="i-layout-header-trigger i-layout-header-trigger-min" @click="handleOpenLog">
<Badge :count="lengthError === 0 ? null : lengthError" :overflow-count="99" :dot="showDot" :offset="showDot ? [26, 2] : [20, 0]">
<Icon custom="i-icon i-icon-record" />
</Badge>
</span>
</Tooltip>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'iHeaderLog',
computed: {
...mapGetters('admin/log', [
'length',
'lengthError'
]),
showDot () {
return !!this.length && this.lengthError === 0;
},
tooltipContent () {
if (!this.length) {
return '没有日志或异常';
} else {
let text = `${this.length} 条日志`;
if (this.lengthError) text += ` | 包含 ${this.lengthError} 个异常`;
return text;
}
}
},
methods: {
handleOpenLog () {
this.$router.push({
name: 'log'
});
}
}
}
</script>
<template>
<i-link class="i-layout-header-logo" :class="{ 'i-layout-header-logo-stick': !isMobile }" to="/">
<img src="@/assets/images/logo-small.png" v-if="isMobile">
<img src="@/assets/images/logo.png" v-else-if="headerTheme === 'light'">
<img src="@/assets/images/logo-dark.png" v-else>
</i-link>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderLogo',
computed: {
...mapState('admin/layout', [
'isMobile',
'headerTheme'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in">
<Notification
:wide="isMobile"
:badge-props="badgeProps"
class="i-layout-header-notice"
:class="{ 'i-layout-header-notice-mobile': isMobile }">
<Icon slot="icon" custom="i-icon i-icon-notification" />
<NotificationTab title="通知">
</NotificationTab>
<NotificationTab title="消息">
</NotificationTab>
<NotificationTab title="待办">
</NotificationTab>
</Notification>
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderNotice',
data () {
return {
badgeProps: {
offset: [20, 0]
}
}
},
computed: {
...mapState('admin/layout', [
'isMobile'
])
}
}
</script>
<template>
<span class="i-layout-header-trigger" :class="{ 'i-layout-header-trigger-min': showSiderCollapse }" @click="handleReload">
<Icon custom="i-icon i-icon-refresh" />
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderReload',
computed: {
...mapState('admin/layout', [
'isMobile',
'showSiderCollapse'
])
},
methods: {
handleReload () {
this.$emit('on-reload');
}
}
}
</script>
<template>
<span v-if="isDesktop" class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in i-layout-header-trigger-nohover">
<input class="i-layout-header-search" type="text" :placeholder="$t('basicLayout.search.placeholder')">
</span>
<span v-else class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown trigger="click" class="i-layout-header-search-drop" ref="dropdown">
<Icon type="ios-search" />
<DropdownMenu slot="list">
<div class="i-layout-header-search-drop-main">
<Input size="large" prefix="ios-search" type="text" :placeholder="$t('basicLayout.search.placeholder')" />
<span class="i-layout-header-search-drop-main-cancel" @click="handleCloseSearch">{{ $t('basicLayout.search.cancel') }}</span>
</div>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'iHeaderSearch',
computed: {
...mapState('admin/layout', [
'isDesktop',
'headerMenu'
])
},
methods: {
handleCloseSearch () {
this.$refs.dropdown.handleClick();
}
}
}
</script>
This diff is collapsed.
<template>
<span class="i-layout-header-trigger i-layout-header-trigger-min">
<Dropdown :trigger="isMobile ? 'click' : 'hover'" class="i-layout-header-user" :class="{ 'i-layout-header-user-mobile': isMobile }" @on-click="handleClick">
<Avatar size="small" :src="info.avatar" v-if="info.avatar" />
<span class="i-layout-header-user-name" v-if="!isMobile">{{ info.name }}</span>
<DropdownMenu slot="list">
<i-link to="/setting/user">
<DropdownItem>
<Icon type="ios-contact-outline" />
<span>{{ $t('basicLayout.user.center') }}</span>
</DropdownItem>
</i-link>
<i-link to="/setting/account">
<DropdownItem>
<Icon type="ios-settings-outline" />
<span>{{ $t('basicLayout.user.setting') }}</span>
</DropdownItem>
</i-link>
<DropdownItem divided name="logout">
<Icon type="ios-log-out" />
<span>{{ $t('basicLayout.user.logOut') }}</span>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</span>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'iHeaderUser',
computed: {
...mapState('admin/user', [
'info'
]),
...mapState('admin/layout', [
'isMobile',
'logoutConfirm'
])
},
methods: {
...mapActions('admin/account', [
'logout'
]),
handleClick (name) {
if (name === 'logout') {
this.logout({
confirm: this.logoutConfirm,
vm: this
});
}
}
}
}
</script>
// 默认布局使用的多语言
export default {
'zh-CN': {
basicLayout: {
search: {
placeholder: '搜索...',
cancel: '取消'
},
user: {
center: '个人中心',
setting: '设置',
logOut: '退出登录'
},
logout: {
confirmTitle: '退出登录确认',
confirmContent: '您确定退出登录当前账户吗?打开的标签页和个人设置将会保存。'
},
tabs: {
left: '关闭左侧',
right: '关闭右侧',
other: '关闭其它',
all: '全部关闭'
}
}
},
'en-US': {
basicLayout: {
search: {
placeholder: 'Search...',
cancel: 'Cancel'
},
user: {
center: 'My home',
setting: 'Setting',
logOut: 'Log out'
},
logout: {
confirmTitle: 'Logout confirmation',
confirmContent: 'Are you sure you are logged out of your current account? Open tabs and personal settings will be saved.'
},
tabs: {
left: 'Close left',
right: 'Close right',
other: 'Close other',
all: 'Close all'
}
}
}
}
This diff is collapsed.
<template>
<div class="i-layout-menu-head" :class="{ 'i-layout-menu-head-mobile': isMobile }">
<Menu mode="horizontal" :active-name="headerName" v-if="!isMobile && !isMenuLimit" ref="menu">
<MenuItem v-for="item in filterHeader" :to="item.path" :replace="item.replace" :target="item.target" :name="item.name" :key="item.path" @click.native="handleClick(item.path, 'header')">
<i-menu-head-title :item="item" />
</MenuItem>
</Menu>
<div class="i-layout-header-trigger i-layout-header-trigger-min i-layout-header-trigger-in i-layout-header-trigger-no-height" v-else>
<Dropdown trigger="click" :class="{ 'i-layout-menu-head-mobile-drop': isMobile }">
<Icon type="ios-apps" />
<DropdownMenu slot="list">
<i-link v-for="item in filterHeader" :to="item.path" :replace="item.replace" :target="item.target" :key="item.path" @click.native="handleClick(item.path, 'header')">
<DropdownItem>
<i-menu-head-title :item="item" />
</DropdownItem>
</i-link>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import iMenuHeadTitle from './title';
import { mapState, mapGetters } from 'vuex';
import { getStyle } from 'view-design/src/utils/assist';
import clickItem from '../mixins/click-item';
// import { on, off } from 'view-design/src/utils/dom';
// import { throttle } from 'lodash';
export default {
name: 'iMenuHead',
components: { iMenuHeadTitle },
mixins: [ clickItem ],
computed: {
...mapState('admin/layout', [
'isMobile'
]),
...mapState('admin/menu', [
'headerName'
]),
...mapGetters('admin/menu', [
'filterHeader'
])
},
data () {
return {
handleResize: () => {},
isMenuLimit: false,
menuMaxWidth: 0 // 达到这个值后,menu 就显示不下了
}
},
methods: {
handleGetMenuHeight () {
const menuWidth = parseInt(getStyle(this.$el, 'width'));
const $menu = this.$refs.menu;
if ($menu) {
const menuHeight = parseInt(getStyle(this.$refs.menu.$el, 'height'));
if (menuHeight > 64) {
if (!this.isMenuLimit) {
this.menuMaxWidth = menuWidth;
}
this.isMenuLimit = true;
}
} else if (menuWidth >= this.menuMaxWidth) {
this.isMenuLimit = false;
}
}
},
watch: {
filterHeader () {
this.handleGetMenuHeight();
},
isMobile () {
this.handleGetMenuHeight();
}
},
mounted () {
// this.handleResize = throttle(this.handleGetMenuHeight, 100, { leading: false });
// on(window, 'resize', this.handleResize);
this.handleGetMenuHeight();
},
beforeDestroy () {
// off(window, 'resize', this.handleResize);
}
}
</script>
<template>
<div class="i-layout-menu-head-title">
<span class="i-layout-menu-head-title-icon" v-if="(item.icon || item.custom || item.img) && !hideIcon">
<Icon :type="item.icon" v-if="item.icon" />
<Icon :custom="item.custom" v-else-if="item.custom" />
<img :src="item.img" v-else-if="item.img" />
</span>
<span class="i-layout-menu-head-title-text">{{ tTitle(item.title) }}</span>
</div>
</template>
<script>
/**
* 该组件除了 Menu,也被 Breadcrumb 使用过
* */
import tTitle from '../mixins/translate-title';
export default {
name: 'iMenuHeadTitle',
mixins: [ tTitle ],
props: {
item: {
type: Object,
default () {
return {}
}
},
hideIcon: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<div>
<div class="i-layout-sider-logo" :class="{ 'i-layout-sider-logo-dark': siderTheme === 'dark' }">
<transition name="fade-quick">
<i-link to="/" v-show="!hideLogo">
<img src="@/assets/images/logo-small.png" v-if="menuCollapse">
<img src="@/assets/images/logo.png" v-else-if="siderTheme === 'light'">
<img src="@/assets/images/logo-dark.png" v-else>
</i-link>
</transition>
</div>
<Menu
ref="menu"
class="i-layout-menu-side i-scrollbar-hide"
:theme="siderTheme"
:accordion="menuAccordion"
:active-name="activePath"
:open-names="openNames"
width="auto">
<template v-if="!menuCollapse" v-for="(item, index) in filterSider">
<i-menu-side-item v-if="item.children === undefined || !item.children.length" :menu="item" :key="index" />
<i-menu-side-submenu v-else :menu="item" :key="index" />
</template>
<template v-else>
<Tooltip :content="tTitle(item.title)" placement="right" v-if="item.children === undefined || !item.children.length" :key="index">
<i-menu-side-item :menu="item" hide-title />
</Tooltip>
<i-menu-side-collapse v-else :menu="item" :key="index" top-level />
</template>
</Menu>
</div>
</template>
<script>
import iMenuSideItem from './menu-item';
import iMenuSideSubmenu from './submenu';
import iMenuSideCollapse from './menu-collapse';
import tTitle from '../mixins/translate-title';
import { mapState, mapGetters } from 'vuex';
export default {
name: 'iMenuSide',
mixins: [ tTitle ],
components: { iMenuSideItem, iMenuSideSubmenu, iMenuSideCollapse },
props: {
hideLogo: {
type: Boolean,
default: false
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'menuAccordion',
'menuCollapse'
]),
...mapState('admin/menu', [
'activePath',
'openNames'
]),
...mapGetters('admin/menu', [
'filterSider'
])
},
watch: {
'$route': {
handler () {
this.handleUpdateMenuState();
},
immediate: true
},
// 在展开/收起侧边菜单栏时,更新一次 menu 的状态
menuCollapse () {
this.handleUpdateMenuState();
}
},
methods: {
handleUpdateMenuState () {
this.$nextTick(() => {
if (this.$refs.menu) {
this.$refs.menu.updateActiveName();
if (this.menuAccordion) this.$refs.menu.updateOpened();
}
});
}
}
}
</script>
<template>
<Dropdown placement="right-start" :class="dropdownClasses">
<li :class="menuItemClasses" v-if="topLevel">
<i-menu-side-title :menu="menu" hide-title />
</li>
<DropdownItem v-else>
<i-menu-side-title :menu="menu" :selected="openNames.indexOf(menu.path) >= 0" />
<Icon type="ios-arrow-forward" class="i-layout-menu-side-arrow" />
</DropdownItem>
<DropdownMenu slot="list">
<div class="i-layout-menu-side-collapse-title" v-if="showCollapseMenuTitle">
<i-menu-side-title :menu="menu" />
</div>
<template v-for="(item, index) in menu.children">
<i-link :to="item.path" :target="item.target" v-if="item.children === undefined || !item.children.length" :key="index" @click.native="handleClick(item.path)">
<DropdownItem :divided="item.divided" :class="{ 'i-layout-menu-side-collapse-item-selected': item.path === activePath }">
<i-menu-side-title :menu="item" />
</DropdownItem>
</i-link>
<i-menu-side-collapse v-else :menu="item" :key="index" />
</template>
</DropdownMenu>
</Dropdown>
</template>
<script>
import iMenuSideTitle from './menu-title';
import clickItem from '../mixins/click-item';
import { mapState } from 'vuex';
export default {
name: 'iMenuSideCollapse',
components: { iMenuSideTitle },
mixins: [ clickItem ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
// 是否是第一级,区分在于左侧和展开侧
topLevel: {
type: Boolean,
default: false
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'showCollapseMenuTitle'
]),
...mapState('admin/menu', [
'activePath',
'openNames'
]),
dropdownClasses () {
return {
'i-layout-menu-side-collapse-top': this.topLevel,
'i-layout-menu-side-collapse-dark': this.siderTheme === 'dark'
}
},
menuItemClasses () {
return [
'ivu-menu-item i-layout-menu-side-collapse-top-item',
{
'ivu-menu-item-selected ivu-menu-item-active': this.openNames.indexOf(this.menu.path) >= 0 // -active 在高亮时,有背景
}
]
}
}
}
</script>
<template>
<div>
<MenuItem :to="menu.path" :replace="menu.replace" :target="menu.target" :name="menu.path" @click.native="handleClick(menu.path)">
<i-menu-side-title :menu="menu" :hide-title="hideTitle" />
</MenuItem>
</div>
</template>
<script>
import iMenuSideTitle from './menu-title';
import clickItem from '../mixins/click-item';
export default {
name: 'iMenuSideItem',
components: { iMenuSideTitle },
mixins: [ clickItem ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
hideTitle: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<span class="i-layout-menu-side-title">
<span class="i-layout-menu-side-title-icon" :class="{ 'i-layout-menu-side-title-icon-single': hideTitle }" v-if="menu.icon || menu.custom || menu.img">
<Icon :type="menu.icon" v-if="menu.icon" />
<Icon :custom="menu.custom" v-else-if="menu.custom" />
<img :src="menu.img" v-else-if="menu.img" />
</span>
<span class="i-layout-menu-side-title-text" :class="{ 'i-layout-menu-side-title-text-selected': selected }" v-if="!hideTitle">{{ tTitle(menu.title) }}</span>
</span>
</template>
<script>
import tTitle from '../mixins/translate-title';
export default {
name: 'iMenuSideTitle',
mixins: [ tTitle ],
props: {
menu: {
type: Object,
default () {
return {}
}
},
hideTitle: {
type: Boolean,
default: false
},
// 用于侧边栏收起 Dropdown 当前高亮
selected: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<Submenu :name="menu.path">
<template slot="title">
<i-menu-side-title :menu="menu" />
</template>
<template v-for="(item, index) in menu.children">
<i-menu-side-item v-if="item.children === undefined || !item.children.length" :menu="item" :key="index" />
<i-menu-side-submenu v-else :menu="item" :key="index" />
</template>
</Submenu>
</template>
<script>
import iMenuSideItem from './menu-item';
import iMenuSideTitle from './menu-title';
export default {
name: 'iMenuSideSubmenu',
components: { iMenuSideItem, iMenuSideTitle },
props: {
menu: {
type: Object,
default () {
return {}
}
}
}
}
</script>
import { findComponentUpward } from 'view-design/src/utils/assist';
import { mapState } from 'vuex';
export default {
computed: {
...mapState('admin/layout', [
'menuSiderReload',
'menuHeaderReload'
])
},
methods: {
handleClick (path, type = 'sider') {
const current = this.$route.path;
if (current === path) {
if (type === 'sider' && this.menuSiderReload) this.handleReload();
else if (type === 'header' && this.menuHeaderReload) this.handleReload();
}
},
handleReload () {
const $layout = findComponentUpward(this, 'BasicLayout');
if ($layout) $layout.handleReload();
}
}
}
export default {
methods: {
tTitle (title) {
if (title && title.indexOf('$t:') === 0) {
return this.$t(title.split('$t:')[1]);
} else {
return title;
}
}
}
}
<template>
<div class="i-layout-tabs" :class="classes" :style="styles">
<div class="i-layout-tabs-main">
<Tabs
type="card"
:value="current"
:animated="false"
closable
@on-click="handleClickTab"
@on-tab-remove="handleClickClose"
>
<TabPane
v-for="page in opened"
:key="page.fullPath"
:label="(h) => tabLabel(h, page)"
:name="page.fullPath"
:closable="page.meta && page.meta.closable"
/>
</Tabs>
<Dropdown class="i-layout-tabs-close" @on-click="handleClose">
<div class="i-layout-tabs-close-main">
<Icon type="ios-arrow-down" />
</div>
<DropdownMenu slot="list">
<DropdownItem name="left">
<Icon type="md-arrow-back" />
{{ $t('basicLayout.tabs.left') }}
</DropdownItem>
<DropdownItem name="right">
<Icon type="md-arrow-forward" />
{{ $t('basicLayout.tabs.right') }}
</DropdownItem>
<DropdownItem name="other">
<Icon type="md-close" />
{{ $t('basicLayout.tabs.other') }}
</DropdownItem>
<DropdownItem name="all">
<Icon type="md-close-circle" />
{{ $t('basicLayout.tabs.all') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import menuSider from '@/menu/sider';
import tTitle from '../mixins/translate-title';
import Setting from '@/setting';
import { getAllSiderMenu } from '@/libs/system';
export default {
name: 'iTabs',
mixins: [ tTitle ],
computed: {
...mapState('admin/page', [
'opened',
'current'
]),
...mapState('admin/layout', [
'showTabsIcon',
'tabsFix',
'tabsReload',
'headerFix',
'headerStick',
'isMobile',
'menuCollapse'
]),
...mapGetters('admin/menu', [
'hideSider'
]),
classes () {
return {
'i-layout-tabs-fix': this.tabsFix
}
},
isHeaderStick () {
return this.hideSider;
},
styles () {
let style = {};
if (this.tabsFix && !this.headerFix) {
style.top = `${64 - this.scrollTop}px`;
}
const menuWidth = this.isHeaderStick ? 0 : this.menuCollapse ? 80 : Setting.menuSideWidth;
if (!this.isMobile && this.tabsFix) {
style.width = `calc(100% - ${menuWidth}px)`;
style.left = `${menuWidth}px`;
}
return style;
}
},
data () {
return {
// 得到所有侧边菜单,并转为平级,查询图标用
allSiderMenu: getAllSiderMenu(menuSider),
scrollTop: 0
}
},
methods: {
...mapActions('admin/page', [
'close',
'closeLeft',
'closeRight',
'closeOther',
'closeAll'
]),
tabLabel (h, page) {
const title = h('span', this.tTitle(page.meta.title) || '未命名');
let slot = [];
if (this.showTabsIcon) {
const fullPathWithoutQuery = page.fullPath.indexOf('?') >= 0 ? page.fullPath.split('?')[0] : page.fullPath;
const currentMenu = this.allSiderMenu.find(menu => menu.path === fullPathWithoutQuery) || {};
let icon;
if (currentMenu.icon) {
icon = h('Icon', {
props: {
type: currentMenu.icon
}
});
} else if (currentMenu.custom) {
icon = h('Icon', {
props: {
custom: currentMenu.custom
}
});
} else if (currentMenu.img) {
icon = h('img', {
attrs: {
src: currentMenu.img
}
});
}
if (icon) slot.push(icon);
slot.push(title);
} else {
slot.push(title);
}
return h('div', {
class: 'i-layout-tabs-title'
}, slot);
},
handleClickTab (tabName) {
if (tabName === this.current) {
if (this.tabsReload) {
this.$emit('on-reload');
}
} else {
const page = this.opened.find(page => page.fullPath === tabName);
const { name, params, query } = page;
if (page) this.$router.push({ name, params, query }, () => {});
}
},
handleClickClose (tagName) {
this.close({
tagName
});
},
handleScroll () {
if (this.tabsFix && !this.headerFix) {
const scrollTop = document.body.scrollTop + document.documentElement.scrollTop;
this.scrollTop = scrollTop > 64 ? 64 : scrollTop;
}
},
handleClose (name) {
const params = {
pageSelect: this.current
};
switch (name) {
case 'left':
this.closeLeft(params);
break;
case 'right':
this.closeRight(params);
break;
case 'other':
this.closeOther(params);
break;
case 'all':
this.closeAll();
break;
}
}
},
mounted () {
document.addEventListener('scroll', this.handleScroll, { passive: true });
this.handleScroll();
},
beforeDestroy () {
document.removeEventListener('scroll', this.handleScroll);
}
}
</script>
<template>
<nuxt/>
</template>
<style lang="less">
html,body,#__layout,#__nuxt{
height: 100%;
}
</style>
<template> <template>
<div class="page-account"> <div class="page-account">
<div v-if="showI18n" class="page-account-header"> <div v-if="showI18n" class="page-account-header">
<i-header-i18n /> <i-header-i18n />
</div>
<div class="page-account-container">
<div class="page-account-top">
<div class="page-account-top-logo">
<img src="@/assets/images/logo.png" alt="logo" />
</div> </div>
<div class="page-account-container"> <div class="page-account-top-desc">iView Admin Pro 企业级中台前端/设计解决方案</div>
<div class="page-account-top"> </div>
<div class="page-account-top-logo"> <Login @on-submit="handleSubmit">
<img src="@/assets/images/logo.png" alt="logo"> <UserName name="username" value="admin" />
</div> <Password name="password" value="admin" enter-to-submit />
<div class="page-account-top-desc">iView Admin Pro 企业级中台前端/设计解决方案</div> <div class="page-account-auto-login">
</div> <Checkbox v-model="autoLogin" size="large">{{ $t('page.login.remember') }}</Checkbox>
<Login @on-submit="handleSubmit"> <a href>{{ $t('page.login.forgot') }}</a>
<UserName name="username" value="admin" />
<Password name="password" value="admin" enter-to-submit />
<div class="page-account-auto-login">
<Checkbox v-model="autoLogin" size="large">{{ $t('page.login.remember') }}</Checkbox>
<a href="">{{ $t('page.login.forgot') }}</a>
</div>
<Submit>{{ $t('page.login.submit') }}</Submit>
</Login>
<div class="page-account-other">
<span>{{ $t('page.login.other') }}</span>
<img src="@/assets/svg/icon-social-wechat.svg" alt="wechat">
<img src="@/assets/svg/icon-social-qq.svg" alt="qq">
<img src="@/assets/svg/icon-social-weibo.svg" alt="weibo">
<router-link class="page-account-register" to="./register">{{ $t('page.login.signup') }}</router-link>
</div>
</div> </div>
<i-copyright /> <Submit>{{ $t('page.login.submit') }}</Submit>
</Login>
<div class="page-account-other">
<span>{{ $t('page.login.other') }}</span>
<img src="@/assets/svg/icon-social-wechat.svg" alt="wechat" />
<img src="@/assets/svg/icon-social-qq.svg" alt="qq" />
<img src="@/assets/svg/icon-social-weibo.svg" alt="weibo" />
<router-link class="page-account-register" to="./register">{{ $t('page.login.signup') }}</router-link>
</div>
</div> </div>
<i-copyright />
</div>
</template> </template>
<script> <script>
import iCopyright from '@/components/copyright'; import iCopyright from "@/components/copyright";
import { mapActions } from 'vuex'; import { mapActions } from "vuex";
import mixins from '../mixins'; import mixins from "../mixins";
export default { export default {
mixins: [ mixins ], layout: "empty",
components: { iCopyright }, mixins: [mixins],
data () { components: { iCopyright },
return { data() {
autoLogin: true return {
} autoLogin: true
},
methods: {
...mapActions('admin/account', [
'login'
]),
/**
* @description 登录
* 表单校验已有 iView Pro 自动完成,如有需要修改,请阅读 iView Pro 文档
*/
handleSubmit (valid, values) {
if (valid) {
const { username, password } = values;
this.login({
username,
password
})
.then(() => {
// 重定向对象不存在则返回顶层路径
this.$router.replace(this.$route.query.redirect || '/');
});
}
}
}
}; };
},
methods: {
...mapActions("admin/account", ["login"]),
/**
* @description 登录
* 表单校验已有 iView Pro 自动完成,如有需要修改,请阅读 iView Pro 文档
*/
handleSubmit(valid, values) {
if (valid) {
const { username, password } = values;
this.login({
username,
password
}).then(() => {
// 重定向对象不存在则返回顶层路径
this.$router.replace(this.$route.query.redirect || "/");
});
}
}
}
};
</script> </script>
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
import mixins from '../mixins'; import mixins from '../mixins';
export default { export default {
layout: "empty",
mixins: [ mixins ], mixins: [ mixins ],
components: { iCopyright }, components: { iCopyright },
data () { data () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment