当前位置: 首页 > news >正文

react0.14+jquery固定表头表格

代码文件index.js如下:

// index.js
import React, { Component, PropTypes } from 'react';
import $ from 'jquery';
import './style.less';export default class CommonTable extends Component {static propTypes = {columns: PropTypes.array.isRequired,data: PropTypes.array.isRequired,height: PropTypes.number,rowHeight: PropTypes.number,className: PropTypes.string};static defaultProps = {height: 300,rowHeight: 36,className: '',};constructor(props) {super(props);this.state = {scrollbarHeight: 0,}}componentDidMount() {this._bindEvents();this._applyColumnWidths();this._syncScrollPositions(); // 初始同步}componentDidUpdate() {this._applyColumnWidths();this._syncScrollPositions();}componentWillUnmount() {this._unbindEvents();}_bindEvents() {const $body = $(this.refs.body);const $headerInner = $(this.refs.headerInner);const $scrollbar = $(this.refs.scrollbar);// 表体滚动时,header与底部滚动条同步$body.on('scroll.commonTable', () => {const left = $body.scrollLeft();$headerInner.css('margin-left', -left);$scrollbar.scrollLeft(left);});// 底部滚动条滚动时,body与header同步$scrollbar.on('scroll.commonTable', () => {const left = $scrollbar.scrollLeft();$headerInner.css('margin-left', -left);$body.scrollLeft(left);});$(window).on('resize.commonTable', () => this._applyColumnWidths());}_unbindEvents() {$(this.refs.body).off('.commonTable');$(this.refs.scrollbar).off('.commonTable');$(window).off('resize.commonTable');}_syncScrollPositions() {// 保证三者初始 scrollLeft 一致const $body = $(this.refs.body);const $scrollbar = $(this.refs.scrollbar);const $headerInner = $(this.refs.headerInner);const left = $body.scrollLeft() || $scrollbar.scrollLeft() || 0;$body.scrollLeft(left);$scrollbar.scrollLeft(left);$headerInner.css('margin-left', -left);}_applyColumnWidths2() {const cols = this.props.columns;const $headerRow = $(this.refs.headerRow);const $bodyTable = $(this.refs.bodyTable);const $bodyRows = $bodyTable.find('tr.ct-row');const containerWidth = $(this.refs.container).innerWidth();// 计算固定列宽和可伸缩列数let totalFixed = 0;let flexibleCount = 0;cols.forEach(col => {if (typeof col.width === 'number') totalFixed += col.width;else flexibleCount++;});let finalWidths = [];if (totalFixed >= containerWidth) {// 总列宽超过容器,固定列宽 + 横向滚动finalWidths = cols.map(col =>typeof col.width === 'number' ? col.width : 100);$(this.refs.body).css({ overflowX: 'auto' });if (this.state.scrollbarHeight === 0) {this.setState({ scrollbarHeight: 16 });}} else {// 列少,均分剩余空间const remaining = containerWidth - totalFixed;const flexWidth = flexibleCount > 0 ? Math.floor(remaining / flexibleCount) : 0;finalWidths = cols.map(col =>typeof col.width === 'number' ? col.width : flexWidth);$(this.refs.body).css({ overflowX: 'hidden' });if (this.state.scrollbarHeight === 16) {this.setState({ scrollbarHeight: 0 });}}const totalWidth = finalWidths.reduce((a, b) => a + b, 0);// 设置表格宽度等于容器宽度$bodyTable.css({ tableLayout: 'fixed', width: containerWidth });$(this.refs.headerRow).closest('table').css({ tableLayout: 'fixed', width: containerWidth });// 设置表头列宽$headerRow.find('th').each(function (idx) {$(this).css({ width: finalWidths[idx], boxSizing: 'border-box' });});// 设置表体所有 td 列宽$bodyRows.each(function () {$(this).find('td').each(function (idx) {$(this).css({ width: finalWidths[idx], boxSizing: 'border-box' });});});// 底部滚动条宽度$(this.refs.scrollbarInner).css({ width: totalWidth, height: 1 });}_applyColumnWidths() {const cols = this.props.columns;const $headerRow = $(this.refs.headerRow);const $bodyTable = $(this.refs.bodyTable);const $bodyRows = $bodyTable.find('tr.ct-row');const containerWidth = $(this.refs.container).innerWidth();// 计算固定列宽和可伸缩列数(只算非 fixedWidth 列)let totalFixed = 0;let flexibleCount = 0;cols.forEach(col => {if (!!col.fixedWidth || typeof col.width === 'number') totalFixed += col.width || 100;else flexibleCount++;});let finalWidths = [];if (totalFixed >= containerWidth) {// 总列宽超过容器,固定列宽 + 横向滚动finalWidths = cols.map(col => {if (!!col.fixedWidth || typeof col.width === 'number') return col.width;return 100; // 默认宽度});$(this.refs.body).css({ overflowX: 'auto' });if (this.state.scrollbarHeight === 0) this.setState({ scrollbarHeight: 16 });} else {// 列少,均分剩余空间const remaining = containerWidth - totalFixed;const flexWidth = flexibleCount > 0 ? Math.floor(remaining / flexibleCount) : 0;finalWidths = cols.map(col => {if (!!col.fixedWidth || typeof col.width === 'number') return col.width;return flexWidth;});$(this.refs.body).css({ overflowX: 'hidden' });if (this.state.scrollbarHeight === 16) this.setState({ scrollbarHeight: 0 });}const totalWidth = finalWidths.reduce((a, b) => a + b, 0);// 设置表格宽度等于容器宽度$bodyTable.css({ tableLayout: 'fixed', width: containerWidth });$(this.refs.headerRow).closest('table').css({ tableLayout: 'fixed', width: containerWidth });// 应用列宽到表头$headerRow.find('th').each(function (idx) {$(this).css({ width: finalWidths[idx], boxSizing: 'border-box' });});// 应用列宽到表体所有 td$bodyRows.each(function () {$(this).find('td').each(function (idx) {$(this).css({ width: finalWidths[idx], boxSizing: 'border-box' });});});// 底部滚动条宽度$(this.refs.scrollbarInner).css({ width: totalWidth, height: 1 });}renderHeader() {const { columns } = this.props;return (<table className="ct-table ct-header-table" ref="headerTable"><thead><tr ref="headerRow">{columns.map(col => (<thkey={col.key || col.dataIndex}title={col.title || col.key || col.dataIndex}className="ct-cell"style={{ textAlign: col.align ? col.align : 'center' }}>{col.title || col.key || col.dataIndex}</th>))}</tr></thead></table>);}renderBody() {const { data, columns, rowHeight } = this.props;return (<table className="ct-table ct-body-table" ref="bodyTable"><tbody>{data.map((row, rIdx) => (<tr className="ct-row" key={rIdx} style={{ height: rowHeight }}>{columns.map(col => (<tdkey={col.key || col.dataIndex}className="ct-cell"title={row[col.key || col.dataIndex] != null ? String(row[col.key || col.dataIndex]) : ''}style={{ textAlign: col.align ? col.align : 'left' }}>{col.render? col.render(row[col.key || col.dataIndex], row, rIdx): row[col.key || col.dataIndex]}</td>))}</tr>))}</tbody></table>);}render() {const { className, height } = this.props;const { scrollbarHeight = 16 } = this.state; // 底部横向滚动条高度const headerHeight = 36; // 表头固定高度// 表体高度 = 总高度 - 表头 - 底部滚动条const bodyHeight = height - headerHeight - scrollbarHeight;return (<div className={`common-table ${className}`} ref="container" style={{ height }}><div className="ct-header-outer" style={{ height: headerHeight }}><div className="ct-header-inner" ref="headerInner">{this.renderHeader()}</div></div>{/* 表体 + 底部滚动条占位布局 */}<div className="ct-body-scroll-wrapper" style={{ height: bodyHeight + scrollbarHeight }}><divclassName="ct-body-outer"ref="body"style={{ height: bodyHeight, overflowY: 'auto', overflowX: 'auto' }}>{this.renderBody()}</div>{/* 底部滚动条占位 */}<div className="ct-scrollbar" ref="scrollbar" style={{ height: scrollbarHeight }}><div className="ct-scrollbar-inner" ref="scrollbarInner" /></div></div></div>);}
}

样式style.less如下:

.common-table {position: relative;width: 100%;font-family: Arial, Helvetica, sans-serif;border: 1px solid #DFE1E6;background: #fff;
}.ct-header-outer {background: #fff;border-bottom: 1px solid #DFE1E6;overflow: hidden;
}.ct-header-inner {width: 100%;transition: margin-left 0.05s linear;
}/* 包裹表体和底部滚动条,使用占位,不覆盖表体内容 */
.ct-body-scroll-wrapper {position: relative;width: 100%;display: flex;flex-direction: column;
}/* 表体 */
.ct-body-outer {width: 100%;overflow-y: auto;overflow-x: auto;flex-shrink: 0;
}/* 隐藏 body 自带水平滚动条,但显示竖向滚动条 */
.ct-body-outer::-webkit-scrollbar {width: 8px;height: 0; /* 隐藏水平滚动条 */
}
.ct-body-outer::-webkit-scrollbar-thumb {background: #dddddd;border-radius: 6px;border: 1px solid #dddddd;
}
.ct-body-outer::-webkit-scrollbar-track {background: transparent;
}
.ct-body-outer {scrollbar-width: auto;-ms-overflow-style: auto;
}.ct-table {border-collapse: collapse;width: 100%;table-layout: fixed;
}.ct-header-table th {padding: 8px 10px;border-right: 1px solid #DFE1E6;background: #E9EBF0;box-sizing: border-box;font-size: 14px;color: #333333;text-align: center;line-height: 20px;font-weight: 400;
}.ct-body-table td {padding: 8px 10px;border-right: 1px solid #DFE1E6;border-bottom: 1px solid #E9EBF0;box-sizing: border-box;font-size: 14px;color: #333333;line-height: 20px;font-weight: 400;
}.ct-cell {overflow: hidden;text-overflow: ellipsis;white-space: nowrap;cursor: default;
}/* 底部滚动条不再绝对定位,占位在表体下方 */
.ct-scrollbar {width: 100%;overflow-x: scroll;overflow-y: hidden;background: #fafafa;border-top: 1px solid #ddd;flex-shrink: 0;height: 16px;
}.ct-scrollbar-inner {height: 1px;
}
http://www.jsqmd.com/news/25400/

相关文章:

  • 2025年新疆租车公司权威推荐榜单:新疆自驾游租车/新疆乌鲁木齐租车/新疆租皮卡车源头服务商精选
  • 芜湖,千兆网络下载速率只有10MB秒,过的什么苦日子
  • 从“能用”到“好用”:兰亭妙微谈小程序设计的5个高转化秘诀
  • 2025 年固定式探伤室,大型辐射防护探伤室,移动式探伤室厂家最新推荐,产能、专利、环保三维数据透视
  • 2025 年辐射防护探伤室,工业射线探伤室,焊接工艺探伤室厂家最新推荐,资质、案例、售后三维透视与选购指南!
  • 2025年比较好的低温压榨粮油厂家推荐及采购指南
  • 2025 年工业探伤室,组合式探伤室,X 射线探伤室,γ 射线探伤室厂家最新推荐,实力品牌深度解析采购无忧之选!
  • 数据驱动的DevOps:如何通过一体化平台破解研发协同效率瓶颈?
  • 2025 年集成式污水处理设备,养殖污水处理设备,重金属污水处理设备,絮凝沉淀污水处理设备厂家最新推荐,资质、案例、性能三维数据透视
  • 2025年口碑好的钢木课桌椅厂家实力及用户口碑排行榜
  • 2025 年 VOC 废气污水处理设备,粪便污水处理设备,锅炉脱硫脱硝污水处理设备厂家最新推荐,产能、专利、环保三维数据透视!
  • 2025年比较好的机械重型纸箱厂家最新权威实力榜
  • 2025 年除尘污水处理设备,一体化污水处理设备,智能化污水处理设备厂家最新推荐,产能、专利、环保三维数据透视
  • 2025年广州沥青施工公司权威推荐:沥青路面修补施工/沥青施工/沥青路面摊铺/彩色沥青施工源头施工企业精选
  • 半结构化数据表头解析vlm方案
  • 2025年靠谱的新中式全品类五金厂家实力及用户口碑排行榜
  • 2025年质量好的超薄型防火涂料厂家最新权威实力榜
  • 2025年评价高的DPA漆雾空气过滤器厂家最新用户好评榜
  • 2025 年聚氨酯砂浆地坪厂家最新推荐榜,技术实力与市场口碑深度解析的优质品牌筛选
  • 2025年知名的景区冰雕厂家最新推荐权威榜
  • 兰亭妙微审美积累 | 温暖插画设计融入UI设计(二)
  • 2025年口碑好的斜齿轮减速电机实力厂家TOP推荐榜
  • 干扰来向变化情况下的阵列天线Laplace零陷加宽算法
  • 2025年质量好的职业装定制用户好评厂家排行
  • Scrapy采集网页出现丢失url的问题,加dont_filter参数
  • ASP.NET Core Blazor 核心功能一:Blazor依赖注入与状态管理指南
  • 2025年热门的垃圾袋厂家最新权威推荐排行榜
  • 2025年评价高的烟台食品包装厂家推荐及采购参考
  • 2025年比较好的单层玻璃隔断厂家最新TOP实力排行
  • 2025年10月工业大风扇厂家推荐榜:五家实力厂商对比