mixin.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import XEUtils from 'xe-utils';
  2. import { VxeUI } from '../../../ui';
  3. import { getDomNode, getAbsolutePos, getEventTargetNode } from '../../../ui/src/dom';
  4. import { isEnableConf, hasChildrenList } from '../../../ui/src/utils';
  5. import { warnLog } from '../../../ui/src/log';
  6. const { menus, globalEvents, GLOBAL_EVENT_KEYS } = VxeUI;
  7. export default {
  8. methods: {
  9. /**
  10. * 关闭快捷菜单
  11. */
  12. _closeMenu() {
  13. const $xeTable = this;
  14. const reactData = $xeTable;
  15. Object.assign(reactData.ctxMenuStore, {
  16. visible: false,
  17. selected: null,
  18. selectChild: null,
  19. showChild: false
  20. });
  21. return $xeTable.$nextTick();
  22. },
  23. // 处理菜单的移动
  24. moveCtxMenu(evnt, ctxMenuStore, property, hasOper, operRest, menuList) {
  25. const $xeTable = this;
  26. let selectItem;
  27. const selectIndex = XEUtils.findIndexOf(menuList, item => ctxMenuStore[property] === item);
  28. if (hasOper) {
  29. if (operRest && hasChildrenList(ctxMenuStore.selected)) {
  30. ctxMenuStore.showChild = true;
  31. }
  32. else {
  33. ctxMenuStore.showChild = false;
  34. ctxMenuStore.selectChild = null;
  35. }
  36. }
  37. else if (globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_UP)) {
  38. for (let len = selectIndex - 1; len >= 0; len--) {
  39. if (menuList[len].visible !== false) {
  40. selectItem = menuList[len];
  41. break;
  42. }
  43. }
  44. ctxMenuStore[property] = selectItem || menuList[menuList.length - 1];
  45. }
  46. else if (globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_DOWN)) {
  47. for (let index = selectIndex + 1; index < menuList.length; index++) {
  48. if (menuList[index].visible !== false) {
  49. selectItem = menuList[index];
  50. break;
  51. }
  52. }
  53. ctxMenuStore[property] = selectItem || menuList[0];
  54. }
  55. else if (ctxMenuStore[property] && (globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ENTER) || globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.SPACEBAR))) {
  56. $xeTable.ctxMenuLinkEvent(evnt, ctxMenuStore[property]);
  57. }
  58. },
  59. /**
  60. * 快捷菜单事件处理
  61. */
  62. handleGlobalContextmenuEvent(evnt) {
  63. const $xeTable = this;
  64. const $xeGrid = $xeTable.$xeGrid;
  65. const $xeGantt = $xeTable.$xeGantt;
  66. const props = $xeTable;
  67. const reactData = $xeTable;
  68. const internalData = $xeTable;
  69. const { xID } = $xeTable;
  70. const { mouseConfig, menuConfig } = props;
  71. const { editStore, ctxMenuStore } = reactData;
  72. const { visibleColumn } = internalData;
  73. const tableFilter = $xeTable.$refs.refTableFilter;
  74. const tableMenu = $xeTable.$refs.refTableMenu;
  75. const mouseOpts = $xeTable.computeMouseOpts;
  76. const menuOpts = $xeTable.computeMenuOpts;
  77. const el = $xeTable.$refs.refElem;
  78. const { selected } = editStore;
  79. const layoutList = ['header', 'body', 'footer'];
  80. if (isEnableConf(menuConfig)) {
  81. if (ctxMenuStore.visible && tableMenu && getEventTargetNode(evnt, tableMenu.$el).flag) {
  82. evnt.preventDefault();
  83. return;
  84. }
  85. if (internalData._keyCtx) {
  86. const type = 'body';
  87. const params = { source: 'table', type, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, keyboard: true, columns: visibleColumn.slice(0), $event: evnt };
  88. // 如果开启单元格区域
  89. if (mouseConfig && mouseOpts.area) {
  90. const activeArea = $xeTable.getActiveCellArea();
  91. if (activeArea && activeArea.row && activeArea.column) {
  92. params.row = activeArea.row;
  93. params.column = activeArea.column;
  94. $xeTable.handleOpenMenuEvent(evnt, type, params);
  95. return;
  96. }
  97. }
  98. else if (mouseConfig && mouseOpts.selected) {
  99. // 如果启用键盘导航且已选中单元格
  100. if (selected.row && selected.column) {
  101. params.row = selected.row;
  102. params.column = selected.column;
  103. $xeTable.handleOpenMenuEvent(evnt, type, params);
  104. return;
  105. }
  106. }
  107. }
  108. // 分别匹配表尾、内容、表尾的快捷菜单
  109. for (let index = 0; index < layoutList.length; index++) {
  110. const layout = layoutList[index];
  111. const columnTargetNode = getEventTargetNode(evnt, el, `vxe-${layout}--column`, (target) => {
  112. // target=td|th,直接向上找 table 去匹配即可
  113. return target.parentNode.parentNode.parentNode.getAttribute('xid') === xID;
  114. });
  115. const params = { source: 'table', type: layout, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, columns: visibleColumn.slice(0), $event: evnt };
  116. if (columnTargetNode.flag) {
  117. const cell = columnTargetNode.targetElem;
  118. const columnNodeRest = $xeTable.getColumnNode(cell);
  119. const column = columnNodeRest ? columnNodeRest.item : null;
  120. let typePrefix = `${layout}-`;
  121. if (column) {
  122. Object.assign(params, { column, columnIndex: $xeTable.getColumnIndex(column), cell });
  123. }
  124. if (layout === 'body') {
  125. const rowNodeRest = $xeTable.getRowNode(cell.parentNode);
  126. const row = rowNodeRest ? rowNodeRest.item : null;
  127. typePrefix = '';
  128. if (row) {
  129. params.row = row;
  130. params.rowIndex = $xeTable.getRowIndex(row);
  131. }
  132. }
  133. const eventType = `${typePrefix}cell-menu`;
  134. $xeTable.handleOpenMenuEvent(evnt, layout, params);
  135. // 在 v4 中废弃事件 cell-context-menu、header-cell-context-menu、footer-cell-context-menu
  136. if ($xeTable.$listeners[`${typePrefix}cell-context-menu`]) {
  137. warnLog('vxe.error.delEvent', [`${typePrefix}cell-context-menu`, `${typePrefix}cell-menu`]);
  138. $xeTable.dispatchEvent(`${typePrefix}cell-context-menu`, params, evnt);
  139. }
  140. else {
  141. $xeTable.dispatchEvent(eventType, params, evnt);
  142. }
  143. return;
  144. }
  145. else if (getEventTargetNode(evnt, $xeTable.$el, `vxe-table--${layout}-wrapper`, target => target.getAttribute('xid') === xID).flag) {
  146. if (menuOpts.trigger === 'cell') {
  147. evnt.preventDefault();
  148. }
  149. else {
  150. $xeTable.handleOpenMenuEvent(evnt, layout, params);
  151. }
  152. return;
  153. }
  154. }
  155. }
  156. if (tableFilter && !getEventTargetNode(evnt, tableFilter.$el).flag) {
  157. $xeTable.closeFilter();
  158. }
  159. $xeTable.closeMenu();
  160. },
  161. /**
  162. * 显示快捷菜单
  163. */
  164. handleOpenMenuEvent(evnt, type, params) {
  165. const $xeTable = this;
  166. const reactData = $xeTable;
  167. const internalData = $xeTable;
  168. const { ctxMenuStore } = reactData;
  169. const isContentMenu = $xeTable.computeIsContentMenu;
  170. const menuOpts = $xeTable.computeMenuOpts;
  171. const config = menuOpts[type];
  172. const { transfer, visibleMethod } = menuOpts;
  173. if (config) {
  174. const { options, disabled } = config;
  175. if (disabled) {
  176. evnt.preventDefault();
  177. }
  178. else if (isContentMenu && options && options.length) {
  179. params.options = options;
  180. $xeTable.preventEvent(evnt, 'event.showMenu', params, () => {
  181. if (!visibleMethod || visibleMethod(params)) {
  182. evnt.preventDefault();
  183. $xeTable.updateZindex();
  184. const el = $xeTable.$refs.refElem;
  185. const tableRect = el.getBoundingClientRect();
  186. const { scrollTop, scrollLeft, visibleHeight, visibleWidth } = getDomNode();
  187. let top = evnt.clientY - tableRect.y;
  188. let left = evnt.clientX - tableRect.x;
  189. if (transfer) {
  190. top = evnt.clientY + scrollTop;
  191. left = evnt.clientX + scrollLeft;
  192. }
  193. const handleVisible = () => {
  194. internalData._currMenuParams = params;
  195. Object.assign(ctxMenuStore, {
  196. visible: true,
  197. list: options,
  198. selected: null,
  199. selectChild: null,
  200. showChild: false,
  201. style: {
  202. zIndex: internalData.tZindex,
  203. top: `${top}px`,
  204. left: `${left}px`
  205. }
  206. });
  207. $xeTable.$nextTick(() => {
  208. const tableMenu = $xeTable.$refs.refTableMenu;
  209. const ctxElem = tableMenu.$el;
  210. const clientHeight = ctxElem.clientHeight;
  211. const clientWidth = ctxElem.clientWidth;
  212. const { boundingTop, boundingLeft } = getAbsolutePos(ctxElem);
  213. const offsetTop = boundingTop + clientHeight - visibleHeight;
  214. const offsetLeft = boundingLeft + clientWidth - visibleWidth;
  215. if (offsetTop > -10) {
  216. ctxMenuStore.style.top = `${Math.max(scrollTop + 2, top - clientHeight - 2)}px`;
  217. }
  218. if (offsetLeft > -10) {
  219. ctxMenuStore.style.left = `${Math.max(scrollLeft + 2, left - clientWidth - 2)}px`;
  220. }
  221. });
  222. };
  223. const { keyboard, row, column } = params;
  224. if (keyboard && row && column) {
  225. $xeTable.scrollToRow(row, column).then(() => {
  226. const cell = $xeTable.getCellElement(row, column);
  227. if (cell) {
  228. const { boundingTop, boundingLeft } = getAbsolutePos(cell);
  229. top = boundingTop + scrollTop + Math.floor(cell.offsetHeight / 2);
  230. left = boundingLeft + scrollLeft + Math.floor(cell.offsetWidth / 2);
  231. }
  232. handleVisible();
  233. });
  234. }
  235. else {
  236. handleVisible();
  237. }
  238. }
  239. else {
  240. $xeTable.closeMenu();
  241. }
  242. });
  243. }
  244. }
  245. $xeTable.closeFilter();
  246. },
  247. ctxMenuMouseoverEvent(evnt, item, child) {
  248. const $xeTable = this;
  249. const reactData = $xeTable;
  250. const menuElem = evnt.currentTarget;
  251. const { ctxMenuStore } = reactData;
  252. evnt.preventDefault();
  253. evnt.stopPropagation();
  254. ctxMenuStore.selected = item;
  255. ctxMenuStore.selectChild = child;
  256. if (!child) {
  257. ctxMenuStore.showChild = hasChildrenList(item);
  258. if (ctxMenuStore.showChild) {
  259. $xeTable.$nextTick(() => {
  260. const childWrapperElem = menuElem.nextElementSibling;
  261. if (childWrapperElem) {
  262. const { boundingTop, boundingLeft, visibleHeight, visibleWidth } = getAbsolutePos(menuElem);
  263. const posTop = boundingTop + menuElem.offsetHeight;
  264. const posLeft = boundingLeft + menuElem.offsetWidth;
  265. let left = '';
  266. let right = '';
  267. // 是否超出右侧
  268. if (posLeft + childWrapperElem.offsetWidth > visibleWidth - 10) {
  269. left = 'auto';
  270. right = `${menuElem.offsetWidth}px`;
  271. }
  272. // 是否超出底部
  273. let top = '';
  274. let bottom = '';
  275. if (posTop + childWrapperElem.offsetHeight > visibleHeight - 10) {
  276. top = 'auto';
  277. bottom = '0';
  278. }
  279. childWrapperElem.style.left = left;
  280. childWrapperElem.style.right = right;
  281. childWrapperElem.style.top = top;
  282. childWrapperElem.style.bottom = bottom;
  283. }
  284. });
  285. }
  286. }
  287. },
  288. ctxMenuMouseoutEvent(evnt, item) {
  289. const $xeTable = this;
  290. const reactData = $xeTable;
  291. const { ctxMenuStore } = reactData;
  292. if (!item.children) {
  293. ctxMenuStore.selected = null;
  294. }
  295. ctxMenuStore.selectChild = null;
  296. },
  297. /**
  298. * 快捷菜单点击事件
  299. */
  300. ctxMenuLinkEvent(evnt, menu) {
  301. const $xeTable = this;
  302. const $xeGrid = $xeTable.$xeGrid;
  303. const $xeGantt = $xeTable.$xeGantt;
  304. const internalData = $xeTable;
  305. // 如果一级菜单有配置 code 则允许点击,否则不能点击
  306. if (!menu.disabled && (menu.code || !menu.children || !menu.children.length)) {
  307. const gMenuOpts = menus.get(menu.code);
  308. const params = Object.assign({}, internalData._currMenuParams, { menu, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, $event: evnt });
  309. if (gMenuOpts && gMenuOpts.menuMethod) {
  310. gMenuOpts.menuMethod(params, evnt);
  311. }
  312. // 在 v4 中废弃事件 context-menu-click
  313. if ($xeTable.$listeners['context-menu-click']) {
  314. warnLog('vxe.error.delEvent', ['context-menu-click', 'menu-click']);
  315. $xeTable.dispatchEvent('context-menu-click', params, evnt);
  316. }
  317. else {
  318. $xeTable.dispatchEvent('menu-click', params, evnt);
  319. }
  320. $xeTable.closeMenu();
  321. }
  322. }
  323. }
  324. };