vxe-pc-ui
Version:
A vue based PC component library
586 lines (585 loc) • 18.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _vue = require("vue");
var _comp = require("../../ui/src/comp");
var _xeUtils = _interopRequireDefault(require("xe-utils"));
var _ui = require("../../ui");
var _dom = require("../../ui/src/dom");
var _utils = require("../../ui/src/utils");
var _vn = require("../../ui/src/vn");
var _log = require("../../ui/src/log");
var _loading = _interopRequireDefault(require("../../loading"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
menus,
getConfig,
getIcon
} = _ui.VxeUI;
var _default = exports.default = (0, _comp.defineVxeComponent)({
name: 'VxeMenu',
props: {
modelValue: [String, Number],
expandAll: Boolean,
accordion: {
type: Boolean,
default: () => getConfig().menu.accordion
},
collapsed: {
type: Boolean,
default: null
},
collapseFixed: Boolean,
loading: Boolean,
options: {
type: Array,
default: () => []
},
size: {
type: String,
default: () => getConfig().menu.size || getConfig().size
},
menuConfig: Object
},
emits: ['update:modelValue', 'click', 'option-menu', 'menu-click'],
setup(props, context) {
const {
emit,
slots
} = context;
const xID = _xeUtils.default.uniqueId();
const $xeLayoutAside = (0, _vue.inject)('$xeLayoutAside', null);
const refElem = (0, _vue.ref)();
const refCollapseElem = (0, _vue.ref)();
const {
computeSize
} = (0, _ui.useSize)(props);
const reactData = (0, _vue.reactive)({
initialized: !!props.collapsed,
isEnterCollapse: false,
collapseStyle: {},
collapseZindex: 0,
activeName: props.modelValue,
menuList: [],
itemHeight: 1
});
const refMaps = {
refElem
};
const computeMenuOpts = (0, _vue.computed)(() => {
return Object.assign({}, getConfig().menu.menuConfig, props.menuConfig);
});
const computeIsCollapsed = (0, _vue.computed)(() => {
const {
collapsed
} = props;
if (_xeUtils.default.isBoolean(collapsed)) {
return collapsed;
}
if ($xeLayoutAside) {
return !!$xeLayoutAside.props.collapsed;
}
return false;
});
const computeCollapseWidth = (0, _vue.computed)(() => {
let collapseWidth = '';
if ($xeLayoutAside) {
collapseWidth = $xeLayoutAside.props.collapseWidth || '';
}
return collapseWidth;
});
const computeCollapseEnterWidth = (0, _vue.computed)(() => {
let width = '';
if ($xeLayoutAside) {
width = $xeLayoutAside.props.width || '';
}
return width;
});
const computeMaps = {
computeSize
};
const $xeMenu = {
xID,
props,
context,
reactData,
getRefMaps: () => refMaps,
getComputeMaps: () => computeMaps
};
const getMenuTitle = item => {
return `${item.title || item.name}`;
};
const updateZindex = () => {
if (reactData.collapseZindex < (0, _utils.getLastZIndex)()) {
reactData.collapseZindex = (0, _utils.nextZIndex)();
}
};
const updateActiveMenu = isDefExpand => {
const {
activeName
} = reactData;
_xeUtils.default.eachTree(reactData.menuList, (item, index, items, path, parent, nodes) => {
if (item.itemKey === activeName) {
nodes.forEach(obj => {
obj.isActive = true;
if (isDefExpand) {
obj.isExpand = true;
}
});
item.isExactActive = true;
} else {
item.isExactActive = false;
item.isActive = false;
}
}, {
children: 'childList'
});
};
const updateMenuConfig = () => {
const {
options,
expandAll
} = props;
reactData.menuList = _xeUtils.default.mapTree(options, (item, index, items, path, parent) => {
const objItem = Object.assign(Object.assign({}, item), {
parentKey: parent ? parent.name || path.slice(0, path.length - 1).join(',') : '',
level: path.length,
itemKey: item.name || path.join(','),
isExactActive: false,
isActive: false,
isExpand: _xeUtils.default.isBoolean(item.expanded) ? item.expanded : !!expandAll,
hasChild: item.children && item.children.length > 0
});
return objItem;
}, {
children: 'children',
mapChildren: 'childList'
});
};
const updateCollapseStyle = () => {
const {
collapseFixed
} = props;
if (collapseFixed) {
(0, _vue.nextTick)(() => {
const {
isEnterCollapse
} = reactData;
const isCollapsed = computeIsCollapsed.value;
const collapseEnterWidth = computeCollapseEnterWidth.value;
const collapseWidth = computeCollapseWidth.value;
const el = refElem.value;
if (el) {
const clientRect = el.getBoundingClientRect();
const parentNode = el.parentNode;
reactData.collapseStyle = isCollapsed ? {
top: (0, _dom.toCssUnit)(clientRect.top),
left: (0, _dom.toCssUnit)(clientRect.left),
height: (0, _dom.toCssUnit)(parentNode.clientHeight),
width: isEnterCollapse ? collapseEnterWidth ? (0, _dom.toCssUnit)(collapseEnterWidth) : '' : collapseWidth ? (0, _dom.toCssUnit)(collapseWidth) : '',
zIndex: reactData.collapseZindex
} : {};
}
});
}
};
const handleCollapseMenu = () => {
const {
collapseFixed
} = props;
if (collapseFixed) {
const {
initialized
} = reactData;
const isCollapsed = computeIsCollapsed.value;
if (isCollapsed) {
if (!initialized) {
reactData.initialized = true;
(0, _vue.nextTick)(() => {
const collapseEl = refCollapseElem.value;
if (collapseEl) {
document.body.appendChild(collapseEl);
}
});
}
}
reactData.isEnterCollapse = false;
updateZindex();
updateCollapseStyle();
}
};
const handleClickIconCollapse = (evnt, item, itemList) => {
const {
accordion
} = props;
const {
hasChild,
isExpand
} = item;
if (hasChild) {
evnt.stopPropagation();
evnt.preventDefault();
if (accordion) {
itemList.forEach(obj => {
if (obj !== item) {
obj.isExpand = false;
}
});
}
item.isExpand = !isExpand;
}
};
const emitModel = value => {
reactData.activeName = value;
emit('update:modelValue', value);
};
const handleContextmenuEvent = (evnt, item) => {
const {
menuConfig
} = props;
const menuOpts = computeMenuOpts.value;
if (menuConfig ? (0, _utils.isEnableConf)(menuOpts) : menuOpts.enabled) {
const {
options,
visibleMethod
} = menuOpts;
if (!visibleMethod || visibleMethod({
$menu: $xeMenu,
options,
currentMenu: item
})) {
if (_ui.VxeUI.contextMenu) {
_ui.VxeUI.contextMenu.openByEvent(evnt, {
options,
events: {
optionClick(eventParams) {
const {
option
} = eventParams;
const gMenuOpts = menus.get(option.code);
const mmMethod = gMenuOpts ? gMenuOpts.menuMenuMethod : null;
const params = {
menu: option,
currentMenu: item,
$event: evnt,
$menu: $xeMenu
};
if (mmMethod) {
mmMethod(params, evnt);
}
dispatchEvent('menu-click', params, eventParams.$event);
}
}
});
}
}
}
dispatchEvent('option-menu', {
currentMenu: item
}, evnt);
};
const handleClickMenu = (evnt, item, itemList) => {
const {
itemKey,
routerLink,
hasChild
} = item;
if (routerLink) {
emitModel(itemKey);
handleMenuMouseleave();
} else {
if (hasChild) {
handleClickIconCollapse(evnt, item, itemList);
} else {
emitModel(itemKey);
handleMenuMouseleave();
}
}
const params = {
currentMenu: item,
// 已废弃
menu: item
};
dispatchEvent('click', params, evnt);
};
const handleMenuMouseenter = () => {
const {
collapseStyle
} = reactData;
const collapseEnterWidth = computeCollapseEnterWidth.value;
reactData.collapseStyle = Object.assign({}, collapseStyle, {
width: collapseEnterWidth ? (0, _dom.toCssUnit)(collapseEnterWidth) : ''
});
reactData.isEnterCollapse = true;
};
const handleMenuMouseover = () => {
const {
isEnterCollapse
} = reactData;
if (!isEnterCollapse) {
handleMenuMouseenter();
}
};
const handleMenuMouseleave = () => {
const {
collapseStyle
} = reactData;
const el = refElem.value;
reactData.collapseStyle = Object.assign({}, collapseStyle, {
width: el ? (0, _dom.toCssUnit)(el.offsetWidth) : ''
});
reactData.isEnterCollapse = false;
};
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 dispatchEvent = (type, params, evnt) => {
emit(type, (0, _ui.createEvent)(evnt, {
$menu: $xeMenu
}, params));
};
const menuMethods = {
dispatchEvent
};
const menuPrivateMethods = {};
Object.assign($xeMenu, menuMethods, menuPrivateMethods);
const renderMenuTitle = (item, itemList) => {
const {
icon,
isExpand,
hasChild
} = item;
const itemSlots = item.slots || {};
const optionSlot = itemSlots.default || slots.option;
const titleSlot = itemSlots.title || slots.optionTitle || slots['option-title'];
const iconSlot = itemSlots.icon || slots.optionIcon || slots['option-icon'];
const title = getMenuTitle(item);
const isCollapsed = computeIsCollapsed.value;
const params = {
currentMenu: item,
collapsed: isCollapsed,
// 已废弃
option: item
};
return [optionSlot ? (0, _ui.renderEmptyElement)($xeMenu) : (0, _vue.h)('div', {
class: 'vxe-menu--item-link-icon'
}, iconSlot ? callSlot(iconSlot, params) : icon ? [(0, _vue.h)('i', {
class: icon
})] : []), optionSlot ? (0, _vue.h)('div', {
class: 'vxe-menu--item-custom-title'
}, callSlot(optionSlot, params)) : (0, _vue.h)('div', {
class: 'vxe-menu--item-link-title',
title
}, titleSlot ? callSlot(titleSlot, params) : title), hasChild ? (0, _vue.h)('div', {
class: 'vxe-menu--item-link-collapse',
onClick(evnt) {
handleClickIconCollapse(evnt, item, itemList);
}
}, [(0, _vue.h)('i', {
class: isExpand ? getIcon().MENU_ITEM_EXPAND_OPEN : getIcon().MENU_ITEM_EXPAND_CLOSE
})]) : (0, _ui.renderEmptyElement)($xeMenu)];
};
const renderDefaultChildren = (item, itemList) => {
const {
itemKey,
level,
hasChild,
isActive,
isExactActive,
isExpand,
routerLink,
childList
} = item;
const {
isEnterCollapse
} = reactData;
const isCollapsed = computeIsCollapsed.value;
if (item.permissionCode) {
if (!_ui.permission.checkVisible(item.permissionCode)) {
return (0, _ui.renderEmptyElement)($xeMenu);
}
}
return (0, _vue.h)('div', {
key: itemKey,
class: ['vxe-menu--item-wrapper', `vxe-menu--item-level${level}`, {
'is--exact-active': isExactActive,
'is--active': isActive,
'is--expand': (!isCollapsed || isEnterCollapse) && isExpand
}]
}, [routerLink ? (0, _vue.h)((0, _vue.resolveComponent)('router-link'), {
class: 'vxe-menu--item-link',
to: routerLink,
onContextmenu(evnt) {
handleContextmenuEvent(evnt, item);
},
onClick(evnt) {
handleClickMenu(evnt, item, itemList);
}
}, {
default: () => renderMenuTitle(item, itemList)
}) : (0, _vue.h)('div', {
class: 'vxe-menu--item-link',
onContextmenu(evnt) {
handleContextmenuEvent(evnt, item);
},
onClick(evnt) {
handleClickMenu(evnt, item, itemList);
}
}, renderMenuTitle(item, itemList)), hasChild ? (0, _vue.h)('div', {
class: 'vxe-menu--item-group'
}, childList.map(child => renderDefaultChildren(child, childList))) : (0, _ui.renderEmptyElement)($xeMenu)]);
};
const renderCollapseChildren = (item, itemList) => {
const {
itemKey,
level,
hasChild,
isActive,
isExactActive,
routerLink,
childList
} = item;
if (item.permissionCode) {
if (!_ui.permission.checkVisible(item.permissionCode)) {
return (0, _ui.renderEmptyElement)($xeMenu);
}
}
return (0, _vue.h)('div', {
key: itemKey,
class: ['vxe-menu--item-wrapper', `vxe-menu--item-level${level}`, {
'is--exact-active': isExactActive,
'is--active': isActive
}]
}, [routerLink ? (0, _vue.h)((0, _vue.resolveComponent)('router-link'), {
class: 'vxe-menu--item-link',
to: routerLink,
onContextmenu(evnt) {
handleContextmenuEvent(evnt, item);
},
onClick(evnt) {
handleClickMenu(evnt, item, itemList);
}
}, {
default: () => renderMenuTitle(item, itemList)
}) : (0, _vue.h)('div', {
class: 'vxe-menu--item-link',
onContextmenu(evnt) {
handleContextmenuEvent(evnt, item);
},
onClick(evnt) {
handleClickMenu(evnt, item, itemList);
}
}, renderMenuTitle(item, itemList)), hasChild ? (0, _vue.h)('div', {
class: 'vxe-menu--item-group'
}, childList.map(child => renderDefaultChildren(child, childList))) : (0, _ui.renderEmptyElement)($xeMenu)]);
};
const renderVN = () => {
const {
loading,
collapseFixed
} = props;
const {
initialized,
menuList,
collapseStyle,
isEnterCollapse
} = reactData;
const vSize = computeSize.value;
const isCollapsed = computeIsCollapsed.value;
let ons = {};
if (collapseFixed) {
ons = {
onMouseenter: handleMenuMouseenter,
onMouseover: handleMenuMouseover,
onMouseleave: handleMenuMouseleave
};
}
return (0, _vue.h)('div', {
ref: refElem,
class: ['vxe-menu', {
[`size--${vSize}`]: vSize,
'is--collapsed': isCollapsed,
'is--loading': loading
}]
}, [(0, _vue.h)('div', {
class: 'vxe-menu--item-list'
}, menuList.map(child => isCollapsed ? renderCollapseChildren(child, menuList) : renderDefaultChildren(child, menuList))), initialized ? (0, _vue.h)('div', Object.assign({
ref: refCollapseElem,
class: ['vxe-menu--collapse-wrapper', {
[`size--${vSize}`]: vSize,
'is--collapsed': isCollapsed,
'is--enter': isEnterCollapse,
'is--loading': loading
}],
style: collapseStyle
}, ons), [isCollapsed ? (0, _vue.h)('div', {
class: 'vxe-menu--item-list'
}, menuList.map(child => renderDefaultChildren(child, menuList))) : (0, _ui.renderEmptyElement)($xeMenu)]) : (0, _ui.renderEmptyElement)($xeMenu),
/**
* 加载中
*/
(0, _vue.h)(_loading.default, {
class: 'vxe-list-view--loading',
modelValue: loading
})]);
};
const optFlag = (0, _vue.ref)(0);
(0, _vue.watch)(() => props.options ? props.options.length : -1, () => {
optFlag.value++;
});
(0, _vue.watch)(() => props.options, () => {
optFlag.value++;
});
(0, _vue.watch)(optFlag, () => {
updateMenuConfig();
updateActiveMenu(true);
});
(0, _vue.watch)(() => props.modelValue, val => {
reactData.activeName = val;
});
(0, _vue.watch)(() => reactData.activeName, () => {
updateActiveMenu(true);
});
(0, _vue.watch)(computeIsCollapsed, () => {
handleCollapseMenu();
});
(0, _vue.onMounted)(() => {
const {
menuConfig
} = props;
const VxeUIContextMenu = _ui.VxeUI.getComponent('VxeContextMenu');
if (menuConfig && !VxeUIContextMenu) {
(0, _log.errLog)('vxe.error.reqComp', ['vxe-context-menu']);
}
_ui.globalEvents.on($xeMenu, 'resize', updateCollapseStyle);
updateCollapseStyle();
});
(0, _vue.onBeforeUnmount)(() => {
_ui.globalEvents.off($xeMenu, 'resize');
const collapseEl = refCollapseElem.value;
if (collapseEl) {
const parentNode = collapseEl.parentNode;
if (parentNode) {
parentNode.removeChild(collapseEl);
}
}
});
updateMenuConfig();
updateActiveMenu(true);
$xeMenu.renderVN = renderVN;
return $xeMenu;
},
render() {
return this.renderVN();
}
});