UNPKG

vxe-pc-ui

Version:
712 lines (711 loc) 22.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _vue = require("vue"); var _ui = require("../../ui"); var _vn = require("../../ui/src/vn"); var _dom = require("../..//ui/src/dom"); var _utils = require("../..//ui/src/utils"); var _log = require("../../ui/src/log"); var _xeUtils = _interopRequireDefault(require("xe-utils")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _default = exports.default = (0, _vue.defineComponent)({ name: 'VxeTabs', props: { modelValue: [String, Number, Boolean], options: Array, height: [String, Number], destroyOnClose: Boolean, titleWidth: [String, Number], titleAlign: [String, Number], type: String, showClose: Boolean, padding: { type: Boolean, default: () => (0, _ui.getConfig)().tabs.padding }, trigger: String, beforeChangeMethod: Function, closeConfig: Object, refreshConfig: Object, // 已废弃 beforeCloseMethod: Function }, emits: ['update:modelValue', 'change', 'tab-change', 'tab-change-fail', 'tab-close', 'tab-close-fail', 'tab-click', 'tab-load'], setup(props, context) { const { slots, emit } = context; const xID = _xeUtils.default.uniqueId(); const $xeParentTabs = (0, _vue.inject)('$xeTabs', null); const refElem = (0, _vue.ref)(); const refHeadWrapperElem = (0, _vue.ref)(); const reactData = (0, _vue.reactive)({ staticTabs: [], activeName: null, initNames: [], lintLeft: 0, lintWidth: 0, isTabOver: false, resizeFlag: 1, cacheTabMaps: {} }); const internalData = { slTimeout: undefined }; const refMaps = { refElem }; const computeCloseOpts = (0, _vue.computed)(() => { return Object.assign({}, (0, _ui.getConfig)().tabs.closeConfig, props.closeConfig); }); const computeRefreshOpts = (0, _vue.computed)(() => { return Object.assign({}, (0, _ui.getConfig)().tabs.refreshConfig, props.refreshConfig); }); const computeTabOptions = (0, _vue.computed)(() => { const { options } = props; return (options || []).filter(item => handleFilterTab(item)); }); const computeTabStaticOptions = (0, _vue.computed)(() => { const { staticTabs } = reactData; return staticTabs.filter(item => handleFilterTab(item)); }); const computeMaps = {}; const $xeTabs = { xID, props, context, reactData, getRefMaps: () => refMaps, getComputeMaps: () => computeMaps }; const handleFilterTab = item => { const { permissionCode } = item; if (permissionCode) { if (!_ui.permission.checkVisible(permissionCode)) { return false; } } return true; }; const callSlot = (slotFunc, params) => { if (slotFunc) { if (_xeUtils.default.isString(slotFunc)) { slotFunc = slots[slotFunc] || null; } if (_xeUtils.default.isFunction(slotFunc)) { return (0, _vn.getSlotVNs)(slotFunc(params)); } } return []; }; const updateTabStyle = () => { (0, _vue.nextTick)(() => { const { type } = props; const { activeName } = reactData; const tabOptions = computeTabOptions.value; const tabStaticOptions = computeTabStaticOptions.value; const headerWrapperEl = refHeadWrapperElem.value; let lintWidth = 0; let lintLeft = 0; let isOver = false; if (headerWrapperEl) { const index = _xeUtils.default.findIndexOf(tabStaticOptions.length ? tabStaticOptions : tabOptions, item => item.name === activeName); const { children, scrollWidth, clientWidth } = headerWrapperEl; isOver = scrollWidth !== clientWidth; if (index > -1) { const tabEl = children[index]; const tabWidth = tabEl.clientWidth; if (type) { if (type === 'card') { lintWidth = tabWidth + 2; lintLeft = tabEl.offsetLeft; } else if (type === 'border-card') { lintWidth = tabWidth + 2; lintLeft = tabEl.offsetLeft - 1; } } else { lintWidth = Math.max(4, Math.floor(tabWidth * 0.6)); lintLeft = tabEl.offsetLeft + Math.floor((tabWidth - lintWidth) / 2); } } } reactData.lintLeft = lintLeft; reactData.lintWidth = lintWidth; reactData.isTabOver = isOver; }); }; const dispatchEvent = (type, params, evnt) => { emit(type, (0, _ui.createEvent)(evnt, { $tabs: $xeTabs }, params)); }; const addInitName = (name, evnt) => { const { initNames } = reactData; if (name && !initNames.includes(name)) { dispatchEvent('tab-load', { name }, evnt); initNames.push(name); return true; } return false; }; const initDefaultName = list => { let activeName = null; const nameMaps = {}; if (list && list.length) { let validVal = false; activeName = props.modelValue; list.forEach(item => { const { name, preload } = item || {}; if (name) { nameMaps[`${name}`] = { loading: false }; if (activeName === name) { validVal = true; } if (preload) { addInitName(name, null); } } }); if (!validVal) { activeName = list[0].name; addInitName(activeName, null); emit('update:modelValue', activeName); } } reactData.activeName = activeName; reactData.cacheTabMaps = nameMaps; }; const clickEvent = (evnt, item) => { const { trigger } = props; const beforeMethod = props.beforeChangeMethod || (0, _ui.getConfig)().tabs.beforeChangeMethod; const { activeName } = reactData; const { name } = item; const value = name; dispatchEvent('tab-click', { name }, evnt); if (trigger === 'manual') { return; } if (name !== activeName) { Promise.resolve(!beforeMethod || beforeMethod({ $tabs: $xeTabs, name, oldName: activeName, newName: name, option: item })).then(status => { if (status) { reactData.activeName = name; emit('update:modelValue', value); addInitName(name, evnt); dispatchEvent('change', { value, name, oldName: activeName, newName: name, option: item }, evnt); dispatchEvent('tab-change', { value, name, oldName: activeName, newName: name, option: item }, evnt); } else { dispatchEvent('tab-change-fail', { value, name, oldName: activeName, newName: name, option: item }, evnt); } }).catch(() => { dispatchEvent('tab-change-fail', { value, name, oldName: activeName, newName: name, option: item }, evnt); }); } }; const handleRefreshTabEvent = (evnt, item) => { evnt.stopPropagation(); const { activeName, cacheTabMaps } = reactData; const { name } = item; const refreshOpts = computeRefreshOpts.value; const { queryMethod } = refreshOpts; const cacheItem = name ? cacheTabMaps[`${name}`] : null; if (cacheItem) { if (queryMethod) { cacheItem.loading = true; Promise.resolve(queryMethod({ $tabs: $xeTabs, value: activeName, name, option: item })).finally(() => { cacheItem.loading = false; }); } else { (0, _log.errLog)('vxe.error.notFunc', ['refresh-config.queryMethod']); } } }; const handleCloseTabEvent = (evnt, item, index, list) => { evnt.stopPropagation(); const { activeName } = reactData; const closeOpts = computeCloseOpts.value; const beforeMethod = closeOpts.beforeMethod || props.beforeCloseMethod || (0, _ui.getConfig)().tabs.beforeCloseMethod; const { name } = item; const value = activeName; let nextName = value; if (activeName === name) { const nextItem = index < list.length - 1 ? list[index + 1] : list[index - 1]; nextName = nextItem ? nextItem.name : null; } Promise.resolve(!beforeMethod || beforeMethod({ $tabs: $xeTabs, value, name, nextName, option: item })).then(status => { if (status) { dispatchEvent('tab-close', { value, name, nextName }, evnt); } else { dispatchEvent('tab-close-fail', { value, name, nextName }, evnt); } }).catch(() => { dispatchEvent('tab-close-fail', { value, name, nextName }, evnt); }); }; const startScrollAnimation = (offsetPos, offsetSize) => { const { slTimeout } = internalData; let offsetLeft = offsetSize; let scrollCount = 6; let delayNum = 35; if (slTimeout) { clearTimeout(slTimeout); internalData.slTimeout = undefined; } const scrollAnimate = () => { const headerWrapperEl = refHeadWrapperElem.value; if (scrollCount > 0) { scrollCount--; if (headerWrapperEl) { const { clientWidth, scrollWidth, scrollLeft } = headerWrapperEl; offsetLeft = Math.floor(offsetLeft / 2); if (offsetPos > 0) { if (clientWidth + scrollLeft < scrollWidth) { headerWrapperEl.scrollLeft += offsetLeft; delayNum -= 4; internalData.slTimeout = setTimeout(scrollAnimate, delayNum); } } else { if (scrollLeft > 0) { headerWrapperEl.scrollLeft -= offsetLeft; delayNum -= 4; internalData.slTimeout = setTimeout(scrollAnimate, delayNum); } } updateTabStyle(); } } }; scrollAnimate(); }; const handleScrollToLeft = offsetPos => { const headerWrapperEl = refHeadWrapperElem.value; if (headerWrapperEl) { const offsetSize = Math.floor(headerWrapperEl.clientWidth * 0.75); startScrollAnimation(offsetPos, offsetSize); } }; const scrollLeftEvent = () => { handleScrollToLeft(-1); }; const scrollRightEvent = () => { handleScrollToLeft(1); }; const scrollToTab = name => { const tabOptions = computeTabOptions.value; const tabStaticOptions = computeTabStaticOptions.value; return (0, _vue.nextTick)().then(() => { const headerWrapperEl = refHeadWrapperElem.value; if (headerWrapperEl) { const index = _xeUtils.default.findIndexOf(tabStaticOptions.length ? tabStaticOptions : tabOptions, item => item.name === name); if (index > -1) { const { scrollLeft, clientWidth, children } = headerWrapperEl; const tabEl = children[index]; if (tabEl) { const tabOffsetLeft = tabEl.offsetLeft; const tabClientWidth = tabEl.clientWidth; // 如果右侧被挡 const overSize = tabOffsetLeft + tabClientWidth - (scrollLeft + clientWidth); if (overSize > 0) { headerWrapperEl.scrollLeft += overSize; } // 如果左侧被挡,优先 if (tabOffsetLeft < scrollLeft) { headerWrapperEl.scrollLeft = tabOffsetLeft; } } } updateTabStyle(); } }); }; const handlePrevNext = isNext => { const { activeName } = reactData; const tabOptions = computeTabOptions.value; const tabStaticOptions = computeTabStaticOptions.value; const list = tabStaticOptions.length ? tabStaticOptions : tabOptions; const index = _xeUtils.default.findIndexOf(list, item => item.name === activeName); if (index > -1) { let item = null; if (isNext) { if (index < list.length - 1) { item = list[index + 1]; } } else { if (index > 0) { item = list[index - 1]; } } if (item) { const name = item.name; const value = name; reactData.activeName = name; emit('update:modelValue', value); addInitName(name, null); } } return (0, _vue.nextTick)(); }; const tabsMethods = { dispatchEvent, scrollToTab, prev() { return handlePrevNext(false); }, next() { return handlePrevNext(true); }, prevTab() { if (process.env.NODE_ENV === 'development') { (0, _log.warnLog)('vxe.error.delFunc', ['prevTab', 'prev']); } return tabsMethods.prev(); }, nextTab() { if (process.env.NODE_ENV === 'development') { (0, _log.warnLog)('vxe.error.delFunc', ['nextTab', 'next']); } return tabsMethods.next(); } }; const tabsPrivateMethods = {}; Object.assign($xeTabs, tabsMethods, tabsPrivateMethods); const renderTabHeader = tabList => { const { type, titleWidth: allTitleWidth, titleAlign: allTitleAlign, showClose, closeConfig, refreshConfig } = props; const { activeName, lintLeft, lintWidth, isTabOver, cacheTabMaps } = reactData; const extraSlot = slots.extra; const closeOpts = computeCloseOpts.value; const closeVisibleMethod = closeOpts.visibleMethod; const refreshOpts = computeRefreshOpts.value; const refreshVisibleMethod = refreshOpts.visibleMethod; return (0, _vue.h)('div', { class: 'vxe-tabs-header' }, [isTabOver ? (0, _vue.h)('div', { class: 'vxe-tabs-header--bar vxe-tabs-header--left-bar', onClick: scrollLeftEvent }, [(0, _vue.h)('span', { class: (0, _ui.getIcon)().TABS_TAB_BUTTON_LEFT })]) : (0, _vue.createCommentVNode)(), (0, _vue.h)('div', { class: 'vxe-tabs-header--wrapper' }, [(0, _vue.h)('div', { ref: refHeadWrapperElem, class: 'vxe-tabs-header--item-wrapper' }, tabList.map((item, index) => { const { title, titleWidth, titleAlign, icon, name, slots } = item; const titleSlot = slots ? slots.title || slots.tab : null; const itemWidth = titleWidth || allTitleWidth; const itemAlign = titleAlign || allTitleAlign; const params = { $tabs: $xeTabs, value: activeName, name, option: item }; const isActive = activeName === name; const cacheItem = name ? cacheTabMaps[`${name}`] : null; const isLoading = cacheItem ? cacheItem.loading : false; return (0, _vue.h)('div', { key: `${name}`, class: ['vxe-tabs-header--item', itemAlign ? `align--${itemAlign}` : '', { 'is--active': isActive }], style: itemWidth ? { width: (0, _dom.toCssUnit)(itemWidth) } : null, onClick(evnt) { clickEvent(evnt, item); } }, [(0, _vue.h)('div', { class: 'vxe-tabs-header--item-inner' }, [(0, _vue.h)('div', { class: 'vxe-tabs-header--item-content' }, [icon ? (0, _vue.h)('span', { class: 'vxe-tabs-header--item-icon' }, [(0, _vue.h)('i', { class: icon })]) : (0, _vue.createCommentVNode)(), (0, _vue.h)('span', { class: 'vxe-tabs-header--item-name' }, titleSlot ? callSlot(titleSlot, { name, title }) : `${title}`)]), ((0, _utils.isEnableConf)(refreshConfig) || refreshOpts.enabled) && (refreshVisibleMethod ? refreshVisibleMethod(params) : isActive) ? (0, _vue.h)('div', { class: 'vxe-tabs-header--refresh-btn', onClick(evnt) { handleRefreshTabEvent(evnt, item); } }, [(0, _vue.h)('i', { class: isLoading ? (0, _ui.getIcon)().TABS_TAB_REFRESH_LOADING : (0, _ui.getIcon)().TABS_TAB_REFRESH })]) : (0, _vue.createCommentVNode)(), (showClose || (0, _utils.isEnableConf)(closeConfig) || closeOpts.enabled) && (!closeVisibleMethod || closeVisibleMethod(params)) ? (0, _vue.h)('div', { class: 'vxe-tabs-header--close-btn', onClick(evnt) { handleCloseTabEvent(evnt, item, index, tabList); } }, [(0, _vue.h)('i', { class: (0, _ui.getIcon)().TABS_TAB_CLOSE })]) : (0, _vue.createCommentVNode)()])]); }).concat([(0, _vue.h)('span', { key: 'line', class: `vxe-tabs-header--active-line type--${type || 'default'}`, style: { left: `${lintLeft}px`, width: `${lintWidth}px` } })]))]), isTabOver ? (0, _vue.h)('div', { class: 'vxe-tabs-header--bar vxe-tabs-header--right-bar', onClick: scrollRightEvent }, [(0, _vue.h)('span', { class: (0, _ui.getIcon)().TABS_TAB_BUTTON_RIGHT })]) : (0, _vue.createCommentVNode)(), extraSlot ? (0, _vue.h)('div', { class: 'vxe-tabs-header--extra' }, (0, _vn.getSlotVNs)(extraSlot({}))) : (0, _vue.createCommentVNode)()]); }; const renderTabPane = item => { const { initNames, activeName } = reactData; const { name, slots } = item; const defaultSlot = slots ? slots.default : null; return name && initNames.includes(name) ? (0, _vue.h)('div', { key: `${name}`, class: ['vxe-tabs-pane--item', { 'is--visible': activeName === name, 'has--content': !!defaultSlot }] }, defaultSlot ? callSlot(defaultSlot, { name }) : []) : (0, _vue.createCommentVNode)(); }; const renderTabContent = tabList => { const { destroyOnClose } = props; const { activeName } = reactData; const activeDefaultTab = tabList.find(item => item.name === activeName); if (destroyOnClose) { return [activeDefaultTab ? renderTabPane(activeDefaultTab) : (0, _vue.createCommentVNode)()]; } return tabList.map(item => renderTabPane(item)); }; const renderVN = () => { const { type, height, padding, trigger } = props; const tabOptions = computeTabOptions.value; const tabStaticOptions = computeTabStaticOptions.value; const defaultSlot = slots.default; const footerSlot = slots.footer; const tabList = defaultSlot ? tabStaticOptions : tabOptions; return (0, _vue.h)('div', { ref: refElem, class: ['vxe-tabs', `vxe-tabs--${type || 'default'}`, `trigger--${trigger === 'manual' ? 'trigger' : 'default'}`, { 'is--padding': padding, 'is--height': height }], style: height ? { height: (0, _dom.toCssUnit)(height) } : null }, [(0, _vue.h)('div', { class: 'vxe-tabs-slots' }, defaultSlot ? defaultSlot({}) : []), renderTabHeader(tabList), (0, _vue.h)('div', { class: 'vxe-tabs-pane' }, renderTabContent(tabList)), footerSlot ? (0, _vue.h)('div', { class: 'vxe-tabs-footer' }, callSlot(footerSlot, {})) : (0, _ui.renderEmptyElement)($xeTabs)]); }; (0, _vue.watch)(() => props.modelValue, val => { addInitName(val, null); reactData.activeName = val; }); (0, _vue.watch)(() => reactData.activeName, val => { scrollToTab(val); (0, _vue.nextTick)(() => { reactData.resizeFlag++; }); }); const optsFlag = (0, _vue.ref)(0); (0, _vue.watch)(() => props.options ? props.options.length : -1, () => { optsFlag.value++; }); (0, _vue.watch)(() => props.options, () => { optsFlag.value++; }); (0, _vue.watch)(optsFlag, () => { initDefaultName(props.options); updateTabStyle(); }); const stFlag = (0, _vue.ref)(0); (0, _vue.watch)(() => reactData.staticTabs ? reactData.staticTabs.length : -1, () => { stFlag.value++; }); (0, _vue.watch)(() => reactData.staticTabs, () => { stFlag.value++; }); (0, _vue.watch)(stFlag, () => { initDefaultName(reactData.staticTabs); updateTabStyle(); }); if ($xeParentTabs) { (0, _vue.watch)(() => $xeParentTabs ? $xeParentTabs.reactData.resizeFlag : null, () => { reactData.resizeFlag++; }); } (0, _vue.watch)(() => reactData.resizeFlag, () => { (0, _vue.nextTick)(() => { updateTabStyle(); }); }); (0, _vue.onMounted)(() => { _ui.globalEvents.on($xeTabs, 'resize', updateTabStyle); updateTabStyle(); }); (0, _vue.onUnmounted)(() => { _ui.globalEvents.off($xeTabs, 'resize'); }); (0, _vue.provide)('$xeTabs', $xeTabs); addInitName(props.modelValue, null); initDefaultName(reactData.staticTabs.length ? reactData.staticTabs : props.options); $xeTabs.renderVN = renderVN; return $xeTabs; }, render() { return this.renderVN(); } });