mixin.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import XEUtils from 'xe-utils'
  2. import { getRefElem } from '../../src/util'
  3. import { getAbsolutePos, addClass, removeClass, hasControlKey } from '../../../ui/src/dom'
  4. import type { VxeTableConstructor, VxeTablePrivateMethods, VxeTableDefines, TableReactData, TableInternalData } from '../../../../types'
  5. function getCheckboxRangeRows ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, evnt: MouseEvent, params: any, targetTrElem: HTMLElement, trRect: DOMRect, offsetClientTop: number, moveRange: number) {
  6. const props = $xeTable
  7. const reactData = $xeTable as unknown as TableReactData
  8. const internalData = $xeTable as unknown as TableInternalData
  9. const { showOverflow } = props
  10. const { fullAllDataRowIdData, isResizeCellHeight } = internalData
  11. const rowOpts = $xeTable.computeRowOpts
  12. const cellOpts = $xeTable.computeCellOpts
  13. const defaultRowHeight = $xeTable.computeDefaultRowHeight
  14. const { row } = params
  15. let countHeight = 0
  16. let rangeRows: any[] = []
  17. let moveSize = 0
  18. const isDown = moveRange > 0
  19. const { scrollYLoad } = reactData
  20. const { afterFullData } = internalData
  21. if (isDown) {
  22. moveSize = offsetClientTop + moveRange
  23. } else {
  24. moveSize = (trRect.height - offsetClientTop) + Math.abs(moveRange)
  25. }
  26. if (scrollYLoad) {
  27. const _rowIndex = $xeTable.getVTRowIndex(row)
  28. const isCustomCellHeight = isResizeCellHeight || cellOpts.height || rowOpts.height
  29. if (!isCustomCellHeight && showOverflow) {
  30. if (isDown) {
  31. rangeRows = afterFullData.slice(_rowIndex, _rowIndex + Math.ceil(moveSize / defaultRowHeight))
  32. } else {
  33. rangeRows = afterFullData.slice(_rowIndex - Math.floor(moveSize / defaultRowHeight), _rowIndex + 1)
  34. }
  35. } else {
  36. if (isDown) {
  37. for (let i = _rowIndex; i < afterFullData.length; i++) {
  38. const item = afterFullData[i]
  39. const rowid = $xeTable.getRowid(item)
  40. const rowRest = fullAllDataRowIdData[rowid] || {}
  41. countHeight += rowRest.resizeHeight || cellOpts.height || rowOpts.height || rowRest.height || defaultRowHeight
  42. rangeRows.push(item)
  43. if (countHeight > moveSize) {
  44. return rangeRows
  45. }
  46. }
  47. } else {
  48. for (let len = _rowIndex; len >= 0; len--) {
  49. const item = afterFullData[len]
  50. const rowid = $xeTable.getRowid(item)
  51. const rowRest = fullAllDataRowIdData[rowid] || {}
  52. countHeight += rowRest.resizeHeight || cellOpts.height || rowOpts.height || rowRest.height || defaultRowHeight
  53. rangeRows.push(item)
  54. if (countHeight > moveSize) {
  55. return rangeRows
  56. }
  57. }
  58. }
  59. }
  60. } else {
  61. const siblingProp = isDown ? 'next' : 'previous'
  62. while (targetTrElem && countHeight < moveSize) {
  63. const rowNodeRest = $xeTable.getRowNode(targetTrElem)
  64. if (rowNodeRest) {
  65. rangeRows.push(rowNodeRest.item)
  66. countHeight += targetTrElem.offsetHeight
  67. targetTrElem = targetTrElem[`${siblingProp}ElementSibling`] as HTMLElement
  68. }
  69. }
  70. }
  71. return rangeRows
  72. }
  73. function handleMoveSelected ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, evnt: any, args: any, isLeftArrow: boolean, isUpArrow: boolean, isRightArrow: boolean, isDwArrow: boolean) {
  74. const internalData = $xeTable as unknown as TableInternalData
  75. const { afterFullData, visibleColumn } = internalData
  76. const params = Object.assign({}, args)
  77. const _rowIndex = $xeTable.getVTRowIndex(params.row)
  78. const _columnIndex = $xeTable.getVTColumnIndex(params.column)
  79. evnt.preventDefault()
  80. if (isUpArrow && _rowIndex > 0) {
  81. // 移动到上一行
  82. params.rowIndex = _rowIndex - 1
  83. params.row = afterFullData[params.rowIndex]
  84. } else if (isDwArrow && _rowIndex < afterFullData.length - 1) {
  85. // 移动到下一行
  86. params.rowIndex = _rowIndex + 1
  87. params.row = afterFullData[params.rowIndex]
  88. } else if (isLeftArrow && _columnIndex) {
  89. // 移动到左侧单元格
  90. params.columnIndex = _columnIndex - 1
  91. params.column = visibleColumn[params.columnIndex]
  92. } else if (isRightArrow && _columnIndex < visibleColumn.length - 1) {
  93. // 移动到右侧单元格
  94. params.columnIndex = _columnIndex + 1
  95. params.column = visibleColumn[params.columnIndex]
  96. }
  97. $xeTable.scrollToRow(params.row, params.column).then(() => {
  98. params.cell = $xeTable.getCellElement(params.row, params.column)
  99. $xeTable.handleSelected(params, evnt)
  100. })
  101. return params
  102. }
  103. function handleCheckboxRangeEvent ($xeTable: VxeTableConstructor & VxeTablePrivateMethods, evnt: any, params: any) {
  104. const internalData = $xeTable as unknown as TableInternalData
  105. const { elemStore } = internalData
  106. const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
  107. const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
  108. const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
  109. const { column, cell } = params
  110. if (column.type === 'checkbox') {
  111. let bodyWrapperElem = bodyScrollElem as HTMLElement
  112. if (leftScrollElem && column.fixed === 'left') {
  113. bodyWrapperElem = leftScrollElem
  114. } else if (rightScrollElem && column.fixed === 'right') {
  115. bodyWrapperElem = rightScrollElem
  116. }
  117. if (!bodyWrapperElem) {
  118. return
  119. }
  120. const bodyRect = bodyWrapperElem.getBoundingClientRect()
  121. const el = $xeTable.$refs.refElem as HTMLDivElement
  122. const disX = evnt.clientX
  123. const disY = evnt.clientY
  124. const checkboxRangeElem = bodyWrapperElem.querySelector('.vxe-table--checkbox-range') as HTMLElement
  125. const trElem = cell.parentNode
  126. const selectRecords = $xeTable.getCheckboxRecords()
  127. let lastRangeRows = []
  128. const marginSize = 1
  129. const startTop = evnt.clientY - bodyRect.y + bodyWrapperElem.scrollTop
  130. const startLeft = evnt.clientX - bodyRect.x + bodyWrapperElem.scrollLeft
  131. const startScrollTop = bodyWrapperElem.scrollTop
  132. const rowHeight = trElem.offsetHeight
  133. const trRect = trElem.getBoundingClientRect()
  134. const offsetClientTop = disY - trRect.y
  135. let mouseScrollTimeout: any = null
  136. let isMouseScrollDown: any = false
  137. let mouseScrollSpaceSize = 1
  138. const triggerEvent = (type: 'change' | 'start' | 'end', evnt: Event) => {
  139. $xeTable.dispatchEvent(`checkbox-range-${type}`, {
  140. records: () => $xeTable.getCheckboxRecords(),
  141. reserves: () => $xeTable.getCheckboxReserveRecords()
  142. }, evnt)
  143. }
  144. const handleChecked = (evnt: any) => {
  145. const { clientX, clientY } = evnt
  146. const offsetLeft = clientX - disX
  147. const offsetTop = clientY - disY + (bodyWrapperElem.scrollTop - startScrollTop)
  148. let rangeHeight = Math.abs(offsetTop)
  149. let rangeWidth = Math.abs(offsetLeft)
  150. let rangeTop = startTop
  151. let rangeLeft = startLeft
  152. if (offsetTop < marginSize) {
  153. // 向上
  154. rangeTop += offsetTop
  155. if (rangeTop < marginSize) {
  156. rangeTop = marginSize
  157. rangeHeight = startTop
  158. }
  159. } else {
  160. // 向下
  161. rangeHeight = Math.min(rangeHeight, bodyWrapperElem.scrollHeight - startTop - marginSize)
  162. }
  163. if (offsetLeft < marginSize) {
  164. // 向左
  165. rangeLeft += offsetLeft
  166. if (rangeWidth > startLeft) {
  167. rangeLeft = marginSize
  168. rangeWidth = startLeft
  169. }
  170. } else {
  171. // 向右
  172. rangeWidth = Math.min(rangeWidth, bodyWrapperElem.clientWidth - startLeft - marginSize)
  173. }
  174. checkboxRangeElem.style.height = `${rangeHeight}px`
  175. checkboxRangeElem.style.width = `${rangeWidth}px`
  176. checkboxRangeElem.style.left = `${rangeLeft}px`
  177. checkboxRangeElem.style.top = `${rangeTop}px`
  178. checkboxRangeElem.style.display = 'block'
  179. const rangeRows = getCheckboxRangeRows($xeTable, evnt, params, trElem, trRect, offsetClientTop, offsetTop < marginSize ? -rangeHeight : rangeHeight)
  180. // 至少滑动 10px 才能有效匹配
  181. if (rangeHeight > 10 && rangeRows.length !== lastRangeRows.length) {
  182. const isControlKey = hasControlKey(evnt)
  183. lastRangeRows = rangeRows
  184. if (isControlKey) {
  185. rangeRows.forEach((row: any) => {
  186. $xeTable.handleBatchSelectRows([row], selectRecords.indexOf(row) === -1)
  187. })
  188. } else {
  189. $xeTable.setAllCheckboxRow(false)
  190. $xeTable.handleCheckedCheckboxRow(rangeRows, true, false)
  191. }
  192. triggerEvent('change', evnt)
  193. }
  194. }
  195. // 停止鼠标滚动
  196. const stopMouseScroll = () => {
  197. clearTimeout(mouseScrollTimeout)
  198. mouseScrollTimeout = null
  199. }
  200. // 开始鼠标滚动
  201. const startMouseScroll = (evnt: any) => {
  202. stopMouseScroll()
  203. mouseScrollTimeout = setTimeout(() => {
  204. if (mouseScrollTimeout) {
  205. const { scrollLeft, scrollTop, clientHeight, scrollHeight } = bodyWrapperElem
  206. const topSize = Math.ceil(mouseScrollSpaceSize * 50 / rowHeight)
  207. if (isMouseScrollDown) {
  208. if (scrollTop + clientHeight < scrollHeight) {
  209. $xeTable.scrollTo(scrollLeft, scrollTop + topSize)
  210. startMouseScroll(evnt)
  211. handleChecked(evnt)
  212. } else {
  213. stopMouseScroll()
  214. }
  215. } else {
  216. if (scrollTop) {
  217. $xeTable.scrollTo(scrollLeft, scrollTop - topSize)
  218. startMouseScroll(evnt)
  219. handleChecked(evnt)
  220. } else {
  221. stopMouseScroll()
  222. }
  223. }
  224. }
  225. }, 50)
  226. }
  227. addClass(el, 'drag--range')
  228. document.onmousemove = evnt => {
  229. evnt.preventDefault()
  230. evnt.stopPropagation()
  231. const { clientY } = evnt
  232. const { boundingTop } = getAbsolutePos(bodyWrapperElem)
  233. // 如果超过可视区,触发滚动
  234. if (clientY < boundingTop) {
  235. isMouseScrollDown = false
  236. mouseScrollSpaceSize = boundingTop - clientY
  237. if (!mouseScrollTimeout) {
  238. startMouseScroll(evnt)
  239. }
  240. } else if (clientY > boundingTop + bodyWrapperElem.clientHeight) {
  241. isMouseScrollDown = true
  242. mouseScrollSpaceSize = clientY - boundingTop - bodyWrapperElem.clientHeight
  243. if (!mouseScrollTimeout) {
  244. startMouseScroll(evnt)
  245. }
  246. } else if (mouseScrollTimeout) {
  247. stopMouseScroll()
  248. }
  249. handleChecked(evnt)
  250. }
  251. document.onmouseup = (evnt) => {
  252. stopMouseScroll()
  253. removeClass(el, 'drag--range')
  254. checkboxRangeElem.removeAttribute('style')
  255. document.onmousemove = null
  256. document.onmouseup = null
  257. triggerEvent('end', evnt)
  258. }
  259. triggerEvent('start', evnt)
  260. }
  261. }
  262. export default {
  263. methods: {
  264. // 处理 Tab 键移动
  265. moveTabSelected (args: any, isLeft: any, evnt: any) {
  266. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  267. const props = $xeTable
  268. const internalData = $xeTable as unknown as TableInternalData
  269. const { editConfig } = props
  270. const { afterFullData, visibleColumn } = internalData
  271. const editOpts = $xeTable.computeEditOpts
  272. const rowOpts = $xeTable.computeRowOpts
  273. const currentRowOpts = $xeTable.computeCurrentRowOpts
  274. const columnOpts = $xeTable.computeColumnOpts
  275. const currentColumnOpts = $xeTable.computeCurrentColumnOpts
  276. let targetRow: any
  277. let targetRowIndex: any
  278. let targetColumnIndex: any
  279. const params = Object.assign({}, args)
  280. const _rowIndex = $xeTable.getVTRowIndex(params.row)
  281. const _columnIndex = $xeTable.getVTColumnIndex(params.column)
  282. evnt.preventDefault()
  283. if (isLeft) {
  284. // 向左
  285. if (_columnIndex <= 0) {
  286. // 如果已经是第一列,则移动到上一行
  287. if (_rowIndex > 0) {
  288. targetRowIndex = _rowIndex - 1
  289. targetRow = afterFullData[targetRowIndex]
  290. targetColumnIndex = visibleColumn.length - 1
  291. }
  292. } else {
  293. targetColumnIndex = _columnIndex - 1
  294. }
  295. } else {
  296. if (_columnIndex >= visibleColumn.length - 1) {
  297. // 如果已经是第一列,则移动到上一行
  298. if (_rowIndex < afterFullData.length - 1) {
  299. targetRowIndex = _rowIndex + 1
  300. targetRow = afterFullData[targetRowIndex]
  301. targetColumnIndex = 0
  302. }
  303. } else {
  304. targetColumnIndex = _columnIndex + 1
  305. }
  306. }
  307. const targetColumn = visibleColumn[targetColumnIndex]
  308. if (targetColumn) {
  309. if (targetRow) {
  310. params.rowIndex = targetRowIndex
  311. params.row = targetRow
  312. } else {
  313. params.rowIndex = _rowIndex
  314. }
  315. params.columnIndex = targetColumnIndex
  316. params.column = targetColumn
  317. params.cell = $xeTable.getCellElement(params.row, params.column)
  318. if (rowOpts.isCurrent && currentRowOpts.isFollowSelected) {
  319. $xeTable.triggerCurrentRowEvent(evnt, params)
  320. }
  321. if (columnOpts.isCurrent && currentColumnOpts.isFollowSelected) {
  322. $xeTable.triggerCurrentColumnEvent(evnt, params)
  323. }
  324. if (editConfig) {
  325. if (editOpts.trigger === 'click' || editOpts.trigger === 'dblclick') {
  326. if (editOpts.mode === 'row') {
  327. $xeTable.handleEdit(params, evnt)
  328. } else {
  329. $xeTable.scrollToRow(params.row, params.column)
  330. .then(() => {
  331. $xeTable.handleSelected(params, evnt)
  332. })
  333. }
  334. }
  335. } else {
  336. $xeTable.scrollToRow(params.row, params.column)
  337. .then(() => {
  338. $xeTable.handleSelected(params, evnt)
  339. })
  340. }
  341. }
  342. },
  343. // 处理当前行方向键移动
  344. moveCurrentRow (isUpArrow: boolean, isDwArrow: boolean, evnt: any) {
  345. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  346. const props = $xeTable
  347. const reactData = $xeTable as unknown as TableReactData
  348. const internalData = $xeTable as unknown as TableInternalData
  349. const { treeConfig } = props
  350. const { currentRow } = reactData
  351. const { afterFullData } = internalData
  352. const treeOpts = $xeTable.computeTreeOpts
  353. const childrenField = treeOpts.children || treeOpts.childrenField
  354. let targetRow
  355. if (currentRow) {
  356. if (treeConfig) {
  357. const { index, items } = XEUtils.findTree(afterFullData, item => item === currentRow, { children: childrenField })
  358. if (isUpArrow && index > 0) {
  359. targetRow = items[index - 1]
  360. } else if (isDwArrow && index < items.length - 1) {
  361. targetRow = items[index + 1]
  362. }
  363. } else {
  364. const _rowIndex = $xeTable.getVTRowIndex(currentRow)
  365. if (isUpArrow && _rowIndex > 0) {
  366. targetRow = afterFullData[_rowIndex - 1]
  367. } else if (isDwArrow && _rowIndex < afterFullData.length - 1) {
  368. targetRow = afterFullData[_rowIndex + 1]
  369. }
  370. }
  371. } else {
  372. targetRow = afterFullData[0]
  373. }
  374. if (targetRow) {
  375. evnt.preventDefault()
  376. const params = {
  377. $table: $xeTable,
  378. row: targetRow,
  379. rowIndex: $xeTable.getRowIndex(targetRow),
  380. $rowIndex: $xeTable.getVMRowIndex(targetRow)
  381. }
  382. $xeTable.scrollToRow(targetRow)
  383. .then(() => $xeTable.triggerCurrentRowEvent(evnt, params))
  384. }
  385. },
  386. // 处理当前列方向键移动
  387. moveCurrentColumn (isLeftArrow: boolean, isRightArrow: boolean, evnt: any) {
  388. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  389. const reactData = $xeTable as unknown as TableReactData
  390. const internalData = $xeTable as unknown as TableInternalData
  391. const { currentColumn } = reactData
  392. const { visibleColumn } = internalData
  393. let targetCol: VxeTableDefines.ColumnInfo | null = null
  394. if (currentColumn) {
  395. const _columnIndex = $xeTable.getVTColumnIndex(currentColumn)
  396. if (isLeftArrow && _columnIndex > 0) {
  397. targetCol = visibleColumn[_columnIndex - 1]
  398. } else if (isRightArrow && _columnIndex < visibleColumn.length - 1) {
  399. targetCol = visibleColumn[_columnIndex + 1]
  400. }
  401. } else {
  402. targetCol = visibleColumn[0]
  403. }
  404. if (targetCol) {
  405. evnt.preventDefault()
  406. const params = {
  407. $table: $xeTable,
  408. column: targetCol,
  409. columnIndex: $xeTable.getColumnIndex(targetCol),
  410. $columnIndex: $xeTable.getVMColumnIndex(targetCol)
  411. }
  412. $xeTable.scrollToColumn(targetCol)
  413. .then(() => $xeTable.triggerCurrentColumnEvent(evnt, params))
  414. }
  415. },
  416. // 处理可编辑方向键移动
  417. moveArrowSelected (args: any, isLeftArrow: boolean, isUpArrow: boolean, isRightArrow: boolean, isDwArrow: boolean, evnt: any) {
  418. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  419. const props = $xeTable
  420. const { highlightCurrentRow, highlightCurrentColumn } = props
  421. const rowOpts = $xeTable.computeRowOpts
  422. const currentRowOpts = $xeTable.computeCurrentRowOpts
  423. const columnOpts = $xeTable.computeColumnOpts
  424. const currentColumnOpts = $xeTable.computeCurrentColumnOpts
  425. const params = handleMoveSelected($xeTable, evnt, args, isLeftArrow, isUpArrow, isRightArrow, isDwArrow)
  426. if (rowOpts.isCurrent || highlightCurrentRow) {
  427. if (currentRowOpts.isFollowSelected) {
  428. $xeTable.triggerCurrentRowEvent(evnt, params)
  429. } else {
  430. // 当前行按键上下移动
  431. if ((isUpArrow || isDwArrow) && (rowOpts.isCurrent || highlightCurrentRow)) {
  432. $xeTable.moveCurrentRow(isUpArrow, isDwArrow, evnt)
  433. }
  434. }
  435. }
  436. if (columnOpts.isCurrent || highlightCurrentColumn) {
  437. if (currentColumnOpts.isFollowSelected) {
  438. $xeTable.triggerCurrentColumnEvent(evnt, params)
  439. } else {
  440. // 当前行按键左右移动
  441. if ((isLeftArrow || isRightArrow) && (columnOpts.isCurrent || highlightCurrentColumn)) {
  442. $xeTable.moveCurrentColumn(isLeftArrow, isRightArrow, evnt)
  443. }
  444. }
  445. }
  446. },
  447. moveEnterSelected (args: any, isLeftArrow: boolean, isUpArrow: boolean, isRightArrow: boolean, isDwArrow: boolean, evnt: any) {
  448. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  449. const props = $xeTable
  450. const { highlightCurrentRow, highlightCurrentColumn } = props
  451. const rowOpts = $xeTable.computeRowOpts
  452. const currentRowOpts = $xeTable.computeCurrentRowOpts
  453. const columnOpts = $xeTable.computeColumnOpts
  454. const currentColumnOpts = $xeTable.computeCurrentColumnOpts
  455. const params = handleMoveSelected($xeTable, evnt, args, isLeftArrow, isUpArrow, isRightArrow, isDwArrow)
  456. if (((rowOpts.isCurrent || highlightCurrentRow) && currentRowOpts.isFollowSelected)) {
  457. $xeTable.triggerCurrentRowEvent(evnt, params)
  458. }
  459. if ((columnOpts.isCurrent || highlightCurrentColumn) && currentColumnOpts.isFollowSelected) {
  460. $xeTable.triggerCurrentColumnEvent(evnt, params)
  461. }
  462. },
  463. // 处理可编辑方向键移动
  464. moveSelected (args: any, isLeftArrow: boolean, isUpArrow: boolean, isRightArrow: boolean, isDwArrow: boolean, evnt: any) {
  465. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  466. handleMoveSelected($xeTable, evnt, args, isLeftArrow, isUpArrow, isRightArrow, isDwArrow)
  467. },
  468. handleCellMousedownEvent (evnt: any, params: any) {
  469. const $xeTable = this as VxeTableConstructor & VxeTablePrivateMethods
  470. const props = $xeTable
  471. const { editConfig, checkboxConfig, mouseConfig } = props
  472. const checkboxOpts = $xeTable.computeCheckboxOpts
  473. const mouseOpts = $xeTable.computeMouseOpts
  474. const editOpts = $xeTable.computeEditOpts
  475. if (mouseConfig && mouseOpts.area && $xeTable.triggerCellAreaModnEvent) {
  476. return $xeTable.triggerCellAreaModnEvent(evnt, params)
  477. } else {
  478. if (checkboxConfig && checkboxOpts.range) {
  479. handleCheckboxRangeEvent($xeTable, evnt, params)
  480. }
  481. if (mouseConfig && mouseOpts.selected) {
  482. if (!editConfig || editOpts.mode === 'cell') {
  483. $xeTable.handleSelected(params, evnt)
  484. }
  485. }
  486. }
  487. }
  488. }
  489. }