You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

417 lines
13 KiB

  1. <style lang="scss" scoped>
  2. .u-table-row {
  3. display: flex;
  4. flex-direction: row;
  5. position: relative;
  6. color: var(--up-content-color, #606266);
  7. }
  8. // 添加border样式支持
  9. .u-table-border {
  10. border-top: 1px solid var(--up-border-color, #ebeef5);
  11. border-left: 1px solid var(--up-border-color, #ebeef5);
  12. border-right: 1px solid var(--up-border-color, #ebeef5);
  13. .u-table-cell {
  14. border-right: 1px solid var(--up-border-color, #ebeef5);
  15. }
  16. .u-table-cell:last-child {
  17. border-right: none;
  18. }
  19. }
  20. .u-table-cell {
  21. flex: 1;
  22. display: flex;
  23. flex-direction: row;
  24. align-items: center;
  25. min-width: 0;
  26. box-sizing: border-box;
  27. padding: 10px 1px;
  28. font-size: 14px;
  29. white-space: nowrap;
  30. overflow: hidden;
  31. text-overflow: ellipsis;
  32. line-height: 1.1;
  33. color: var(--up-content-color, #606266);
  34. border-bottom: 1px solid var(--up-border-color, #ebeef5);
  35. &.u-text-left {
  36. justify-content: flex-start;
  37. text-align: left;
  38. }
  39. &.u-text-center {
  40. justify-content: center;
  41. text-align: center;
  42. }
  43. &.u-text-right {
  44. justify-content: flex-end;
  45. text-align: right;
  46. }
  47. }
  48. .u-table-cell_content {
  49. width: 100%;
  50. min-width: 0;
  51. max-width: 100%;
  52. flex: 1;
  53. box-sizing: border-box;
  54. display: block;
  55. text-align: inherit;
  56. }
  57. .u-table-cell_text {
  58. width: 100%;
  59. min-width: 0;
  60. max-width: 100%;
  61. display: block;
  62. box-sizing: border-box;
  63. text-align: inherit;
  64. }
  65. .u-table-cell_content-overflow,
  66. .u-table-cell_content-overflow .u-table-cell_text {
  67. width: 100%;
  68. min-width: 0;
  69. max-width: 100%;
  70. display: block;
  71. overflow: hidden;
  72. text-overflow: ellipsis;
  73. white-space: nowrap;
  74. }
  75. .u-table-row-zebra {
  76. background-color: var(--up-table2-zebra-bg-color, #fafafa);
  77. }
  78. .u-table-row-highlight {
  79. background-color: var(--up-table2-highlight-bg-color, #f5f7fa);
  80. }
  81. .u-table-empty {
  82. text-align: center;
  83. padding: 20px;
  84. color: var(--up-tips-color, #999);
  85. }
  86. // 隐藏被合并的单元格
  87. .u-table-cell-hidden {
  88. opacity: 0;
  89. }
  90. // 合并单元格样式
  91. .u-table-cell-merged {
  92. z-index: 1;
  93. }
  94. </style>
  95. <template>
  96. <view class="u-table-row u-table-row-child"
  97. :class="[highlightCurrentRow && currentRow === row ? 'u-table-row-highlight' : '',
  98. rowClassName ? rowClassName(row, rowIndex, context) : '',
  99. stripe && rowIndex % 2 === 1 ? 'u-table-row-zebra' : ''
  100. ]" :style="[{height: rowHeight}, getRowStyle(row, rowIndex)]" @click="handleRowClick(row)">
  101. <view v-for="(col, colIndex) in columns" :key="col.key"
  102. class="u-table-cell"
  103. :class="[col.align ? 'u-text-' + col.align : '',
  104. cellClassName ? cellClassName(row, col, context) : '',
  105. getFixedClass(col),
  106. isOverflowTooltipEnabled(col) ? 'u-table-cell-overflow' : '',
  107. getCellSpanClass(rowIndex, colIndex)
  108. ]"
  109. :style="[cellStyleInner({row: row, column: col, rowIndex: rowIndex, columnIndex: colIndex, level: level}), getCellSpanStyle(rowIndex, colIndex)]">
  110. <!-- 复选框列 -->
  111. <view v-if="col.type === 'selection'">
  112. <checkbox :checked="isSelected(row)"
  113. @click.stop="$emit('toggleSelect', row)" />
  114. </view>
  115. <template v-else>
  116. <!-- 在mainCol列显示展开图标 -->
  117. <view v-if="col.key === computedMainCol && hasTree"
  118. @click.stop="toggleExpand(row, level)" :style="{width: expandWidth}">
  119. <view v-if="hasExpandableChildren(row)">
  120. {{ isLazyLoading(row) ? '...' : (isExpanded(row) ? '▼' : '▶') }}
  121. </view>
  122. </view>
  123. <view class="u-table-cell_content"
  124. :class="{ 'u-table-cell_content-overflow': isOverflowTooltipEnabled(col) }">
  125. <slot name="cellChild" :row="row" :column="col" :prow="parentRow"
  126. :rowIndex="rowIndex" :columnIndex="colIndex" :level="level" :context="context">
  127. <text class="u-table-cell_text" :class="{ 'u-line-1': isOverflowTooltipEnabled(col) }" :lines="isOverflowTooltipEnabled(col) ? 1 : undefined">
  128. {{ row[col.key] }}
  129. </text>
  130. </slot>
  131. </view>
  132. </template>
  133. </view>
  134. </view>
  135. <!-- 递归渲染更深层的子级 -->
  136. <template v-if="isExpanded(row) && row[treeProps.children] && row[treeProps.children].length">
  137. <template v-for="(rowChild, childIndex) in row[treeProps.children]" :key="rowChild[rowKey] || childIndex">
  138. <table-row
  139. :row="rowChild"
  140. :rowIndex="childIndex"
  141. :parent-row="row"
  142. :columns="columns"
  143. :tree-props="treeProps"
  144. :row-key="rowKey"
  145. :expanded-keys="expandedKeys"
  146. :cell-style-inner="cellStyleInner"
  147. :is-expanded="isExpanded"
  148. :row-class-name="rowClassName"
  149. :row-style="rowStyle"
  150. :stripe="stripe"
  151. :cell-class-name="cellClassName"
  152. :get-fixed-class="getFixedClass"
  153. :context="context"
  154. :highlight-current-row="highlightCurrentRow"
  155. :current-row="currentRow"
  156. :handle-row-click="handleRowClick"
  157. :toggle-expand="toggleExpand"
  158. :level="level + 1"
  159. :rowHeight="rowHeight"
  160. :hasTree="hasTree"
  161. :selectedRows="selectedRows"
  162. :expandWidth="expandWidth"
  163. :computed-main-col="computedMainCol"
  164. :span-method="spanMethod"
  165. :show-overflow-tooltip="showOverflowTooltip"
  166. :has-expandable-children="hasExpandableChildren"
  167. :is-lazy-loading="isLazyLoading"
  168. @toggle-select="$emit('toggleSelect', $event)"
  169. @row-click="$emit('rowClick', $event)"
  170. @toggle-expand="$emit('toggleExpand', $event)"
  171. >
  172. <template v-slot:cellChild="scope">
  173. <slot name="cellChild" :row="scope.row" :column="scope.column" :prow="scope.prow"
  174. :rowIndex="scope.rowIndex" :columnIndex="scope.columnIndex" :level="level" :context="context">
  175. <text class="u-table-cell_text" :class="{ 'u-line-1': isOverflowTooltipEnabled(scope.column) }" :lines="isOverflowTooltipEnabled(scope.column) ? 1 : undefined">
  176. {{ scope.row[scope.column.key] }}
  177. </text>
  178. </slot>
  179. </template>
  180. </table-row>
  181. </template>
  182. </template>
  183. </template>
  184. <script>
  185. export default {
  186. name: 'tableRow',
  187. props: {
  188. row: {
  189. type: Object,
  190. required: true
  191. },
  192. rowIndex: {
  193. type: Number,
  194. required: true
  195. },
  196. parentRow: {
  197. type: Object,
  198. default: null
  199. },
  200. columns: {
  201. type: Array,
  202. required: true
  203. },
  204. treeProps: {
  205. type: Object,
  206. required: true
  207. },
  208. rowKey: {
  209. type: String,
  210. required: true
  211. },
  212. expandedKeys: {
  213. type: Array,
  214. required: true
  215. },
  216. cellStyleInner: {
  217. type: Function,
  218. required: true
  219. },
  220. isExpanded: {
  221. type: Function,
  222. required: true
  223. },
  224. rowClassName: {
  225. type: Function,
  226. default: null
  227. },
  228. rowStyle: {
  229. type: [Object, Function],
  230. default: () => ({})
  231. },
  232. stripe: {
  233. type: Boolean,
  234. default: false
  235. },
  236. cellClassName: {
  237. type: Function,
  238. default: null
  239. },
  240. getFixedClass: {
  241. type: Function,
  242. required: true
  243. },
  244. context: {
  245. type: Object,
  246. default: null
  247. },
  248. highlightCurrentRow: {
  249. type: Boolean,
  250. default: false
  251. },
  252. currentRow: {
  253. type: Object,
  254. default: null
  255. },
  256. handleRowClick: {
  257. type: Function,
  258. required: true
  259. },
  260. toggleExpand: {
  261. type: Function,
  262. required: true
  263. },
  264. level: {
  265. type: Number,
  266. required: true
  267. },
  268. // 添加computedMainCol属性
  269. computedMainCol: {
  270. type: String,
  271. required: true
  272. },
  273. showOverflowTooltip: {
  274. type: [Boolean, Object],
  275. default: false
  276. },
  277. expandWidth: {
  278. type: String,
  279. required: true
  280. },
  281. hasTree: {
  282. type: Boolean,
  283. required: false
  284. },
  285. selectedRows: {
  286. type: Array,
  287. required: false
  288. },
  289. rowHeight: {
  290. type: String,
  291. required: true
  292. },
  293. // 添加spanMethod属性
  294. spanMethod: {
  295. type: Function,
  296. default: null
  297. },
  298. hasExpandableChildren: {
  299. type: Function,
  300. required: true
  301. },
  302. isLazyLoading: {
  303. type: Function,
  304. required: true
  305. }
  306. },
  307. emits: ['rowClick', 'toggleExpand', 'toggleSelect'],
  308. methods: {
  309. isSelected(row) {
  310. return this.selectedRows.some(r => r[this.rowKey] === row[this.rowKey]);
  311. },
  312. isOverflowTooltipEnabled(column) {
  313. if (column && column.type === 'selection') {
  314. return false;
  315. }
  316. if (column && typeof column.showOverflowTooltip !== 'undefined') {
  317. return !!column.showOverflowTooltip;
  318. }
  319. return !!this.showOverflowTooltip;
  320. },
  321. getRowStyle(row, rowIndex) {
  322. if (typeof this.rowStyle === 'function') {
  323. return this.rowStyle({
  324. row,
  325. rowIndex,
  326. level: this.level,
  327. parentRow: this.parentRow,
  328. context: this.context
  329. }) || {};
  330. }
  331. return this.rowStyle || {};
  332. },
  333. // 获取单元格的合并信息
  334. getCellSpan(rowIndex, columnIndex) {
  335. if (typeof this.spanMethod !== 'function') {
  336. return { rowspan: 1, colspan: 1 };
  337. }
  338. const row = this.row;
  339. const column = this.columns[columnIndex];
  340. const result = this.spanMethod({
  341. row,
  342. column,
  343. rowIndex,
  344. columnIndex,
  345. context: this.context
  346. });
  347. if (Array.isArray(result)) {
  348. const [rowspan, colspan] = result;
  349. return { rowspan: rowspan != null ? rowspan : 1, colspan: colspan != null ? colspan : 1 };
  350. } else if (typeof result === 'object') {
  351. return {
  352. rowspan: rowspan != null ? rowspan : 1,
  353. colspan: colspan != null ? colspan : 1
  354. };
  355. }
  356. return { rowspan: 1, colspan: 1 };
  357. },
  358. // 获取单元格的样式类
  359. getCellSpanClass(rowIndex, columnIndex) {
  360. const span = this.getCellSpan(rowIndex, columnIndex);
  361. // 如果rowspan为0或colspan为0,表示该单元格被合并,需要隐藏
  362. if (span.rowspan === 0 || span.colspan === 0) {
  363. return 'u-table-cell-hidden';
  364. } else if (span.rowspan > 1 || span.colspan > 1) {
  365. // 如果有合并,添加合并样式类
  366. return 'u-table-cell-merged';
  367. }
  368. return '';
  369. },
  370. // 获取单元格的样式
  371. getCellSpanStyle(rowIndex, columnIndex) {
  372. const span = this.getCellSpan(rowIndex, columnIndex);
  373. const style = {};
  374. // 设置rowspan
  375. if (span.rowspan > 1) {
  376. // 正确计算合并后的高度
  377. const currentHeight = parseInt(this.rowHeight);
  378. if (!isNaN(currentHeight)) {
  379. style.height = `${span.rowspan * currentHeight}px`;
  380. }
  381. }
  382. // 设置colspan
  383. if (span.colspan > 1) {
  384. style.flex = span.colspan;
  385. }
  386. // 如果rowspan为0或colspan为0,表示该单元格被合并,需要隐藏
  387. if (span.rowspan === 0 || span.colspan === 0) {
  388. style.display = 'none';
  389. }
  390. return style;
  391. }
  392. }
  393. }
  394. </script>