import { Component , Vue , Prop , Watch } from 'vue-property-decorator' ; import { State , Action } from 'vuex-class' ; import { Icon } from 'ant-design-vue' ; import styles from './index.less' ; import { DOMInfo } from './types' ; import defaultHomeKey from '@/config/default.homeKey' ; import { bindEvent , removeEvent } from '@/utils/event' ; import { tabItem , language , routesInfoMap , navLayout } from '@/store/types' ; @ Component export default class TabTool extends Vue { tabOverFlow : boolean = false ; leftArrow : boolean = false ; rightArrow : boolean = false ; tabDOMInfo : Array < DOMInfo > | any = [ ] ; tabInfo : DOMInfo | any = { } ; tabOffset : number = 0 ; tabTotalWidth : number = 0 ; windowResizeTimer : any | null = null ; lastExcuteResize = 0 ; @ Prop ( String ) mode ! : string ; @ Prop ( Boolean ) show ! : boolean ; @ Prop ( String ) navLayout ! : navLayout ; @ Prop ( String ) deviceType ! : string ; @ State ( 'tabActive' ) tabActive ! : string ; @ State ( 'tabList' ) tabList ! : Array < tabItem > ; @ State ( 'language' ) language ! : language ; @ State ( 'routesInfoMap' ) routesInfoMap ! : routesInfoMap ; @ Action ( 'clearTab' ) clearTab : Function ; @ Action ( 'handleTab' ) handleTab : Function ; @ Action ( 'removeTab' ) removeTab : Function ; @ Watch ( 'show' ) onShowChange ( status ) { if ( status ) { this . handleSiderEvent ( 'remove' ) ; this . handleWindowEvent ( 'remove' ) ; this . $nextTick ( ( ) => this . autoOffset ( ) ) ; this . handleSiderEvent ( 'bind' ) ; this . handleWindowEvent ( 'bind' ) ; } else { this . initTab ( ) ; this . handleSiderEvent ( 'remove' ) ; this . handleWindowEvent ( 'remove' ) ; } } @ Watch ( 'tabList' , { deep : true } ) onTabListChange ( newList ) { this . $nextTick ( ( ) => this . calculateTab ( ) ) ; if ( newList . length === 1 ) { this . initTab ( ) ; } } @ Watch ( 'tabActive' ) onTabActiveChnage ( active ) { this . $nextTick ( ( ) => this . handleActiveNotShow ( active ) ) ; } protected mounted ( ) { this . $nextTick ( ( ) => this . autoOffset ( ) ) ; this . handleSiderEvent ( 'bind' ) ; this . handleWindowEvent ( 'bind' ) ; } initTab ( ) { this . tabOverFlow = false ; this . leftArrow = false ; this . rightArrow = false ; this . tabOverFlow = false ; this . tabDOMInfo = [ ] ; this . tabInfo = { } ; this . tabOffset = 0 ; this . tabTotalWidth = 0 ; } calculateTab ( ) { const tabContainer = this . $refs . tabContainer ; if ( ! tabContainer || ! tabContainer . children ) return false ; const childrenArr = tabContainer . children [ 0 ] . children , childrenLen = childrenArr . length , tabInfo = tabContainer . getBoundingClientRect ( ) , childrenInfo = [ ] ; let totalWidth = 1 ; for ( let num = 0 ; num < childrenLen ; num ++ ) { const rectInfo = childrenArr [ num ] . getBoundingClientRect ( ) ; totalWidth += rectInfo . width - 1 ; childrenInfo [ childrenInfo . length ] = rectInfo ; } this . tabInfo = tabInfo ; this . tabDOMInfo = childrenInfo ; this . tabTotalWidth = totalWidth ; if ( totalWidth > tabInfo . width ) { if ( this . navLayout === 'left' && ! this . tabOverFlow ) tabInfo . width -= 60 ; this . tabOverFlow = true ; } else { this . tabOverFlow = false ; } } handleActiveNotShow ( active ) { if ( ! this . show ) return ; const tabContainer = this . $refs . tabContainer ; if ( ! tabContainer || ! tabContainer . children ) return ; const { tabOffset , tabList , tabDOMInfo , tabInfo } = this ; let showArea = tabOffset + 1 ; let start = showArea ; const activeIndex = tabList . findIndex ( item => item . id === active ) ; for ( let num = 0 ; num < activeIndex + 1 ; num ++ ) { const item = tabDOMInfo [ num ] ; showArea += item . width - 1 ; if ( num === activeIndex - 1 ) { start = showArea ; } } if ( showArea > tabInfo . width ) { this . tabOffset += tabInfo . width - showArea ; return ; } if ( start < 0 ) { this . tabOffset -= start ; } } operateTab ( type ) { let offset = this . tabOffset , pointer = 1 , calculated = offset ; const showArea = this . tabInfo . width , tabLen = this . tabDOMInfo . length ; for ( let num = 0 ; num < tabLen ; num ++ ) { const item = this . tabDOMInfo [ num ] ; pointer += item . width - 1 ; if ( type === 'next' ) { if ( pointer + offset > showArea ) { calculated = - ( pointer - item . width ) ; const extraWidth = Math . abs ( calculated ) + showArea - this . tabTotalWidth ; if ( extraWidth > 0 ) calculated += extraWidth ; break ; } } else if ( type === 'prev' ) { if ( pointer + offset >= 0 ) { calculated = - ( pointer - showArea ) ; if ( calculated > 0 ) calculated = 0 ; break ; } } } this . tabOffset = calculated ; } openTab ( keyPath ) { this . $router . push ( keyPath ) ; } clearExtraTab ( ) { this . clearTab ( { vm : this , tabList : this . tabList , routesInfoMap : this . routesInfoMap } ) ; } closeItem ( item , event ) { event . stopPropagation ( ) ; const { module } = this . routesInfoMap [ item . id ] ; const removeInfo = { tabInfo : item , vm : this } ; if ( module && module . length > 0 ) { removeInfo . module = module ; } this . removeTab ( removeInfo ) ; } handleSiderEvent ( type ) { if ( this . navLayout === 'top' ) return ; let SiderMenu = document . getElementById ( 's_siderMenu' ) ; SiderMenu = document . getElementById ( 's_siderMenu' ) ; if ( ! SiderMenu ) return ; if ( type == 'bind' ) return bindEvent ( SiderMenu , 'transitionend' , this . listenSideMenuChange , false ) ; removeEvent ( SiderMenu , 'transitionend' , this . listenSideMenuChange , false ) ; } handleWindowEvent ( type ) { if ( type == 'bind' ) return bindEvent ( window , 'resize' , this . listenWindowResize , false ) ; removeEvent ( window , 'resize' , this . listenWindowResize , false ) ; } listenSideMenuChange ( e ) { const SiderMenu = document . getElementById ( 's_siderMenu' ) ; if ( e . target === SiderMenu && e . propertyName === 'width' ) { this . autoOffset ( ) ; } } listenWindowResize ( { timeStamp } ) { if ( this . lastExcuteResize === 0 ) { this . lastExcuteResize = timeStamp ; } else if ( timeStamp - this . lastExcuteResize < 300 && this . windowResizeTimer ) { clearTimeout ( this . windowResizeTimer ) ; this . windowResizeTimer = null ; } this . windowResizeTimer = setTimeout ( ( ) => { this . autoOffset ( ) ; clearTimeout ( this . windowResizeTimer ) ; this . windowResizeTimer = null ; this . lastExcuteResize = 0 ; } , 300 ) ; } autoOffset ( ) { this . calculateTab ( ) ; this . tabOffset = 0 ; this . handleActiveNotShow ( this . tabActive ) ; } render ( ) { const { show , navLayout , tabOffset , tabOverFlow , tabTotalWidth , tabList , tabActive , $locale , language } = this ; const tabTranslate = $locale [ language . current ] . menu ; return < div class = { `
        ${ styles . tabContainer }
        ${ navLayout === 'top' || tabOverFlow ? styles . tabContainerPadding : tabList . length > 1 ? styles . tabContainerClear : '' }
        ${ navLayout === 'top' ? styles . tabContainerTopNav : '' }
        ` } > { tabOverFlow ? < button disabled = { tabOffset === 0 ? true : false } onClick = { ( ) => this . operateTab ( 'prev' ) } class = { `${ styles . arrowBt } system-theme-color` } > < Icon type = "left" / > < /button> : null} { show ? < div class = { styles . tabWrap } ref = "tabContainer" > < ul style = { { transform : `translate3d(${ tabOffset }px, 0, 0)` } } > { tabList . map ( item => < li onClick = { ( ) => this . openTab ( item . keyPath ) } class = { `
                  ${ styles . tabItem }
                  ${ defaultHomeKey === item . id ? '' : styles . tabItemClose }
                  ${ tabActive === item . id ? styles . tabItemActive : '' }` } > { tabTranslate [ item . id ] } { defaultHomeKey !== item . id ? < Icon onClick = { ( event ) => this . closeItem ( item , event ) } type = "close" / > : null } < /li>)} < /ul> < /div> : null} { tabOverFlow ? < button disabled = { Math . abs ( tabOffset ) + this . tabInfo . width >= tabTotalWidth ? true : false } onClick = { ( ) => this . operateTab ( 'next' ) } class = { `${ styles . arrowBt } system-theme-color` } > < Icon type = "right" / > < /button> : null} { tabList . length > 1 && show ? < button onClick = { this . clearExtraTab } class = { `${ styles . arrowBt } system-theme-color` } > < Icon type = "stop" / > < /button> : null} < /div>; } }