u-table2.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. <template>
  2. <view class="u-table2" :class="{ 'u-table-border': border }">
  3. <scroll-view scroll-x class="u-table2-content" :style="{ height: height ? height + 'px' : 'auto' }" @scroll="onScroll">
  4. <!-- 表头 -->
  5. <view v-if="showHeader" class="u-table-header" :class="{ 'u-table-sticky': fixedHeader }" :style="{minWidth: scrollWidth}">
  6. <view class="u-table-row">
  7. <view v-for="(col, colIndex) in columns" :key="col.key" class="u-table-cell"
  8. :style="headerColStyle(col)"
  9. :class="[col.align ? 'u-text-' + col.align : '',
  10. headerCellClassName ? headerCellClassName(col) : '',
  11. getFixedClass(col)
  12. ]" @click="handleHeaderClick(col)">
  13. <slot name="header" :column="col" :columnIndex="colIndex" :level="1">
  14. </slot>
  15. <text v-if="!$slots['header']">{{ col.title }}</text>
  16. <template v-if="col.sortable">
  17. <slot name="headerSort" :sortStatus="getSortValue(col.key)" :column="col"
  18. :columnIndex="colIndex" :level="1">
  19. </slot>
  20. <view v-if="!$slots['headerSort']">
  21. {{ getSortIcon(col.key) }}
  22. </view>
  23. </template>
  24. </view>
  25. </view>
  26. </view>
  27. <!-- 表体 -->
  28. <view class="u-table-body" :style="{ minWidth: scrollWidth, maxHeight: maxHeight ? maxHeight + 'px' : 'none' }">
  29. <template v-if="data && data.length > 0">
  30. <table-row
  31. v-for="(row, rowIndex) in sortedData"
  32. :key="row[rowKey] || rowIndex"
  33. :row="row"
  34. :rowIndex="rowIndex"
  35. :parent-row="null"
  36. :columns="columns"
  37. :tree-props="treeProps"
  38. :row-key="rowKey"
  39. :expanded-keys="expandedKeys"
  40. :cell-style-inner="cellStyleInner"
  41. :is-expanded="isExpanded"
  42. :row-class-name="rowClassName"
  43. :stripe="stripe"
  44. :cell-class-name="cellClassName"
  45. :get-fixed-class="getFixedClass"
  46. :highlight-current-row="highlightCurrentRow"
  47. :current-row="currentRow"
  48. :handle-row-click="handleRowClick"
  49. :toggle-expand="toggleExpand"
  50. :level="1"
  51. :rowHeight="rowHeight"
  52. :hasTree="hasTree"
  53. :selectedRows="selectedRows"
  54. :expandWidth="expandWidth"
  55. :computedMainCol="computedMainCol"
  56. @toggle-select="toggleSelect"
  57. @row-click="handleRowClick"
  58. @toggle-expand="toggleExpand"
  59. >
  60. <template v-slot:cellChild="scope">
  61. <slot name="cell" :row="scope.row" :column="scope.column" :prow="scope.prow"
  62. :rowIndex="scope.rowIndex" :columnIndex="scope.columnIndex" :level="scope.level">
  63. </slot>
  64. </template>
  65. </table-row>
  66. </template>
  67. <template v-else>
  68. <slot name="empty">
  69. </slot>
  70. <view v-if="!$slots['empty']" class="u-table-empty">{{ emptyText }}</view>
  71. </template>
  72. </view>
  73. </scroll-view>
  74. <!-- 固定列浮动视图 -->
  75. <view v-if="showFixedColumnShadow" class="u-table-fixed-shadow" :style="{ height: tableHeight }">
  76. <!-- 表头 -->
  77. <view v-if="showHeader" class="u-table-header" :class="{ 'u-table-sticky': fixedHeader }" :style="{minWidth: scrollWidth}">
  78. <view class="u-table-row" :style="{height: headerHeight}">
  79. <view v-for="(col, colIndex) in visibleFixedLeftColumns" :key="col.key" class="u-table-cell"
  80. :style="headerColStyle(col)"
  81. :class="[col.align ? 'u-text-' + col.align : '',
  82. headerCellClassName ? headerCellClassName(col) : '',
  83. getFixedClass(col)
  84. ]" @click="handleHeaderClick(col)">
  85. <slot name="header" :column="col" :columnIndex="colIndex" :level="1">
  86. </slot>
  87. <text v-if="!$slots['header']">{{ col.title }}</text>
  88. <template v-if="col.sortable">
  89. <slot name="headerSort" :sortStatus="getSortValue(col.key)" :column="col"
  90. :columnIndex="colIndex" :level="1">
  91. </slot>
  92. <view v-if="!$slots['headerSort']">
  93. {{ getSortIcon(col.key) }}
  94. </view>
  95. </template>
  96. </view>
  97. </view>
  98. </view>
  99. <!-- 表体 -->
  100. <view class="u-table-body" :style="{ minWidth: scrollWidth, maxHeight: maxHeight ? maxHeight + 'px' : 'none' }">
  101. <template v-if="data && data.length > 0">
  102. <template v-for="(row, rowIndex) in sortedData" :key="row[rowKey] || rowIndex">
  103. <!-- 子级渲染 (递归组件) -->
  104. <table-row
  105. :row="row"
  106. :rowIndex="rowIndex"
  107. :parent-row="null"
  108. :columns="visibleFixedLeftColumns"
  109. :tree-props="treeProps"
  110. :row-key="rowKey"
  111. :expanded-keys="expandedKeys"
  112. :cell-style-inner="cellStyleInner"
  113. :is-expanded="isExpanded"
  114. :row-class-name="rowClassName"
  115. :stripe="stripe"
  116. :cell-class-name="cellClassName"
  117. :get-fixed-class="getFixedClass"
  118. :highlight-current-row="highlightCurrentRow"
  119. :current-row="currentRow"
  120. :handle-row-click="handleRowClick"
  121. :toggle-expand="toggleExpand"
  122. :level="1"
  123. :rowHeight="rowHeight"
  124. :hasTree="hasTree"
  125. :selectedRows="selectedRows"
  126. :expandWidth="expandWidth"
  127. :computedMainCol="computedMainCol"
  128. @toggle-select="toggleSelect"
  129. @row-click="handleRowClick"
  130. @toggle-expand="toggleExpand"
  131. >
  132. <template v-slot:cellChild="scope">
  133. <slot name="cell" :row="scope.row" :column="scope.column" :prow="scope.prow"
  134. :rowIndex="scope.rowIndex" :columnIndex="scope.columnIndex" :level="scope.level">
  135. </slot>
  136. </template>
  137. </table-row>
  138. </template>
  139. </template>
  140. </view>
  141. </view>
  142. </view>
  143. </template>
  144. <script>
  145. import { addUnit, sleep } from '../../libs/function/index';
  146. import tableRow from './tableRow.vue'; // 引入递归组件
  147. export default {
  148. name: 'u-table2',
  149. components: {
  150. tableRow // 注册递归组件
  151. },
  152. props: {
  153. data: {
  154. type: Array,
  155. required: true,
  156. default: () => {
  157. return []
  158. }
  159. },
  160. columns: {
  161. type: Array,
  162. required: true,
  163. default: () => {
  164. return []
  165. },
  166. validator: cols =>
  167. cols.every(col =>
  168. ['default', 'selection', 'expand'].includes(col.type || 'default')
  169. )
  170. },
  171. stripe: {
  172. type: Boolean,
  173. default: false
  174. },
  175. border: {
  176. type: Boolean,
  177. default: false
  178. },
  179. height: {
  180. type: [String, Number],
  181. default: null
  182. },
  183. maxHeight: {
  184. type: [String, Number],
  185. default: null
  186. },
  187. showHeader: {
  188. type: Boolean,
  189. default: true
  190. },
  191. highlightCurrentRow: {
  192. type: Boolean,
  193. default: false
  194. },
  195. rowKey: {
  196. type: String,
  197. default: 'id'
  198. },
  199. currentRowKey: {
  200. type: [String, Number],
  201. default: null
  202. },
  203. rowStyle: {
  204. type: Object,
  205. default: () => ({})
  206. },
  207. cellClassName: {
  208. type: Function,
  209. default: null
  210. },
  211. cellStyle: {
  212. type: Function,
  213. default: null
  214. },
  215. headerCellClassName: {
  216. type: Function,
  217. default: null
  218. },
  219. rowClassName: {
  220. type: Function,
  221. default: null
  222. },
  223. context: {
  224. type: Object,
  225. default: null
  226. },
  227. showOverflowTooltip: {
  228. type: Boolean,
  229. default: false
  230. },
  231. lazy: {
  232. type: Boolean,
  233. default: false
  234. },
  235. load: {
  236. type: Function,
  237. default: null
  238. },
  239. treeProps: {
  240. type: Object,
  241. default: () => ({
  242. children: 'children',
  243. hasChildren: 'hasChildren'
  244. })
  245. },
  246. defaultExpandAll: {
  247. type: Boolean,
  248. default: false
  249. },
  250. expandRowKeys: {
  251. type: Array,
  252. default: () => []
  253. },
  254. sortOrders: {
  255. type: Array,
  256. default: () => ['ascending', 'descending']
  257. },
  258. sortable: {
  259. type: [Boolean, String],
  260. default: false
  261. },
  262. multiSort: {
  263. type: Boolean,
  264. default: false
  265. },
  266. sortBy: {
  267. type: String,
  268. default: null
  269. },
  270. sortMethod: {
  271. type: Function,
  272. default: null
  273. },
  274. filters: {
  275. type: Object,
  276. default: () => ({})
  277. },
  278. fixedHeader: {
  279. type: Boolean,
  280. default: true
  281. },
  282. emptyText: {
  283. type: String,
  284. default: '暂无数据'
  285. },
  286. // 添加mainCol属性,用于指定树形结构展开控制图标所在的列
  287. mainCol: {
  288. type: String,
  289. default: ''
  290. },
  291. expandWidth: {
  292. type: String,
  293. default: '25px'
  294. },
  295. rowHeight: {
  296. String,
  297. default: '40px'
  298. }
  299. },
  300. emits: [
  301. 'select', 'select-all', 'selection-change',
  302. 'cell-click', 'row-click', 'row-dblclick',
  303. 'header-click', 'sort-change', 'filter-change',
  304. 'current-change', 'expand-change'
  305. ],
  306. data() {
  307. return {
  308. scrollWidth: 'auto',
  309. // 将setup中的ref转换为data属性
  310. expandedKeys: [...this.expandRowKeys],
  311. selectedRows: [],
  312. sortConditions: [],
  313. currentRow: null,
  314. scrollLeft: 0, // 新增滚动位置数据
  315. showFixedColumnShadow: false, // 是否显示固定列阴影
  316. fixedLeftColumns: [], // 左侧固定列
  317. tableHeight: 'auto', // 表格高度
  318. headerHeight: 'auto', // 新增表头高度属性
  319. hasTree: false // 新增属性,用于判断是否存在树形结构
  320. }
  321. },
  322. mounted() {
  323. this.getComponentWidth()
  324. // 处理currentRowKey初始化
  325. if (this.currentRowKey !== null) {
  326. const found = this.data.find(item => item[this.rowKey] === this.currentRowKey);
  327. if (found) {
  328. this.currentRow = found;
  329. }
  330. }
  331. // 获取固定列
  332. this.fixedLeftColumns = this.columns.filter(col => col.fixed === 'left');
  333. },
  334. computed: {
  335. // 将setup中的computed转换为computed属性
  336. filteredData() {
  337. return this.data.filter(row => {
  338. return Object.keys(this.filters).every(key => {
  339. const filter = this.filters[key];
  340. if (!filter) return true;
  341. return row[key]?.toString().includes(filter.toString());
  342. });
  343. });
  344. },
  345. sortedData() {
  346. if (!this.sortConditions.length) return this.filteredData;
  347. const data = [...this.filteredData];
  348. return data.sort((a, b) => {
  349. for (const condition of this.sortConditions) {
  350. const { field, order } = condition;
  351. let valA = a[field];
  352. let valB = b[field];
  353. if (this.sortMethod) {
  354. const result = this.sortMethod(a, b, field);
  355. if (result !== 0) return result * (order === 'ascending' ? 1 : -1);
  356. }
  357. if (valA < valB) return order === 'ascending' ? -1 : 1;
  358. if (valA > valB) return order === 'ascending' ? 1 : -1;
  359. }
  360. return 0;
  361. });
  362. },
  363. // 计算当前应该显示的固定左侧列
  364. visibleFixedLeftColumns() {
  365. if (this.scrollLeft <= 0) {
  366. return [];
  367. }
  368. let totalWidth = 0;
  369. let fixedWidth = 0;
  370. const visibleColumns = [];
  371. // 遍历所有列,不仅仅是固定列
  372. for (let i = 0; i < this.columns.length; i++) {
  373. const col = this.columns[i];
  374. const colWidth = col.width ? parseInt(col.width) : 100; // 默认宽度100px
  375. // 如果是固定列且滚动位置足够显示该列
  376. if (col.fixed === 'left' && this.scrollLeft > totalWidth - fixedWidth) {
  377. visibleColumns.push(col);
  378. fixedWidth += colWidth;
  379. }
  380. totalWidth += colWidth;
  381. }
  382. return visibleColumns;
  383. },
  384. // 获取mainCol的值,如果未设置则默认为第一列的key
  385. computedMainCol() {
  386. if (this.mainCol) {
  387. return this.mainCol;
  388. }
  389. // 修改为排除有type值的列
  390. const validColumns = this.columns.filter(col => !col.type);
  391. let mainCol = validColumns && validColumns.length > 0 ? validColumns[0].key : '';
  392. // console.log('mainCol', mainCol)
  393. return mainCol;
  394. }
  395. },
  396. watch: {
  397. // 将setup中的watch转换为watch属性
  398. expandRowKeys: {
  399. handler(newVal) {
  400. this.expandedKeys = [...newVal];
  401. },
  402. immediate: true
  403. },
  404. currentRowKey: {
  405. handler(newVal) {
  406. const found = this.data.find(item => item[this.rowKey] === newVal);
  407. if (found) {
  408. this.currentRow = found;
  409. }
  410. },
  411. immediate: true
  412. },
  413. columns: {
  414. handler() {
  415. // this.fixedLeftColumns = this.columns.filter(col => col.fixed === 'left');
  416. },
  417. deep: true,
  418. immediate: false
  419. }
  420. },
  421. methods: {
  422. addUnit,
  423. onScroll(e) {
  424. this.scrollLeft = e.detail.scrollLeft;
  425. // 获取所有左侧固定列
  426. this.fixedLeftColumns = this.columns.filter(col => col.fixed === 'left');
  427. // 计算是否需要显示固定列阴影
  428. if (this.fixedLeftColumns.length > 0) {
  429. this.showFixedColumnShadow = this.scrollLeft > 0;
  430. }
  431. },
  432. getFixedShadowStyle(col, index) {
  433. let style = {
  434. width: col.width ? addUnit(col.width) : 'auto',
  435. };
  436. if (col?.style) {
  437. style = {...style, ...col?.style};
  438. }
  439. return style;
  440. },
  441. getFixedClass(col) {
  442. return ''; // 不再使用原来的固定列样式类
  443. },
  444. headerColStyle(col) {
  445. let style = {
  446. width: col.width ? addUnit(col.width) : 'auto',
  447. flex: col.width ? 'none' : 1
  448. };
  449. if (col?.style) {
  450. style = {...style, ...col?.style};
  451. }
  452. return style;
  453. },
  454. setCellStyle(e) {
  455. this.cellStyle = e
  456. },
  457. cellStyleInner(scope) {
  458. let style = {
  459. width: scope.column?.width ? addUnit(scope.column.width) : 'auto',
  460. flex: scope.column?.width ? 'none' : 1
  461. };
  462. // 只有展开列设置padding
  463. if (scope.column.key == this.computedMainCol) {
  464. style.paddingLeft = (16 * (scope.level -1 )) + 2 + 'px'
  465. }
  466. if (this.cellStyle != null) {
  467. let styleCalc = this.cellStyle(scope)
  468. if (styleCalc != null) {
  469. style = {...style, ...styleCalc}
  470. }
  471. }
  472. return style;
  473. },
  474. // 获取组件的宽度
  475. async getComponentWidth() {
  476. // 延时一定时间,以获取dom尺寸
  477. await sleep(30)
  478. this.$uGetRect('.u-table-row').then(size => {
  479. this.scrollWidth = size.width + 'px'
  480. })
  481. // 获取表头高度并设置
  482. this.$uGetRect('.u-table-header').then(size => {
  483. if (size.height) {
  484. this.headerHeight = size.height + 'px';
  485. }
  486. })
  487. // 遍历数据列表第一层判断是否存在树形结构
  488. this.hasTree = this.sortedData.some(item => {
  489. return item[this.treeProps.children] && item[this.treeProps.children].length > 0;
  490. });
  491. },
  492. // 将setup中的函数转换为methods
  493. handleRowClick(row) {
  494. if (this.highlightCurrentRow) {
  495. const oldRow = this.currentRow;
  496. this.currentRow = row;
  497. this.$emit('current-change', row, oldRow);
  498. }
  499. this.$emit('row-click', row);
  500. },
  501. handleHeaderClick(column) {
  502. if (!column.sortable) return;
  503. const index = this.sortConditions.findIndex(c => c.field === column.key);
  504. let newOrder = 'ascending';
  505. if (index >= 0) {
  506. if (this.sortConditions[index].order === 'ascending') {
  507. newOrder = 'descending';
  508. } else {
  509. this.sortConditions.splice(index, 1);
  510. this.$emit('sort-change', this.sortConditions);
  511. return;
  512. }
  513. }
  514. if (!this.multiSort) {
  515. this.sortConditions = [{ field: column.key, order: newOrder }];
  516. } else {
  517. if (index >= 0) {
  518. this.sortConditions[index].order = newOrder;
  519. } else {
  520. this.sortConditions.push({ field: column.key, order: newOrder });
  521. }
  522. }
  523. this.$emit('sort-change', this.sortConditions);
  524. },
  525. getSortIcon(field) {
  526. const cond = this.sortConditions.find(c => c.field === field);
  527. if (!cond) return '';
  528. return cond.order === 'ascending' ? '↑' : '↓';
  529. },
  530. getSortValue(field) {
  531. const cond = this.sortConditions.find(c => c.field === field);
  532. if (!cond) return '';
  533. return cond.order === 'ascending';
  534. },
  535. toggleSelect(row) {
  536. const index = this.selectedRows.findIndex(r => r[this.rowKey] === row[this.rowKey]);
  537. if (index >= 0) {
  538. // 取消选中当前行及其所有子节点
  539. this.selectedRows.splice(index, 1);
  540. // 递归取消所有子节点
  541. this.unselectChildren(row);
  542. } else {
  543. // 选中当前行及其所有子节点
  544. this.selectedRows.push(row);
  545. // 递归选中所有子节点
  546. this.selectChildren(row);
  547. }
  548. console.log(this.selectedRows)
  549. this.$emit('selection-change', this.selectedRows);
  550. this.$emit('select', row);
  551. },
  552. toggleExpand(row) {
  553. // console.log(row)
  554. const key = row[this.rowKey];
  555. const index = this.expandedKeys.indexOf(key);
  556. if (index === -1) {
  557. this.expandedKeys.push(key);
  558. } else {
  559. this.expandedKeys.splice(index, 1);
  560. }
  561. this.$emit('expand-change', this.expandedKeys);
  562. },
  563. isExpanded(row) {
  564. if (!row) {
  565. return false;
  566. }
  567. return this.expandedKeys.includes(row[this.rowKey]);
  568. },
  569. // 新增方法:递归选中所有子节点
  570. selectChildren(row) {
  571. const children = row[this.treeProps.children];
  572. if (children && children.length > 0) {
  573. children.forEach(child => {
  574. // 检查是否已选中,避免重复添加
  575. const childIndex = this.selectedRows.findIndex(r => r[this.rowKey] === child[this.rowKey]);
  576. if (childIndex === -1) {
  577. this.selectedRows.push(child);
  578. }
  579. // 递归处理子节点的子节点
  580. this.selectChildren(child);
  581. });
  582. }
  583. },
  584. // 新增方法:递归取消选中所有子节点
  585. unselectChildren(row) {
  586. const children = row[this.treeProps.children];
  587. if (children && children.length > 0) {
  588. children.forEach(child => {
  589. const childIndex = this.selectedRows.findIndex(r => r[this.rowKey] === child[this.rowKey]);
  590. if (childIndex >= 0) {
  591. this.selectedRows.splice(childIndex, 1);
  592. }
  593. // 递归处理子节点的子节点
  594. this.unselectChildren(child);
  595. });
  596. }
  597. },
  598. }
  599. };
  600. </script>
  601. <style lang="scss" scoped>
  602. .u-table2 {
  603. width: auto;
  604. overflow: auto;
  605. white-space: nowrap;
  606. position: relative;
  607. .u-table-header {
  608. min-width: 100% !important;
  609. width: fit-content;
  610. background-color: #f5f7fa;
  611. }
  612. .u-table-body {
  613. min-width: 100% !important;
  614. width: fit-content;
  615. position: relative;
  616. }
  617. .u-table-sticky {
  618. position: sticky;
  619. top: 0;
  620. z-index: 10;
  621. }
  622. .u-table-row {
  623. display: flex;
  624. flex-direction: row;
  625. align-items: center;
  626. border-bottom: 1px solid #ebeef5;
  627. overflow: hidden;
  628. position: relative;
  629. // min-height: 40px;
  630. }
  631. // 添加border样式支持
  632. &.u-table-border {
  633. border-top: 1px solid #ebeef5;
  634. border-left: 1px solid #ebeef5;
  635. border-right: 1px solid #ebeef5;
  636. .u-table-cell {
  637. border-right: 1px solid #ebeef5;
  638. }
  639. .u-table-cell:last-child {
  640. border-right: none;
  641. }
  642. }
  643. .u-table-cell {
  644. flex: 1;
  645. display: flex;
  646. flex-direction: row;
  647. padding: 10px 10px;
  648. font-size: 14px;
  649. white-space: nowrap;
  650. overflow: hidden;
  651. text-overflow: ellipsis;
  652. line-height: 1.1;
  653. }
  654. .u-table-row-zebra {
  655. background-color: #fafafa;
  656. }
  657. .u-table-row-highlight {
  658. background-color: #f5f7fa;
  659. }
  660. .u-table-empty {
  661. text-align: center;
  662. padding: 20px;
  663. color: #999;
  664. }
  665. .u-text-left {
  666. text-align: left;
  667. }
  668. .u-text-center {
  669. text-align: center;
  670. }
  671. .u-text-right {
  672. text-align: right;
  673. }
  674. }
  675. // 固定列浮动视图
  676. .u-table-fixed-shadow {
  677. position: absolute;
  678. top: 0;
  679. left: 0;
  680. width: auto;
  681. z-index: 20;
  682. box-shadow: 2px 0 5px rgba(0, 0, 0, 0.15);
  683. overflow: hidden;
  684. background-color: #ffffff;
  685. }
  686. // .u-table-fixed-row {
  687. // display: flex;
  688. // flex-direction: row;
  689. // align-items: center;
  690. // border-bottom: 1rpx solid #ebeef5;
  691. // position: relative;
  692. // }
  693. // 为固定列也添加border样式支持
  694. .u-table-fixed-shadow .u-table-border {
  695. .u-table-cell {
  696. border-right: 1rpx solid #ebeef5;
  697. }
  698. .u-table-cell:last-child {
  699. border-right: none;
  700. }
  701. }
  702. </style>