grid.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. import _ from 'lodash'
  2. import {grid} from '../Defaults'
  3. import {baseConfig} from "./base";
  4. import {lookupFn, lookupScope} from "../lib/lib";
  5. import LAY_EXCEL from "lay-excel"
  6. import {
  7. disabled,
  8. fieldLabel,
  9. gravity, height, metaId,
  10. PropertyDescriptionTable,
  11. tooltip,
  12. value, width,
  13. YvBase
  14. } from "../PropertyDescriptionTable";
  15. import {PropertyDescription} from "../PropertyDescription";
  16. import {gridInvokeBuild} from "./stores";
  17. import {msg} from 'src/message';
  18. const defaultGrid = grid
  19. export default function () {
  20. Ext.define('Yvan.Grid', {
  21. extend: 'Ext.grid.Panel',
  22. xtype: 'yvgrid',
  23. constructor(config) {
  24. const me = this
  25. const {dataSource} = config
  26. if (!window["IS_DESIGN_MODE"]) {
  27. this.columnConfigCacheKey = this.makeColumnConfigCacheKey(config)
  28. if (Array.isArray(config.columns) && config.columns.length > 0) {
  29. const cacheData = this.getColumnConfigCache()
  30. if (Array.isArray(cacheData) && cacheData.length > 0) {
  31. const newColumns = []
  32. for (let j = 0; j < cacheData.length; j++) {
  33. const itData = cacheData[j]
  34. for (let i = 0; i < config.columns.length; i++) {
  35. const column = config.columns[i]
  36. if (itData.dataIndex === column.dataIndex) {
  37. if (itData.width) {
  38. column.width = itData.width
  39. }
  40. column.hidden = itData.hidden
  41. column.locked = itData.locked
  42. newColumns.push(column)
  43. break
  44. }
  45. }
  46. }
  47. config.columns = newColumns
  48. }
  49. }
  50. }
  51. const newConfig = _.defaultsDeep({
  52. // 强制性属性
  53. }, baseConfig(config, 'row-item'), config, grid)
  54. // 在面板上的组件
  55. const scope = newConfig.$initParent.yvanScope || newConfig.$initParent.lookupReferenceHolder().yvanScope;
  56. const buttons = []
  57. const {getRowClass} = newConfig
  58. if (typeof getRowClass === 'string' && (
  59. _.startsWith(getRowClass, "scope.") ||
  60. _.startsWith(getRowClass, "system."))
  61. ) {
  62. const fn = lookupFn(scope, getRowClass)
  63. _.set(newConfig, 'viewConfig.getRowClass', fn)
  64. }
  65. if (!newConfig.hideExport) {
  66. buttons.push({
  67. xtype: 'button',
  68. tooltip: '导出Excel',
  69. iconCls: 'x-fa fa-download',
  70. listeners: {
  71. click: this.exportExcel
  72. },
  73. })
  74. }
  75. if (!newConfig.hideAutoSize) {
  76. buttons.push({
  77. xtype: 'button',
  78. iconCls: 'x-fa fa-text-width',
  79. tooltip: '自适应宽度',
  80. listeners: {
  81. click: this.autoSizeColumns
  82. }
  83. })
  84. }
  85. if (!newConfig.hideClearFilter) {
  86. buttons.push({
  87. xtype: 'button',
  88. tooltip: '清空筛选',
  89. iconCls: 'x-fa fa-filter',
  90. handler: this.clearFilter
  91. })
  92. }
  93. if (!newConfig.hideSaveGridUIConfig) {
  94. buttons.push({
  95. xtype: 'button',
  96. tooltip: '保存表格自定义配置',
  97. iconCls: 'x-fa fa-cogs',
  98. handler: this.saveGridUIConfig
  99. })
  100. }
  101. if (!newConfig.hideClearGridUIConfig) {
  102. buttons.push({
  103. xtype: 'button',
  104. tooltip: '还原表格自定义配置',
  105. iconCls: 'x-fa fa-reply-all',
  106. handler: this.clearGridUIConfig
  107. })
  108. }
  109. if (!newConfig.hideFootbar) {
  110. if (newConfig.pagination) {
  111. newConfig.bbar = new Ext.PagingToolbar({
  112. // pageSize: newConfig.pageSize, 这个值是无效的
  113. displayInfo: true,
  114. store: this.store,
  115. emptyMsg: '没有记录',
  116. items: [
  117. {
  118. xtype: 'combobox',
  119. tooltip: '分页',
  120. queryMode: 'local',
  121. editable: false,
  122. allowBlank: true,
  123. labelAlign: 'right',
  124. width: 90,
  125. // labelWidth: 30,
  126. listConfig: {
  127. minWidth: null
  128. },
  129. value: 50,
  130. valueField: undefined,
  131. displayField: undefined,
  132. hideClear: true,
  133. store: newConfig.pageSizeOption,
  134. listeners: {
  135. change: (sender, nv, ov) => {
  136. this.store.pageSize = nv;
  137. this.store.loadPage(1);
  138. }
  139. }
  140. },
  141. ...buttons
  142. ]
  143. })
  144. } else {
  145. newConfig.bbar = {
  146. xtype: 'toolbar', overflowHandler: 'menu',
  147. items: [
  148. {
  149. xtype: 'button',
  150. tooltip: '刷新',
  151. iconCls: 'x-fa fa-refresh',
  152. handler: () => {
  153. this.reload()
  154. }
  155. },
  156. '-',
  157. ...buttons,
  158. ]
  159. }
  160. }
  161. }
  162. _.each(newConfig.columns, c => {
  163. const {renderer} = c
  164. if (typeof renderer === 'string' && (
  165. _.startsWith(renderer, "scope.") ||
  166. _.startsWith(renderer, "system."))
  167. ) {
  168. if (newConfig.$initParent) {
  169. if (scope) {
  170. const rendererFn = lookupFn(scope, renderer)
  171. c.renderer = rendererFn
  172. }
  173. }
  174. }
  175. })
  176. this.superclass.constructor.call(this, newConfig)
  177. this.store.pageSize = newConfig.pageSize
  178. },
  179. setData(value) {
  180. const me = this
  181. me._setDataReal(value)
  182. },
  183. /**
  184. * 添加行,并进入编辑状态
  185. * @param record 新行的属性集
  186. * @param editRowCol 要编辑的列序号
  187. */
  188. appendEditRow(record, editRowCol) {
  189. const records = this.getStore().add(record)
  190. const recNew = records[0]
  191. this.setSelection(records)
  192. if (typeof editRowCol === 'number') {
  193. const ce = this.findPlugin('cellediting')
  194. this.editingPlugin = ce
  195. ce.startEdit(recNew, editRowCol)
  196. }
  197. },
  198. /**
  199. * 移除行
  200. * @param record 如果记录传空,就是当前选中的行
  201. */
  202. removeEditRow(record) {
  203. if (!record) {
  204. record = this.selection
  205. }
  206. if (!record) {
  207. msg('请选中要删除的行')
  208. return
  209. }
  210. this.getStore().remove(record)
  211. },
  212. /**
  213. * 获取全部原始数据
  214. */
  215. getDataRows() {
  216. return this.getStore().data.items.map(r => r.data)
  217. },
  218. _transform(data) {
  219. // 无论是 grid._setDataReal 还是 stores.gridInvokeBuild 都会走这个函数,设值前都可以改变表格值
  220. _.forEach(data, row => {
  221. row._origin = _.clone(row)
  222. })
  223. },
  224. _setDataReal(value) {
  225. const me = this
  226. this._transform(value)
  227. me.setStore(new Ext.data.Store({
  228. fields: getFileds(this),
  229. data: value
  230. }))
  231. },
  232. /**
  233. * 轻量级刷新
  234. */
  235. refreshData() {
  236. const store = this.getStore()
  237. if (store) {
  238. store.reload()
  239. }
  240. },
  241. /**
  242. * 为表格强制设置焦点
  243. * @param seq 顺序号
  244. */
  245. focusRow(seq) {
  246. this.setSelection(this.store.getAt(seq))
  247. this.getView().focusRow(seq)
  248. },
  249. /**
  250. * 重新载入数据(重新计算参数)
  251. */
  252. reload(reloadParams = {}) {
  253. const me = this
  254. const {config} = me
  255. if (config.dataSourceCallbackFn) {
  256. // 函数请求刷新
  257. const scope = lookupScope(this)
  258. _.defer(() => {
  259. me.setLoading(true)
  260. })
  261. config.dataSourceCallbackFn.call(scope, me, {
  262. successCallback(value) {
  263. me._setDataReal(value)
  264. _.defer(() => {
  265. me.setLoading(false)
  266. })
  267. me.fireEvent('dataLoadComplete', me, true, value);
  268. },
  269. failCallback(error) {
  270. _.defer(() => {
  271. me.setLoading(false)
  272. })
  273. me.fireEvent('dataLoadComplete', me, false, error);
  274. }
  275. })
  276. return
  277. }
  278. // if (this.store) {
  279. // this.store.reload({aaaa: 1, bbbb: 2})
  280. // }
  281. const {dataSource} = config
  282. if (_.isPlainObject(dataSource) && !window["IS_DESIGN_MODE"]) {
  283. const scope = lookupScope(me)
  284. gridInvokeBuild(scope, me, config, dataSource, reloadParams)
  285. }
  286. },
  287. exportCurrentExcelClick() {
  288. const me = this
  289. const {config} = me
  290. const scope = lookupScope(me)
  291. let excelFileName = config.excelFileName || scope.vjson.title || _.uniqueId("excel-")
  292. if (excelFileName.endsWith(".xlsx")) {
  293. excelFileName = excelFileName.split(".xlsx")[0]
  294. }
  295. excelFileName += ".xlsx"
  296. const rowsAll = this.getStore().getData().items?.map(r => r.data);
  297. const excelData = me.makeExcelData(rowsAll)
  298. LAY_EXCEL.exportExcel(excelData, excelFileName, 'xlsx')
  299. },
  300. exportExcelClick(excelExportParams) {
  301. const me = this
  302. const {config} = me
  303. excelExportParams.isExcelExport = true
  304. const scope = lookupScope(me)
  305. const {dataSource} = config
  306. let excelFileName = config.excelFileName || scope.vjson.title || _.uniqueId("excel-")
  307. gridInvokeBuild(scope, me, config, dataSource, excelExportParams, true, (responseData) => {
  308. let page = parseInt(responseData.pagination?.current) || 1
  309. const size = parseInt(responseData.pagination?.size) || me.exportExcelPageSize
  310. const total = parseInt(responseData.pagination?.total) || responseData.data?.length || 0
  311. me.exportExcelCurrentPage = page
  312. me.exportExcelPageSize = size
  313. me.exportExcelTotal = total
  314. if (excelFileName.endsWith(".xlsx")) {
  315. excelFileName = excelFileName.split(".xlsx")[0]
  316. }
  317. excelFileName += "(第" + me.exportExcelCurrentPage + "页,共" + Math.ceil(total / size) + "页、" + total + "条)"
  318. excelFileName += ".xlsx"
  319. const excelData = me.makeExcelData(responseData.data)
  320. LAY_EXCEL.exportExcel(excelData, excelFileName, 'xlsx')
  321. if (page < total / size) {
  322. page++
  323. }
  324. })
  325. },
  326. makeExcelData(jsonData) {
  327. if (!Array.isArray(jsonData) || jsonData.length === 0) {
  328. return
  329. }
  330. const me = this
  331. const data = [];
  332. // 获取表格的列定义
  333. const headerTextArr = []
  334. const headers = []
  335. for (let i = 0; i < me.headerCt.getGridColumns().length; i++) {
  336. const header = me.headerCt.getGridColumns()[i]
  337. if (!header.isHidden()) {
  338. const textStr = _.trim(header.text)
  339. const dataIndexStr = _.trim(header.dataIndex)
  340. if (dataIndexStr) {
  341. if (textStr === '') {
  342. headerTextArr.push(dataIndexStr)
  343. } else {
  344. headerTextArr.push(textStr)
  345. }
  346. headers.push(header)
  347. }
  348. }
  349. }
  350. if (headers.length === 0) {
  351. return
  352. }
  353. data.push(headerTextArr)
  354. for (let i = 0; i < jsonData.length; i++) {
  355. const dataRow = jsonData[i]
  356. const row = []
  357. for (let j = 0; j < headers.length; j++) {
  358. const key = headers[j].dataIndex
  359. let value = dataRow[key]
  360. if (typeof headers[j].renderer === 'function') {
  361. value = headers[j].renderer(value)
  362. }
  363. row.push(value || "")
  364. }
  365. data.push(row)
  366. }
  367. return data
  368. },
  369. initComponent() {
  370. const me = this
  371. const {config} = me
  372. const scope = lookupScope(this)
  373. if (!window["IS_DESIGN_MODE"]) {
  374. // 转换 dataSource 属性
  375. convertDataSource(me, scope, config)
  376. }
  377. this.on({
  378. afterrender(sender) {
  379. const me = this
  380. const {config} = this
  381. const {dataSource} = config
  382. if (config.autoLoad) {
  383. if (config.dataSourceCallbackFn) {
  384. me.reload()
  385. } else if (_.isPlainObject(dataSource)) {
  386. me.reload()
  387. }
  388. }
  389. if (config.contextMenu === true && _.isArray(config.tbar)) {
  390. const vm = this.lookupViewModel()
  391. this.contextMenu = this.add(new Ext.menu.Menu({
  392. viewModel: vm,
  393. items: _.map(config.tbar, item => {
  394. const menuItem = {
  395. ...item
  396. }
  397. if (menuItem.xtype === 'button') {
  398. delete menuItem.xtype
  399. }
  400. return menuItem
  401. })
  402. }))
  403. } else if (_.isPlainObject(config.contextMenu)) {
  404. this.contextMenu = this.add(config.contextMenu)
  405. }
  406. const $dom = $(sender.el.dom)
  407. $dom.on('keydown', (e) => {
  408. me.fireEvent('keydown', me, e,)
  409. }).on('keyup', (e) => {
  410. me.fireEvent('keyup', me, e,)
  411. })
  412. },
  413. itemcontextmenu(view, rec, node, index, e) {
  414. if (this.contextMenu) {
  415. e.stopEvent();
  416. this.contextMenu.show().setLocalXY(e.getXY());
  417. return false;
  418. }
  419. },
  420. // columnmove(sender, column, fromIndex, toIndex, eOpts) {
  421. // this.setColumnConfigCache()
  422. // },
  423. // columnhide(sender, column, eOpts) {
  424. // this.setColumnConfigCache()
  425. // },
  426. // columnshow(sender, column, eOpts) {
  427. // this.setColumnConfigCache()
  428. // },
  429. // columnresize(sender, column, width, eOpts) {
  430. // this.setColumnConfigCache()
  431. // },
  432. destory() {
  433. },
  434. })
  435. if (this.store?.proxy) {
  436. // 为 stores.proxy.buildRequest 做准备
  437. this.store.proxy.$owner = this
  438. }
  439. this.superclass.initComponent.call(this)
  440. },
  441. // 生成列自定义的缓存key
  442. makeColumnConfigCacheKey(config) {
  443. const me = this
  444. const scope = config.$initParent.yvanScope || config.$initParent.lookupReferenceHolder().yvanScope;
  445. let key = "gridColumnCache-" + scope.scopeKey + "-"
  446. if (config.reference) {
  447. key += config.reference
  448. } else {
  449. let subKey = ""
  450. for (let i = 0; i < config.columns.length; i++) {
  451. const column = config.columns[i]
  452. if (column.dataIndex) {
  453. subKey += column.dataIndex
  454. }
  455. }
  456. key += subKey
  457. }
  458. return key
  459. },
  460. getColumnConfigCache() {
  461. const key = this.columnConfigCacheKey
  462. const dataStr = localStorage.getItem(key)
  463. if (dataStr) {
  464. return JSON.parse(dataStr)
  465. }
  466. return ""
  467. },
  468. setColumnConfigCache() {
  469. const key = this.columnConfigCacheKey
  470. const cacheData = []
  471. const columns = this.headerCt.getGridColumns()
  472. let index = 0
  473. for (let i = 0; i < columns.length; i++) {
  474. const column = columns[i]
  475. if (column.dataIndex) {
  476. cacheData.push({
  477. dataIndex: column.dataIndex,
  478. width: column.width,
  479. hidden: column.hidden,
  480. locked: column.locked,
  481. index
  482. })
  483. index++
  484. }
  485. }
  486. localStorage.setItem(key, JSON.stringify(cacheData))
  487. },
  488. autoSizeColumns(sender) {
  489. const grid = sender.up('grid')
  490. // const columns = grid.columns;
  491. // for (let i = 0; i < columns.length; i++) {
  492. // const column = columns[i];
  493. // grid.getView().autoSizeColumn(column);
  494. // column.setWidth(column.getWidth() + 5);
  495. // }
  496. for (let i = 1; i < grid.headerCt.getColumnCount(); i++) {
  497. grid.headerCt.getGridColumns()[i].autoSize(i);
  498. grid.headerCt.getGridColumns()[i].setWidth(grid.headerCt.getGridColumns()[i].getWidth() + 15);
  499. }
  500. },
  501. clearFilter(sender) {
  502. sender.up('grid').filters.clearFilters();
  503. },
  504. saveGridUIConfig(sender) {
  505. const grid = sender.up('grid')
  506. grid.setColumnConfigCache()
  507. msg('保存设置成功!')
  508. },
  509. clearGridUIConfig(sender) {
  510. const grid = sender.up('grid')
  511. const key = grid.columnConfigCacheKey
  512. localStorage.setItem(key, "")
  513. msg('清空设置成功,重新打开后生效!')
  514. },
  515. setLoading(value) {
  516. if (value) {
  517. this.mask('读取中')
  518. } else {
  519. this.unmask()
  520. }
  521. },
  522. exportExcel(sender) {
  523. const rect = sender.btnEl.dom.getBoundingClientRect()
  524. const scope = lookupScope(this)
  525. const grid = sender.up('grid')
  526. const treeMenu = new Ext.menu.Menu(
  527. {
  528. xtype: 'menu',
  529. floated: false,
  530. width: 300,
  531. docked: 'left',
  532. items: [{
  533. text: '导出表格当前的数据',
  534. iconCls: 'x-fa fa-download',
  535. listeners: {
  536. click: (sender, value) => {
  537. grid.exportCurrentExcelClick()
  538. }
  539. }
  540. }, {
  541. xtype: "textfield",
  542. fieldLabel: '当前导出页',
  543. maskRe: /[0-9]/,
  544. value: grid.exportExcelCurrentPage,
  545. listeners: {
  546. render: (sender) => {
  547. grid.exportExcelCurrentPageCmp = sender
  548. },
  549. change: (sender, value) => {
  550. let v = parseInt(value)
  551. if (isNaN(v) || v === 0) {
  552. window['system'].msg("页码不能为0")
  553. v = 1
  554. sender.setValue(v)
  555. }
  556. const size = parseInt(grid.exportExcelPageSize)
  557. const total = parseInt(grid.exportExcelTotal)
  558. if (v > total / size) {
  559. v = parseInt(total / size + "")
  560. }
  561. grid.exportExcelCurrentPage = v + ""
  562. }
  563. }
  564. }, {
  565. xtype: "textfield",
  566. fieldLabel: '导出页大小',
  567. maskRe: /[0-9]/,
  568. value: grid.exportExcelPageSize,
  569. listeners: {
  570. render: (sender) => {
  571. grid.exportExcelPageSizeCmp = sender
  572. },
  573. change: (sender, value) => {
  574. let v = parseInt(value)
  575. if (isNaN(v) || v === 0) {
  576. window['system'].msg("导出页大小不能为0")
  577. v = defaultGrid.exportExcelPageSize
  578. sender.setValue(v);
  579. }
  580. if (v >= 10000) {
  581. window['system'].msg("导出页大小不能大于10000")
  582. v = 10000
  583. sender.setValue(v);
  584. }
  585. let page = parseInt(grid.exportExcelCurrentPage)
  586. const total = parseInt(grid.exportExcelTotal)
  587. if (page > total / v) {
  588. page = parseInt(total / v + "") + 1
  589. grid.exportExcelCurrentPageCmp.setValue(page)
  590. }
  591. grid.exportExcelPageSize = v + ""
  592. }
  593. }
  594. }, {
  595. xtype: "textfield",
  596. fieldLabel: '总条数',
  597. value: grid.exportExcelTotal,
  598. readOnly: true
  599. }, {
  600. text: '导出',
  601. iconCls: 'x-fa fa-download',
  602. listeners: {
  603. click: (sender, value) => {
  604. grid.exportExcelClick({
  605. exportExcelPageSize: grid.exportExcelPageSize,
  606. exportExcelCurrentPage: grid.exportExcelCurrentPage
  607. })
  608. }
  609. }
  610. }]
  611. }
  612. );
  613. treeMenu.showAt(rect.left, rect.top - 120);
  614. // for (let i = 1; i < grid.headerCt.getColumnCount(); i++) {
  615. // grid.headerCt.getGridColumns()[i].autoSize(i);
  616. // grid.headerCt.getGridColumns()[i].setWidth(grid.headerCt.getGridColumns()[i].getWidth() + 15);
  617. // }
  618. }
  619. // reload() {
  620. // dataSourceReload(this)
  621. // },
  622. })
  623. PropertyDescriptionTable.set(
  624. 'yvgrid',
  625. new PropertyDescription(YvBase, {
  626. props: [
  627. fieldLabel, value, disabled,
  628. gravity, tooltip, metaId, width, height
  629. ],
  630. })
  631. )
  632. }
  633. /**
  634. * 获取 columns 中所有的 dataIndex
  635. */
  636. function getFileds(newConfig) {
  637. const fields = []
  638. _.forEach(newConfig.columns, c => {
  639. if (c.dataIndex) {
  640. fields.push(c.dataIndex)
  641. }
  642. })
  643. return fields
  644. }
  645. function convertDataSource(sender, scope, newConfig) {
  646. if (typeof newConfig.store !== 'undefined') {
  647. // 有 store 属性的情况下,不做任何事
  648. return
  649. }
  650. if (typeof newConfig.dataSource === 'undefined') {
  651. // 没有定义 dataSource 的情况下,不做任何事
  652. return
  653. }
  654. if (_.isArray(newConfig.data)) {
  655. // 有 data 属性赋值的情况下
  656. newConfig.store = {
  657. fields: getFileds(newConfig),
  658. data: newConfig.data
  659. }
  660. delete newConfig.data
  661. return
  662. }
  663. let {dataSource} = newConfig
  664. if (typeof dataSource === 'string') {
  665. // dataSource 是字符串的情况下,找到成员函数
  666. dataSource = lookupFn(scope, dataSource)
  667. }
  668. if (typeof dataSource === 'function') {
  669. // dataSource 是函数的情况下,在 afterrender 之后进行回调
  670. newConfig.store = new Ext.data.Store({
  671. fields: getFileds(newConfig),
  672. // data: [],
  673. autoLoad: true,
  674. proxy: {
  675. type: 'memory',
  676. data: [],
  677. // reader: {
  678. // type: 'json',
  679. // rootProperty: 'users'
  680. // }
  681. }
  682. })
  683. newConfig.dataSourceCallbackFn = dataSource
  684. return
  685. }
  686. // throw new TypeError('无法识别的调用方法')
  687. }