table.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. import XEUtils from 'xe-utils'
  2. import GlobalConfig from '../../v-x-e-table/src/conf'
  3. import VXETable from '../../v-x-e-table'
  4. import VxeTableBody from './body'
  5. import vSize from '../../mixins/size'
  6. import { UtilTools, GlobalEvent, createResizeEvent, isEnableConf } from '../../tools'
  7. import methods from './methods'
  8. /**
  9. * 渲染浮固定列
  10. * 分别渲染左边固定列和右边固定列
  11. * 如果宽度足够情况下,则不需要渲染固定列
  12. * @param {Function} h 创建 VNode 函数
  13. * @param {Object} $xetable 表格实例
  14. * @param {String} fixedType 固定列类型
  15. */
  16. function renderFixed (h, $xetable, fixedType) {
  17. const { _e, tableData, tableColumn, tableGroupColumn, vSize, showHeader, showFooter, columnStore, footerTableData } = $xetable
  18. const fixedColumn = columnStore[`${fixedType}List`]
  19. return h('div', {
  20. class: `vxe-table--fixed-${fixedType}-wrapper`,
  21. ref: `${fixedType}Container`
  22. }, [
  23. showHeader ? h('vxe-table-header', {
  24. props: {
  25. fixedType,
  26. tableData,
  27. tableColumn,
  28. tableGroupColumn,
  29. size: vSize,
  30. fixedColumn
  31. },
  32. ref: `${fixedType}Header`
  33. }) : _e(),
  34. h('vxe-table-body', {
  35. props: {
  36. fixedType,
  37. tableData,
  38. tableColumn,
  39. fixedColumn,
  40. size: vSize
  41. },
  42. ref: `${fixedType}Body`
  43. }),
  44. showFooter ? h('vxe-table-footer', {
  45. props: {
  46. footerTableData,
  47. tableColumn,
  48. fixedColumn,
  49. fixedType,
  50. size: vSize
  51. },
  52. ref: `${fixedType}Footer`
  53. }) : _e()
  54. ])
  55. }
  56. function renderEmptyContenet (h, _vm) {
  57. const { $scopedSlots, emptyOpts } = _vm
  58. let emptyContent = ''
  59. const params = { $table: _vm }
  60. if ($scopedSlots.empty) {
  61. emptyContent = $scopedSlots.empty.call(_vm, params, h)
  62. } else {
  63. const compConf = _vm.emptyRender ? VXETable.renderer.get(emptyOpts.name) : null
  64. if (compConf) {
  65. emptyContent = compConf.renderEmpty.call(_vm, h, emptyOpts, params)
  66. } else {
  67. emptyContent = _vm.emptyText || GlobalConfig.i18n('vxe.table.emptyText')
  68. }
  69. }
  70. return emptyContent
  71. }
  72. function handleUupdateResize (_vm) {
  73. const { $el } = _vm
  74. if ($el && $el.clientWidth && $el.clientHeight) {
  75. _vm.recalculate()
  76. }
  77. }
  78. export default {
  79. name: 'VxeTable',
  80. mixins: [vSize],
  81. props: {
  82. /** 基本属性 */
  83. id: String,
  84. // 数据
  85. data: Array,
  86. // 表格的高度
  87. height: [Number, String],
  88. // 表格的最大高度
  89. maxHeight: [Number, String],
  90. // 所有列是否允许拖动列宽调整大小
  91. resizable: { type: Boolean, default: () => GlobalConfig.table.resizable },
  92. // 是否带有斑马纹
  93. stripe: { type: Boolean, default: () => GlobalConfig.table.stripe },
  94. // 是否带有边框
  95. border: { type: [Boolean, String], default: () => GlobalConfig.table.border },
  96. // 是否圆角边框
  97. round: { type: Boolean, default: () => GlobalConfig.table.round },
  98. // 表格的尺寸
  99. size: { type: String, default: () => GlobalConfig.table.size || GlobalConfig.size },
  100. // 列的宽度是否自撑开(可能会被废弃的参数,不要使用)
  101. fit: { type: Boolean, default: () => GlobalConfig.table.fit },
  102. // 表格是否加载中
  103. loading: Boolean,
  104. // 所有的列对其方式
  105. align: { type: String, default: () => GlobalConfig.table.align },
  106. // 所有的表头列的对齐方式
  107. headerAlign: { type: String, default: () => GlobalConfig.table.headerAlign },
  108. // 所有的表尾列的对齐方式
  109. footerAlign: { type: String, default: () => GlobalConfig.table.footerAlign },
  110. // 是否显示表头
  111. showHeader: { type: Boolean, default: () => GlobalConfig.table.showHeader },
  112. // 是否要高亮当前选中行
  113. highlightCurrentRow: { type: Boolean, default: () => GlobalConfig.table.highlightCurrentRow },
  114. // 鼠标移到行是否要高亮显示
  115. highlightHoverRow: { type: Boolean, default: () => GlobalConfig.table.highlightHoverRow },
  116. // 是否要高亮当前选中列
  117. highlightCurrentColumn: { type: Boolean, default: () => GlobalConfig.table.highlightCurrentColumn },
  118. // 鼠标移到列是否要高亮显示
  119. highlightHoverColumn: { type: Boolean, default: () => GlobalConfig.table.highlightHoverColumn },
  120. // 激活单元格编辑时是否高亮显示
  121. highlightCell: Boolean,
  122. // 是否显示表尾合计
  123. showFooter: Boolean,
  124. // 表尾合计的计算方法
  125. footerMethod: { type: Function, default: GlobalConfig.table.footerMethod },
  126. // 给行附加 className
  127. rowClassName: [String, Function],
  128. // 给单元格附加 className
  129. cellClassName: [String, Function],
  130. // 给表头的行附加 className
  131. headerRowClassName: [String, Function],
  132. // 给表头的单元格附加 className
  133. headerCellClassName: [String, Function],
  134. // 给表尾的行附加 className
  135. footerRowClassName: [String, Function],
  136. // 给表尾的单元格附加 className
  137. footerCellClassName: [String, Function],
  138. // 给单元格附加样式
  139. cellStyle: [Object, Function],
  140. // 给表头单元格附加样式
  141. headerCellStyle: [Object, Function],
  142. // 给表尾单元格附加样式
  143. footerCellStyle: [Object, Function],
  144. // 给行附加样式
  145. rowStyle: [Object, Function],
  146. // 给表头行附加样式
  147. headerRowStyle: [Object, Function],
  148. // 给表尾行附加样式
  149. footerRowStyle: [Object, Function],
  150. // 合并指定单元格
  151. mergeCells: Array,
  152. // 合并指定的表尾
  153. mergeFooterItems: Array,
  154. // 自定义合并行或列的方法
  155. spanMethod: Function,
  156. // 表尾合并行或列
  157. footerSpanMethod: Function,
  158. // 设置所有内容过长时显示为省略号
  159. showOverflow: { type: [Boolean, String], default: () => GlobalConfig.table.showOverflow },
  160. // 设置表头所有内容过长时显示为省略号
  161. showHeaderOverflow: { type: [Boolean, String], default: () => GlobalConfig.table.showHeaderOverflow },
  162. // 设置表尾所有内容过长时显示为省略号
  163. showFooterOverflow: { type: [Boolean, String], default: () => GlobalConfig.table.showFooterOverflow },
  164. /** 高级属性 */
  165. // 主键配置
  166. columnKey: Boolean,
  167. rowKey: Boolean,
  168. rowId: { type: String, default: () => GlobalConfig.table.rowId },
  169. zIndex: Number,
  170. emptyText: String,
  171. keepSource: { type: Boolean, default: () => GlobalConfig.table.keepSource },
  172. // 是否自动监听父容器变化去更新响应式表格宽高
  173. autoResize: { type: Boolean, default: () => GlobalConfig.table.autoResize },
  174. // 是否自动根据状态属性去更新响应式表格宽高
  175. syncResize: [Boolean, String, Number],
  176. // 设置列的默认参数,仅对部分支持的属性有效
  177. columnConfig: Object,
  178. resizableConfig: Object,
  179. // 序号配置项
  180. seqConfig: Object,
  181. // 排序配置项
  182. sortConfig: Object,
  183. // 筛选配置项
  184. filterConfig: Object,
  185. // 单选框配置
  186. radioConfig: Object,
  187. // 复选框配置项
  188. checkboxConfig: Object,
  189. // tooltip 配置项
  190. tooltipConfig: Object,
  191. // 导出配置项
  192. exportConfig: [Boolean, Object],
  193. // 导入配置项
  194. importConfig: [Boolean, Object],
  195. // 打印配置项
  196. printConfig: Object,
  197. // 展开行配置项
  198. expandConfig: Object,
  199. // 树形结构配置项
  200. treeConfig: [Boolean, Object],
  201. // 快捷菜单配置项
  202. menuConfig: [Boolean, Object],
  203. // 在 v4 中废弃 contextMenu
  204. contextMenu: [Boolean, Object],
  205. // 鼠标配置项
  206. mouseConfig: Object,
  207. // 区域配置项
  208. areaConfig: Object,
  209. // 按键配置项
  210. keyboardConfig: Object,
  211. // 复制/粘贴配置项
  212. clipConfig: Object,
  213. // 查找/替换配置项
  214. fnrConfig: Object,
  215. // 编辑配置项
  216. editConfig: [Boolean, Object],
  217. // 校验配置项
  218. validConfig: Object,
  219. // 校验规则配置项
  220. editRules: Object,
  221. // 空内容渲染配置项
  222. emptyRender: [Boolean, Object],
  223. // 自定义列配置项
  224. customConfig: [Boolean, Object],
  225. // 横向虚拟滚动配置项
  226. scrollX: Object,
  227. // 纵向虚拟滚动配置项
  228. scrollY: Object,
  229. // 优化相关
  230. animat: { type: Boolean, default: () => GlobalConfig.table.animat },
  231. delayHover: { type: Number, default: () => GlobalConfig.table.delayHover },
  232. // 额外的参数
  233. params: Object
  234. },
  235. components: {
  236. VxeTableBody
  237. },
  238. provide () {
  239. return {
  240. $xetable: this,
  241. xecolgroup: null
  242. }
  243. },
  244. inject: {
  245. $xegrid: {
  246. default: null
  247. }
  248. },
  249. data () {
  250. return {
  251. tId: `${XEUtils.uniqueId()}`,
  252. // 低性能的静态列
  253. staticColumns: [],
  254. // 渲染的列分组
  255. tableGroupColumn: [],
  256. // 可视区渲染的列
  257. tableColumn: [],
  258. // 渲染中的数据
  259. tableData: [],
  260. // 是否启用了横向 X 可视渲染方式加载
  261. scrollXLoad: false,
  262. // 是否启用了纵向 Y 可视渲染方式加载
  263. scrollYLoad: false,
  264. // 是否存在纵向滚动条
  265. overflowY: true,
  266. // 是否存在横向滚动条
  267. overflowX: false,
  268. // 纵向滚动条的宽度
  269. scrollbarWidth: 0,
  270. // 横向滚动条的高度
  271. scrollbarHeight: 0,
  272. // 行高
  273. rowHeight: 0,
  274. // 表格父容器的高度
  275. parentHeight: 0,
  276. // 是否使用分组表头
  277. isGroup: false,
  278. isAllOverflow: false,
  279. // 复选框属性,是否全选
  280. isAllSelected: false,
  281. // 复选框属性,有选中且非全选状态
  282. isIndeterminate: false,
  283. // 复选框属性,已选中的行
  284. selection: [],
  285. // 当前行
  286. currentRow: null,
  287. // 单选框属性,选中列
  288. currentColumn: null,
  289. // 单选框属性,选中行
  290. selectRow: null,
  291. // 表尾合计数据
  292. footerTableData: [],
  293. // 展开列信息
  294. expandColumn: null,
  295. hasFixedColumn: false,
  296. // 树节点列信息
  297. treeNodeColumn: null,
  298. // 已展开的行
  299. rowExpandeds: [],
  300. // 懒加载中的展开行的列表
  301. expandLazyLoadeds: [],
  302. // 已展开树节点
  303. treeExpandeds: [],
  304. // 懒加载中的树节点的列表
  305. treeLazyLoadeds: [],
  306. // 树节点不确定状态的列表
  307. treeIndeterminates: [],
  308. // 合并单元格的对象集
  309. mergeList: [],
  310. // 合并表尾数据的对象集
  311. mergeFooterList: [],
  312. // 初始化标识
  313. initStore: {
  314. filter: false,
  315. import: false,
  316. export: false
  317. },
  318. // 当前选中的筛选列
  319. filterStore: {
  320. isAllSelected: false,
  321. isIndeterminate: false,
  322. style: null,
  323. options: [],
  324. column: null,
  325. multiple: false,
  326. visible: false
  327. },
  328. // 存放列相关的信息
  329. columnStore: {
  330. leftList: [],
  331. centerList: [],
  332. rightList: [],
  333. resizeList: [],
  334. pxList: [],
  335. pxMinList: [],
  336. scaleList: [],
  337. scaleMinList: [],
  338. autoList: []
  339. },
  340. // 存放快捷菜单的信息
  341. ctxMenuStore: {
  342. selected: null,
  343. visible: false,
  344. showChild: false,
  345. selectChild: null,
  346. list: [],
  347. style: null
  348. },
  349. // 存放可编辑相关信息
  350. editStore: {
  351. indexs: {
  352. columns: []
  353. },
  354. titles: {
  355. columns: []
  356. },
  357. // 选中源
  358. selected: {
  359. row: null,
  360. column: null
  361. },
  362. // 已复制源
  363. copyed: {
  364. cut: false,
  365. rows: [],
  366. columns: []
  367. },
  368. // 激活
  369. actived: {
  370. row: null,
  371. column: null
  372. },
  373. insertList: [],
  374. removeList: []
  375. },
  376. // 存放数据校验相关信息
  377. validStore: {
  378. visible: false,
  379. row: null,
  380. column: null,
  381. content: '',
  382. rule: null,
  383. isArrow: false
  384. },
  385. // 导入相关信息
  386. importStore: {
  387. inited: false,
  388. file: null,
  389. type: '',
  390. modeList: [],
  391. typeList: [],
  392. filename: '',
  393. visible: false
  394. },
  395. importParams: {
  396. mode: '',
  397. types: null,
  398. message: true
  399. },
  400. // 导出相关信息
  401. exportStore: {
  402. inited: false,
  403. name: '',
  404. modeList: [],
  405. typeList: [],
  406. columns: [],
  407. isPrint: false,
  408. hasFooter: false,
  409. hasTree: false,
  410. hasMerge: false,
  411. hasColgroup: false,
  412. visible: false
  413. },
  414. exportParams: {
  415. filename: '',
  416. sheetName: '',
  417. mode: '',
  418. type: '',
  419. isColgroup: false,
  420. isMerge: false,
  421. isAllExpand: false,
  422. useStyle: false,
  423. original: false,
  424. message: true,
  425. isHeader: false,
  426. isFooter: false
  427. }
  428. }
  429. },
  430. computed: {
  431. validOpts () {
  432. return Object.assign({ message: 'default' }, GlobalConfig.table.validConfig, this.validConfig)
  433. },
  434. sXOpts () {
  435. return Object.assign({}, GlobalConfig.table.scrollX, this.scrollX)
  436. },
  437. sYOpts () {
  438. return Object.assign({}, GlobalConfig.table.scrollY, this.scrollY)
  439. },
  440. rowHeightMaps () {
  441. return {
  442. default: 48,
  443. medium: 44,
  444. small: 40,
  445. mini: 36
  446. }
  447. },
  448. columnOpts () {
  449. return Object.assign({}, this.columnConfig)
  450. },
  451. resizableOpts () {
  452. return Object.assign({}, GlobalConfig.table.resizableConfig, this.resizableConfig)
  453. },
  454. seqOpts () {
  455. return Object.assign({ startIndex: 0 }, GlobalConfig.table.seqConfig, this.seqConfig)
  456. },
  457. radioOpts () {
  458. return Object.assign({}, GlobalConfig.table.radioConfig, this.radioConfig)
  459. },
  460. checkboxOpts () {
  461. return Object.assign({}, GlobalConfig.table.checkboxConfig, this.checkboxConfig)
  462. },
  463. tooltipOpts () {
  464. const opts = Object.assign({ leaveDelay: 300 }, GlobalConfig.table.tooltipConfig, this.tooltipConfig)
  465. if (opts.enterable) {
  466. opts.leaveMethod = this.handleTooltipLeaveMethod
  467. }
  468. return opts
  469. },
  470. validTipOpts () {
  471. return Object.assign({ isArrow: false }, this.tooltipOpts)
  472. },
  473. editOpts () {
  474. return Object.assign({}, GlobalConfig.table.editConfig, this.editConfig)
  475. },
  476. sortOpts () {
  477. return Object.assign({ orders: ['asc', 'desc', null] }, GlobalConfig.table.sortConfig, this.sortConfig)
  478. },
  479. filterOpts () {
  480. return Object.assign({}, GlobalConfig.table.filterConfig, this.filterConfig)
  481. },
  482. mouseOpts () {
  483. return Object.assign({}, GlobalConfig.table.mouseConfig, this.mouseConfig)
  484. },
  485. areaOpts () {
  486. return Object.assign({}, GlobalConfig.table.areaConfig, this.areaConfig)
  487. },
  488. keyboardOpts () {
  489. return Object.assign({}, GlobalConfig.table.keyboardConfig, this.keyboardConfig)
  490. },
  491. clipOpts () {
  492. return Object.assign({}, GlobalConfig.table.clipConfig, this.clipConfig)
  493. },
  494. fnrOpts () {
  495. return Object.assign({}, GlobalConfig.table.fnrConfig, this.fnrConfig)
  496. },
  497. hasTip () {
  498. return VXETable._tooltip
  499. },
  500. headerCtxMenu () {
  501. const headerOpts = this.ctxMenuOpts.header
  502. return headerOpts && headerOpts.options ? headerOpts.options : []
  503. },
  504. bodyCtxMenu () {
  505. const bodyOpts = this.ctxMenuOpts.body
  506. return bodyOpts && bodyOpts.options ? bodyOpts.options : []
  507. },
  508. footerCtxMenu () {
  509. const footerOpts = this.ctxMenuOpts.footer
  510. return footerOpts && footerOpts.options ? footerOpts.options : []
  511. },
  512. isCtxMenu () {
  513. return !!((this.contextMenu || this.menuConfig) && isEnableConf(this.ctxMenuOpts) && (this.headerCtxMenu.length || this.bodyCtxMenu.length || this.footerCtxMenu.length))
  514. },
  515. ctxMenuOpts () {
  516. return Object.assign({}, GlobalConfig.table.menuConfig, this.contextMenu, this.menuConfig)
  517. },
  518. ctxMenuList () {
  519. const rest = []
  520. this.ctxMenuStore.list.forEach(list => {
  521. list.forEach(item => {
  522. rest.push(item)
  523. })
  524. })
  525. return rest
  526. },
  527. exportOpts () {
  528. return Object.assign({}, GlobalConfig.table.exportConfig, this.exportConfig)
  529. },
  530. importOpts () {
  531. return Object.assign({}, GlobalConfig.table.importConfig, this.importConfig)
  532. },
  533. printOpts () {
  534. return Object.assign({}, GlobalConfig.table.printConfig, this.printConfig)
  535. },
  536. expandOpts () {
  537. return Object.assign({}, GlobalConfig.table.expandConfig, this.expandConfig)
  538. },
  539. treeOpts () {
  540. return Object.assign({}, GlobalConfig.table.treeConfig, this.treeConfig)
  541. },
  542. emptyOpts () {
  543. return Object.assign({}, GlobalConfig.table.emptyRender, this.emptyRender)
  544. },
  545. cellOffsetWidth () {
  546. return this.border ? Math.max(2, Math.ceil(this.scrollbarWidth / this.tableColumn.length)) : 1
  547. },
  548. customOpts () {
  549. return Object.assign({}, GlobalConfig.table.customConfig, this.customConfig)
  550. },
  551. tableBorder () {
  552. const { border } = this
  553. if (border === true) {
  554. return 'full'
  555. }
  556. if (border) {
  557. return border
  558. }
  559. return 'default'
  560. },
  561. /**
  562. * 判断列全选的复选框是否禁用
  563. */
  564. isAllCheckboxDisabled () {
  565. const { tableFullData, treeConfig, checkboxOpts } = this
  566. const { strict, checkMethod } = checkboxOpts
  567. if (strict) {
  568. if (tableFullData.length) {
  569. if (checkMethod) {
  570. if (treeConfig) {
  571. // 暂时不支持树形结构
  572. }
  573. // 如果所有行都被禁用
  574. return tableFullData.every((row) => !checkMethod({ row }))
  575. }
  576. return false
  577. }
  578. return true
  579. }
  580. return false
  581. }
  582. },
  583. watch: {
  584. data (value) {
  585. const { inited, initStatus } = this
  586. this.loadTableData(value).then(() => {
  587. this.inited = true
  588. this.initStatus = true
  589. if (!initStatus) {
  590. this.handleLoadDefaults()
  591. }
  592. if (!inited) {
  593. this.handleInitDefaults()
  594. }
  595. if ((this.scrollXLoad || this.scrollYLoad) && this.expandColumn) {
  596. UtilTools.warn('vxe.error.scrollErrProp', ['column.type=expand'])
  597. }
  598. this.recalculate()
  599. })
  600. },
  601. staticColumns (value) {
  602. this.handleColumn(value)
  603. },
  604. tableColumn () {
  605. this.analyColumnWidth()
  606. },
  607. showHeader () {
  608. this.$nextTick(() => {
  609. this.recalculate(true).then(() => this.refreshScroll())
  610. })
  611. },
  612. showFooter () {
  613. this.$nextTick(() => {
  614. this.recalculate(true).then(() => this.refreshScroll())
  615. })
  616. },
  617. height () {
  618. this.$nextTick(() => this.recalculate(true))
  619. },
  620. maxHeight () {
  621. this.$nextTick(() => this.recalculate(true))
  622. },
  623. syncResize (value) {
  624. if (value) {
  625. handleUupdateResize(this)
  626. this.$nextTick(() => {
  627. handleUupdateResize(this)
  628. setTimeout(() => handleUupdateResize(this))
  629. })
  630. }
  631. },
  632. mergeCells (value) {
  633. this.clearMergeCells()
  634. this.setMergeCells(value)
  635. },
  636. mergeFooterItems (value) {
  637. this.clearMergeFooterItems()
  638. this.setMergeFooterItems(value)
  639. }
  640. },
  641. created () {
  642. const { scrollXStore, sYOpts, scrollYStore, data, editOpts, treeOpts, treeConfig, showOverflow } = Object.assign(this, {
  643. tZindex: 0,
  644. elemStore: {},
  645. // 存放横向 X 虚拟滚动相关的信息
  646. scrollXStore: {},
  647. // 存放纵向 Y 虚拟滚动相关信息
  648. scrollYStore: {},
  649. // 存放 tooltip 相关信息
  650. tooltipStore: {},
  651. // 表格宽度
  652. tableWidth: 0,
  653. // 表格高度
  654. tableHeight: 0,
  655. // 表头高度
  656. headerHeight: 0,
  657. // 表尾高度
  658. footerHeight: 0,
  659. // 当前 hover 行
  660. // hoverRow: null,
  661. // 最后滚动位置
  662. lastScrollLeft: 0,
  663. lastScrollTop: 0,
  664. // 单选框属性,已选中保留的行
  665. radioReserveRow: null,
  666. // 复选框属性,已选中保留的行
  667. checkboxReserveRowMap: {},
  668. // 行数据,已展开保留的行
  669. rowExpandedReserveRowMap: {},
  670. // 树结构数据,已展开保留的行
  671. treeExpandedReserveRowMap: {},
  672. // 完整数据、条件处理后
  673. tableFullData: [],
  674. afterFullData: [],
  675. // 收集的列配置(带分组)
  676. collectColumn: [],
  677. // 完整所有列(不带分组)
  678. tableFullColumn: [],
  679. // 渲染所有列
  680. visibleColumn: [],
  681. // 缓存数据集
  682. fullAllDataRowMap: new Map(),
  683. fullAllDataRowIdData: {},
  684. fullDataRowMap: new Map(),
  685. fullDataRowIdData: {},
  686. fullColumnMap: new Map(),
  687. fullColumnIdData: {},
  688. fullColumnFieldData: {}
  689. })
  690. if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') {
  691. if (!this.rowId && (this.checkboxOpts.reserve || this.checkboxOpts.checkRowKeys || this.radioOpts.reserve || this.radioOpts.checkRowKey || this.expandOpts.expandRowKeys || this.treeOpts.expandRowKeys)) {
  692. UtilTools.warn('vxe.error.reqProp', ['row-id'])
  693. }
  694. if (this.editConfig && editOpts.showStatus && !this.keepSource) {
  695. UtilTools.warn('vxe.error.reqProp', ['keep-source'])
  696. }
  697. if (treeConfig && treeOpts.line && (!this.rowKey || !showOverflow)) {
  698. UtilTools.warn('vxe.error.reqProp', ['row-key | show-overflow'])
  699. }
  700. if (this.showFooter && !this.footerMethod) {
  701. UtilTools.warn('vxe.error.reqProp', ['footer-method'])
  702. }
  703. if (treeConfig && this.stripe) {
  704. UtilTools.warn('vxe.error.noTree', ['stripe'])
  705. }
  706. if (this.tooltipOpts.enabled) {
  707. UtilTools.warn('vxe.error.delProp', ['tooltip-config.enabled', 'tooltip-config.showAll'])
  708. }
  709. // 检查导入导出类型,如果自定义导入导出方法,则不校验类型
  710. const { exportConfig, exportOpts, importConfig, importOpts } = this
  711. if (importConfig && importOpts.types && !importOpts.importMethod && !XEUtils.includeArrays(VXETable.config.importTypes, importOpts.types)) {
  712. UtilTools.warn('vxe.error.errProp', [`export-config.types=${importOpts.types.join(',')}`, importOpts.types.filter(type => XEUtils.includes(VXETable.config.importTypes, type)).join(',') || VXETable.config.importTypes.join(',')])
  713. }
  714. if (exportConfig && exportOpts.types && !exportOpts.exportMethod && !XEUtils.includeArrays(VXETable.config.exportTypes, exportOpts.types)) {
  715. UtilTools.warn('vxe.error.errProp', [`export-config.types=${exportOpts.types.join(',')}`, exportOpts.types.filter(type => XEUtils.includes(VXETable.config.exportTypes, type)).join(',') || VXETable.config.exportTypes.join(',')])
  716. }
  717. }
  718. if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') {
  719. const customOpts = this.customOpts
  720. if (!this.id && this.customConfig && (customOpts.storage === true || (customOpts.storage && customOpts.storage.resizable) || (customOpts.storage && customOpts.storage.visible))) {
  721. UtilTools.error('vxe.error.reqProp', ['id'])
  722. }
  723. if (this.treeConfig && this.checkboxOpts.range) {
  724. UtilTools.error('vxe.error.noTree', ['checkbox-config.range'])
  725. }
  726. if (!this.handleUpdateCellAreas) {
  727. if (this.clipConfig) {
  728. UtilTools.warn('vxe.error.notProp', ['clip-config'])
  729. }
  730. if (this.fnrConfig) {
  731. UtilTools.warn('vxe.error.notProp', ['fnr-config'])
  732. }
  733. if (this.mouseOpts.area) {
  734. UtilTools.error('vxe.error.notProp', ['mouse-config.area'])
  735. return
  736. }
  737. }
  738. if (this.mouseOpts.area && this.mouseOpts.selected) {
  739. UtilTools.warn('vxe.error.errConflicts', ['mouse-config.area', 'mouse-config.selected'])
  740. }
  741. if (this.mouseOpts.area && this.checkboxOpts.range) {
  742. UtilTools.warn('vxe.error.errConflicts', ['mouse-config.area', 'checkbox-config.range'])
  743. }
  744. if (this.treeConfig && this.mouseOpts.area) {
  745. UtilTools.error('vxe.error.noTree', ['mouse-config.area'])
  746. }
  747. }
  748. // v4 中只支持对象类型
  749. if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') {
  750. // 在 v3.0 中废弃 context-menu
  751. if (this.contextMenu) {
  752. UtilTools.warn('vxe.error.delProp', ['context-menu', 'menu-config'])
  753. if (!XEUtils.isObject(this.contextMenu)) {
  754. UtilTools.warn('vxe.error.errProp', [`table.context-menu=${this.contextMenu}`, 'table.context-menu={}'])
  755. }
  756. }
  757. if (this.menuConfig && !XEUtils.isObject(this.menuConfig)) {
  758. UtilTools.warn('vxe.error.errProp', [`table.menu-config=${this.menuConfig}`, 'table.menu-config={}'])
  759. }
  760. if (this.exportConfig && !XEUtils.isObject(this.exportConfig)) {
  761. UtilTools.warn('vxe.error.errProp', [`table.export-config=${this.exportConfig}`, 'table.export-config={}'])
  762. }
  763. if (this.importConfig && !XEUtils.isObject(this.importConfig)) {
  764. UtilTools.warn('vxe.error.errProp', [`table.import-config=${this.importConfig}`, 'table.import-config={}'])
  765. }
  766. if (this.printConfig && !XEUtils.isObject(this.printConfig)) {
  767. UtilTools.warn('vxe.error.errProp', [`table.print-config=${this.printConfig}`, 'table.print-config={}'])
  768. }
  769. if (this.treeConfig && !XEUtils.isObject(this.treeConfig)) {
  770. UtilTools.warn('vxe.error.errProp', [`table.tree-config=${this.treeConfig}`, 'table.tree-config={}'])
  771. }
  772. if (this.customConfig && !XEUtils.isObject(this.customConfig)) {
  773. UtilTools.warn('vxe.error.errProp', [`table.custom-config=${this.customConfig}`, 'table.custom-config={}'])
  774. }
  775. if (this.editConfig && !XEUtils.isObject(this.editConfig)) {
  776. UtilTools.warn('vxe.error.errProp', [`table.edit-config=${this.editConfig}`, 'table.edit-config={}'])
  777. }
  778. if (this.emptyRender && !XEUtils.isObject(this.emptyRender)) {
  779. UtilTools.warn('vxe.error.errProp', [`table.empty-render=${this.emptyRender}`, 'table.empty-render={}'])
  780. }
  781. }
  782. // 检查是否有安装需要的模块
  783. if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') {
  784. if (this.editConfig && !this._insert) {
  785. UtilTools.error('vxe.error.reqModule', ['Edit'])
  786. }
  787. if (this.editRules && !this._validate) {
  788. UtilTools.error('vxe.error.reqModule', ['Validator'])
  789. }
  790. if ((this.keyboardConfig || this.mouseConfig) && !this.triggerCellMousedownEvent) {
  791. UtilTools.error('vxe.error.reqModule', ['Keyboard'])
  792. }
  793. if ((this.printConfig || this.importConfig || this.exportConfig) && !this._exportData) {
  794. UtilTools.error('vxe.error.reqModule', ['Export'])
  795. }
  796. }
  797. Object.assign(scrollYStore, {
  798. startIndex: 0,
  799. endIndex: 0,
  800. visibleSize: 0,
  801. adaptive: sYOpts.adaptive !== false
  802. })
  803. Object.assign(scrollXStore, {
  804. startIndex: 0,
  805. endIndex: 0,
  806. visibleSize: 0
  807. })
  808. this.loadTableData(data).then(() => {
  809. if (data && data.length) {
  810. this.inited = true
  811. this.initStatus = true
  812. this.handleLoadDefaults()
  813. this.handleInitDefaults()
  814. }
  815. this.updateStyle()
  816. })
  817. GlobalEvent.on(this, 'paste', this.handleGlobalPasteEvent)
  818. GlobalEvent.on(this, 'copy', this.handleGlobalCopyEvent)
  819. GlobalEvent.on(this, 'cut', this.handleGlobalCutEvent)
  820. GlobalEvent.on(this, 'mousedown', this.handleGlobalMousedownEvent)
  821. GlobalEvent.on(this, 'blur', this.handleGlobalBlurEvent)
  822. GlobalEvent.on(this, 'mousewheel', this.handleGlobalMousewheelEvent)
  823. GlobalEvent.on(this, 'keydown', this.handleGlobalKeydownEvent)
  824. GlobalEvent.on(this, 'resize', this.handleGlobalResizeEvent)
  825. GlobalEvent.on(this, 'contextmenu', this.handleGlobalContextmenuEvent)
  826. this.preventEvent(null, 'created')
  827. },
  828. mounted () {
  829. if (this.autoResize) {
  830. const resizeObserver = createResizeEvent(() => this.recalculate(true))
  831. resizeObserver.observe(this.$el)
  832. resizeObserver.observe(this.getParentElem())
  833. this.$resize = resizeObserver
  834. }
  835. this.preventEvent(null, 'mounted')
  836. },
  837. activated () {
  838. this.recalculate().then(() => this.refreshScroll())
  839. this.preventEvent(null, 'activated')
  840. },
  841. deactivated () {
  842. this.preventEvent(null, 'deactivated')
  843. },
  844. beforeDestroy () {
  845. if (this.$resize) {
  846. this.$resize.disconnect()
  847. }
  848. this.closeFilter()
  849. this.closeMenu()
  850. this.preventEvent(null, 'beforeDestroy')
  851. },
  852. destroyed () {
  853. GlobalEvent.off(this, 'paste')
  854. GlobalEvent.off(this, 'copy')
  855. GlobalEvent.off(this, 'cut')
  856. GlobalEvent.off(this, 'mousedown')
  857. GlobalEvent.off(this, 'blur')
  858. GlobalEvent.off(this, 'mousewheel')
  859. GlobalEvent.off(this, 'keydown')
  860. GlobalEvent.off(this, 'resize')
  861. GlobalEvent.off(this, 'contextmenu')
  862. this.preventEvent(null, 'destroyed')
  863. },
  864. render (h) {
  865. const {
  866. _e,
  867. tId,
  868. tableData,
  869. tableColumn,
  870. tableGroupColumn,
  871. isGroup,
  872. loading,
  873. stripe,
  874. showHeader,
  875. height,
  876. tableBorder,
  877. treeOpts,
  878. treeConfig,
  879. mouseConfig,
  880. mouseOpts,
  881. vSize,
  882. validOpts,
  883. showFooter,
  884. overflowX,
  885. overflowY,
  886. scrollXLoad,
  887. scrollYLoad,
  888. scrollbarHeight,
  889. highlightCell,
  890. highlightHoverRow,
  891. highlightHoverColumn,
  892. editConfig,
  893. validTipOpts,
  894. tooltipOpts,
  895. initStore,
  896. columnStore,
  897. filterStore,
  898. ctxMenuStore,
  899. ctxMenuOpts,
  900. footerTableData,
  901. hasTip
  902. } = this
  903. const { leftList, rightList } = columnStore
  904. return h('div', {
  905. class: ['vxe-table', 'vxe-table--render-default', `tid_${tId}`, vSize ? `size--${vSize}` : '', `border--${tableBorder}`, {
  906. 'vxe-editable': !!editConfig,
  907. 'cell--highlight': highlightCell,
  908. 'cell--selected': mouseConfig && mouseOpts.selected,
  909. 'cell--area': mouseConfig && mouseOpts.area,
  910. 'row--highlight': highlightHoverRow,
  911. 'column--highlight': highlightHoverColumn,
  912. 'is--header': showHeader,
  913. 'is--footer': showFooter,
  914. 'is--group': isGroup,
  915. 'is--tree-line': treeConfig && treeOpts.line,
  916. 'is--fixed-left': leftList.length,
  917. 'is--fixed-right': rightList.length,
  918. 'is--animat': !!this.animat,
  919. 'is--round': this.round,
  920. 'is--stripe': !treeConfig && stripe,
  921. 'is--loading': loading,
  922. 'is--empty': !loading && !tableData.length,
  923. 'is--scroll-y': overflowY,
  924. 'is--scroll-x': overflowX,
  925. 'is--virtual-x': scrollXLoad,
  926. 'is--virtual-y': scrollYLoad
  927. }]
  928. }, [
  929. /**
  930. * 隐藏列
  931. */
  932. h('div', {
  933. class: 'vxe-table-slots',
  934. ref: 'hideColumn'
  935. }, this.$slots.default),
  936. h('div', {
  937. class: 'vxe-table--render-wrapper'
  938. }, [
  939. h('div', {
  940. class: 'vxe-table--main-wrapper'
  941. }, [
  942. /**
  943. * 表头
  944. */
  945. showHeader ? h('vxe-table-header', {
  946. ref: 'tableHeader',
  947. props: {
  948. tableData,
  949. tableColumn,
  950. tableGroupColumn,
  951. size: vSize
  952. }
  953. }) : _e(),
  954. /**
  955. * 表体
  956. */
  957. h('vxe-table-body', {
  958. ref: 'tableBody',
  959. props: {
  960. tableData,
  961. tableColumn,
  962. size: vSize
  963. }
  964. }),
  965. /**
  966. * 表尾
  967. */
  968. showFooter ? h('vxe-table-footer', {
  969. ref: 'tableFooter',
  970. props: {
  971. footerTableData,
  972. tableColumn,
  973. size: vSize
  974. }
  975. }) : _e()
  976. ]),
  977. h('div', {
  978. class: 'vxe-table--fixed-wrapper'
  979. }, [
  980. /**
  981. * 左侧固定区域
  982. */
  983. leftList && leftList.length && overflowX ? renderFixed(h, this, 'left') : _e(),
  984. /**
  985. * 右侧固定区域
  986. */
  987. rightList && rightList.length && overflowX ? renderFixed(h, this, 'right') : _e()
  988. ])
  989. ]),
  990. /**
  991. * 空数据
  992. */
  993. h('div', {
  994. ref: 'emptyPlaceholder',
  995. class: 'vxe-table--empty-placeholder'
  996. }, [
  997. h('div', {
  998. class: 'vxe-table--empty-content'
  999. }, renderEmptyContenet(h, this))
  1000. ]),
  1001. /**
  1002. * 边框线
  1003. */
  1004. h('div', {
  1005. class: 'vxe-table--border-line'
  1006. }),
  1007. /**
  1008. * 列宽线
  1009. */
  1010. h('div', {
  1011. class: 'vxe-table--resizable-bar',
  1012. style: overflowX ? {
  1013. 'padding-bottom': `${scrollbarHeight}px`
  1014. } : null,
  1015. ref: 'resizeBar'
  1016. }),
  1017. /**
  1018. * 加载中
  1019. */
  1020. h('div', {
  1021. class: ['vxe-table--loading vxe-loading', {
  1022. 'is--visible': loading
  1023. }]
  1024. }, [
  1025. h('div', {
  1026. class: 'vxe-loading--spinner'
  1027. })
  1028. ]),
  1029. /**
  1030. * 筛选
  1031. */
  1032. initStore.filter ? h('vxe-table-filter', {
  1033. ref: 'filterWrapper',
  1034. props: {
  1035. filterStore
  1036. }
  1037. }) : _e(),
  1038. /**
  1039. * 导入
  1040. */
  1041. initStore.import && this.importConfig ? h('vxe-import-panel', {
  1042. props: {
  1043. defaultOptions: this.importParams,
  1044. storeData: this.importStore
  1045. }
  1046. }) : _e(),
  1047. /**
  1048. * 导出/打印
  1049. */
  1050. initStore.export && (this.exportConfig || this.printConfig) ? h('vxe-export-panel', {
  1051. props: {
  1052. defaultOptions: this.exportParams,
  1053. storeData: this.exportStore
  1054. }
  1055. }) : _e(),
  1056. /**
  1057. * 快捷菜单
  1058. */
  1059. ctxMenuStore.visible && this.isCtxMenu ? h('vxe-table-context-menu', {
  1060. ref: 'ctxWrapper',
  1061. props: {
  1062. ctxMenuStore,
  1063. ctxMenuOpts
  1064. }
  1065. }) : _e(),
  1066. /**
  1067. * 通用提示
  1068. */
  1069. hasTip ? h('vxe-tooltip', {
  1070. ref: 'commTip',
  1071. props: {
  1072. isArrow: false,
  1073. enterable: false
  1074. }
  1075. }) : _e(),
  1076. /**
  1077. * 工具提示
  1078. */
  1079. hasTip ? h('vxe-tooltip', {
  1080. ref: 'tooltip',
  1081. props: tooltipOpts
  1082. }) : _e(),
  1083. /**
  1084. * 校验提示
  1085. */
  1086. hasTip && this.editRules && validOpts.showMessage && (validOpts.message === 'default' ? !height : validOpts.message === 'tooltip') ? h('vxe-tooltip', {
  1087. ref: 'validTip',
  1088. class: 'vxe-table--valid-error',
  1089. props: validOpts.message === 'tooltip' || tableData.length === 1 ? validTipOpts : null
  1090. }) : _e()
  1091. ])
  1092. },
  1093. methods
  1094. }