一、需求描述
1、手动封装一个table,如果数据中有勾选显示勾选列。无勾选不显示这列。
2、table数据中如果有勾选的数据,勾选的这行row设置渐变背景色。
3、采用的是直接传入data,或者通过接口查询。
4、table内容超出时,显示横向滚动条,表头也要一起滚动。
二、效果图
三、代码实现
1、基础模式:直接每列添加宽度
组件封装
1、main.tsx // 级件页面
2、store.ts // ts
3、main.less // 样式
// index.tsx import React, { useEffect, useMemo, useState } from 'react'; import { languageConfig } from '@/language/language'; import { rebatePersonnelInformationListChildApi } from '@/api/rebatePersonnelInformation'; import { queryMapIdpValue } from 'services/api'; import { message, Tooltip } from 'choerodon-ui/pro'; import { handleFormatAmount } from '@/public/store'; import TableEmpty from '../TableEmpty'; import { ColumnConfig, StatusItems, TableData } from './store'; import styles from './main.less'; // 表头配置 const tableHeaders = [ { key: 'isIssueRebate', label: '', width: '50px', align: 'center' as const, visible: true, render: (item: TableData) => item.isIssueRebate === 'Y' ? ( <img src={require('@/assets/imgs/icon_success.svg')} alt="选中" style={{ width: '16px', height: '16px' }} /> ) : null, }, { key: 'serialNumber', label: languageConfig('', '序号'), width: '60px', align: 'center' as const, visible: true, render: (item: TableData, index: number) => index + 1, }, { key: 'personalName', label: languageConfig('', '认证人员'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => item.personalName, }, { key: 'certificationLevel', label: languageConfig('', '认证等级'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: () => '', // 动态计算 }, { key: 'baseNumber', label: languageConfig('', '等级基数'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => handleFormatAmount(item.baseNumber), }, { key: 'evaluationCoefficient', label: languageConfig('', '年度贡献评价'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => item.evaluationCoefficient, }, { key: 'rebateAmount', label: languageConfig('', '返点金额'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => handleFormatAmount(item.rebateAmount), }, ]; const PersonnelList: React.FC<{ id?: number; data?: TableData[] | null; // styleClass?: any; }> = ({ id, data }) => { const [tableData, setTableData] = useState<TableData[]>([]); // 表格数据 const [hasChecked, setHasChecked] = useState<boolean>(false); // 表格是否有勾选数据 // 认证类型、认证等级 const [certOptions, setCertOptions] = useState<{ type: StatusItems[]; level: StatusItems[]; }>({ type: [], level: [] }); /** 获取值集:'认证类型'、'认证等级' */ useEffect(() => { const fetchCertOptions = async () => { const res = await queryMapIdpValue([ 'PRM_AE_CERTIFICATION_LEVEL_2', 'PRM_AE_CERTIFICATION_LEVEL_3', ]); setCertOptions({ type: res?.[0] || [], level: res?.[1] || [] }); }; fetchCertOptions(); }, []); useEffect(() => { const fetchTableData = async () => { let resData: TableData[] = []; // 优先使用传入的 data,无则调用接口 if (data) { resData = data; } else if (id) { const res = await rebatePersonnelInformationListChildApi({ initialId: id, }); if (res.failed) { message.error(res.message, undefined, undefined, 'top'); return; } resData = res; } // 统一赋值 + 判断勾选状态 setTableData(resData); setHasChecked(resData.some(item => item.isIssueRebate === 'Y')); }; fetchTableData(); }, [id, data]); /** 【渲染】认证等级:认证类型-认证等级 */ const renderCertLevel = (item: TableData) => { const type = certOptions.type.find(v => v.value === item.certificationType)?.meaning || ''; const level = certOptions.level.find(v => v.value === item.certificationLevel) ?.meaning || ''; return `${type}-${level}`; }; /** 【渲染】单元格内容-认证等级 */ const renderCellContent = ( content: React.ReactNode, column: ColumnConfig, ) => { if (column.tooltip && content) { return ( <Tooltip theme="dark" title={content}> <span className={styles.tableCell_cellContent}>{content}</span> </Tooltip> ); } return <span className={styles.tableCell_cellContent}>{content}</span>; }; /** 【获取】可见的列配置(根据 hasChecked 过滤) */ const visibleColumns = useMemo(() => { return tableHeaders .map(col => { // 如果是认证等级列,更新render函数 if (col.key === 'certificationLevel') { return { ...col, render: (item: TableData) => renderCertLevel(item), }; } return col; }) .filter(col => { // 如果 hasChecked 为 false,隐藏 isIssueRebate 列 if (col.key === 'isIssueRebate' && !hasChecked) { return false; } return col.visible !== false; }); }, [hasChecked, certOptions]); return ( <> <div className={styles.tableWrapper}> <div className={styles.tableContainer}> {/* 表头 - 使用 visibleColumns */} <div className={styles.tableHeader}> <div className={styles.headerRow}> {visibleColumns.map(column => ( <div key={column.key} className={styles.headerCell} style={{ width: column.width, flex: `0 0 ${column.width}`, textAlign: column.align || 'left', }} > {column.label} </div> ))} </div> </div> {/* 表格内容 - 统一使用 visibleColumns 渲染 */} <div className={styles.tableBody}> {tableData.map((item, index) => { // 设置的背景是整行的背景 const rowStyle = { // 设置的背景是整行的背景 background: item.isIssueRebate === 'Y' ? 'linear-gradient(90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70%)' : undefined, // 设置整行的文字颜色 color: item.isIssueRebate === 'Y' ? '#222222' : '#788295', }; return ( <div key={index} className={styles.tableRow} style={rowStyle}> {visibleColumns.map(column => { const content = column.render ? column.render(item, index) : null; return ( <div key={column.key} className={styles.tableCell} style={{ width: column.width, flex: `0 0 ${column.width}`, textAlign: column.align || 'left', }} > {renderCellContent(content, column)} </div> ); })} </div> ); })} </div> </div> </div> </> ); }; export default PersonnelList; // store.ts export interface TableData { isIssueRebate: string; serialNumber: string; personalName: string; certificationLevel: string; certificationType: string; evaluationCoefficient: string; baseNumber: string; rebateAmount: string; } // 列对齐方式 export type AlignType = 'left' | 'center' | 'right'; export interface ColumnConfig { key: string; label: string; width: string; align?: AlignType; visible?: boolean; tooltip?: boolean; render: (item: TableData, index: number) => React.ReactNode; } export interface StatusItems { value?: string; meaning?: string; tag?: string; description?: string; } // main.less // 表格容器 .tableWrapper { width: 100%; background: #fff; display: flex; flex-direction: column; overflow: hidden; position: relative; z-index: 1; } // 表格主容器 .tableContainer { flex: 1; overflow: auto; position: relative; &::-webkit-scrollbar { width: 6px; height: 6px; } &::-webkit-scrollbar-track { background: #f5f5f5; border-radius: 3px; } &::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 3px; &:hover { background: #bfbfbf; } } } // 表头 .tableHeader { position: sticky; top: 0; z-index: 10; background: #fafafa; border-bottom: 1px solid #e5e6e8; min-width: fit-content; .headerRow { display: flex; width: 100%; min-width: 100%; } .headerCell { flex-shrink: 0; line-height: 36px; height: 36px; background: #f5faff; font-size: 12px; color: #000000; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; padding: 0 12px; // 第一列固定宽度 // &:first-child { // width: 100px; // flex: 0 0 100px; // } // 其他列均分剩余宽度 // &:not(:first-child) { // flex: 1; // min-width: 120px; // } } } // 表格内容 .tableBody { .tableRow { display: flex; width: 100%; min-width: 100%; border-bottom: 1px solid #e5e6e8; background: #fff; // 默认背景 // 斑马纹 // &:nth-child(even) { // background: pink; // } // 渐变行(JSX中设置的) &.gradientRow { background: linear-gradient( 90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70% ) !important; color: #222222 !important; // 如果是偶数行 &:nth-child(even) { background: linear-gradient( 90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70% ) !important; } // &:hover { // background: linear-gradient( // 90deg, // rgba(200, 237, 221, 0.9), // rgba(200, 237, 221, 0) 70% // ) !important; // } } // &:hover { // background: #f0f9ff; // } &:last-child { border-bottom: none; } } .tableCell { flex-shrink: 0; line-height: 36px; height: 36px; color: inherit; overflow: hidden; background: none !important; // 关键:不设置任何背景色,完全显示行背景 padding: 0 12px; // &:first-child { // width: 100px; // flex: 0 0 100px; // // border-right: 1px solid #f0f0f0; // } // &:not(:first-child) { // flex: 1; // min-width: 120px; // } &_cellContent { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 36px; color: inherit; } } }2、高级模式:组件传入type:前两列固定,后面均分
组件使用:
<PersonnelList
data={tableRecord?.rebateTechnologyPersonalVOList}
type="look"
/>
(1)组件代码
此模块的组件是,正确可使用版本。
1、样式需注意。
2、逻辑需注意
效果图1:
效果图2:(传入type)
import React, { useEffect, useMemo, useState } from 'react'; import { languageConfig } from '@/language/language'; import { rebatePersonnelInformationListChildApi } from '@/api/rebatePersonnelInformation'; import { queryMapIdpValue } from 'services/api'; import { message, Tooltip } from 'choerodon-ui/pro'; import { handleFormatAmount } from '@/public/store'; import TableEmpty from '../TableEmpty'; import { ColumnConfig, StatusItems, TableData } from './store'; import styles from './main.less'; // 表头配置 const tableHeaders = [ { key: 'isIssueRebate', label: '', width: '50px', align: 'center' as const, visible: true, render: (item: TableData) => item.isIssueRebate === 'Y' ? ( <img src={require('@/assets/imgs/icon_success.svg')} alt="选中" style={{ width: '16px', height: '16px' }} /> ) : null, }, { key: 'serialNumber', label: languageConfig('', '序号'), width: '60px', align: 'center' as const, visible: true, render: (item: TableData, index: number) => index + 1, }, { key: 'personalName', label: languageConfig('', '认证人员'), // width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => item.personalName, }, { key: 'certificationLevel', label: languageConfig('', '认证等级'), // width: '120px', align: 'left' as const, visible: true, tooltip: true, render: () => '', // 动态计算 }, { key: 'baseNumber', label: languageConfig('', '等级基数'), align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => handleFormatAmount(item.baseNumber), }, { key: 'evaluationCoefficient', label: languageConfig('', '年度贡献评价'), align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => item.evaluationCoefficient, }, { key: 'rebateAmount', label: languageConfig('', '返点金额'), align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => handleFormatAmount(item.rebateAmount), }, ]; const PersonnelList: React.FC<{ id?: number; data?: TableData[] | null; type?: string; }> = ({ id, data, type = '' }) => { const [tableData, setTableData] = useState<TableData[]>([]); const [hasChecked, setHasChecked] = useState<boolean>(false); const [certOptions, setCertOptions] = useState<{ type: StatusItems[]; level: StatusItems[]; }>({ type: [], level: [] }); /** 获取值集:'认证类型'、'认证等级' */ useEffect(() => { const fetchCertOptions = async () => { const res = await queryMapIdpValue([ 'PRM_AE_CERTIFICATION_LEVEL_2', 'PRM_AE_CERTIFICATION_LEVEL_3', ]); setCertOptions({ type: res?.[0] || [], level: res?.[1] || [] }); }; fetchCertOptions(); }, []); useEffect(() => { const fetchTableData = async () => { let resData: TableData[] = []; if (data) { resData = data; } else if (id) { const res = await rebatePersonnelInformationListChildApi({ initialId: id, }); if (res.failed) { message.error(res.message, undefined, undefined, 'top'); return; } resData = res; } setTableData(resData); setHasChecked(resData.some(item => item.isIssueRebate === 'Y')); }; fetchTableData(); }, [id, data]); /** 【渲染】认证等级:认证类型-认证等级 */ const renderCertLevel = (item: TableData) => { const type = certOptions.type.find(v => v.value === item.certificationType)?.meaning || ''; const level = certOptions.level.find(v => v.value === item.certificationLevel) ?.meaning || ''; return `${type}-${level}`; }; /** 【渲染】单元格内容 */ const renderCellContent = ( content: React.ReactNode, column: ColumnConfig, ) => { if (column.tooltip && content) { return ( <Tooltip theme="dark" title={content}> <span className={styles.prmRcTableWrapper__cellContent}> {content} </span> </Tooltip> ); } return ( <span className={styles.prmRcTableWrapper__cellContent}>{content}</span> ); }; /** 【获取】可见的列配置 */ const visibleColumns = useMemo(() => { const headers = tableHeaders.map(col => { if (col.key === 'certificationLevel') { return { ...col, render: (item: TableData) => renderCertLevel(item), }; } return col; }); return headers.filter(col => { if (col.key === 'isIssueRebate' && !hasChecked) { return false; } return col.visible !== false; }); }, [hasChecked, certOptions]); /** 【渲染】单元格样式 */ const getCellStyle = (column: ColumnConfig) => { const style: React.CSSProperties = { textAlign: column.align || 'left', }; // 如果有设置宽度,使用固定宽度 if (column.width) { style.width = column.width; style.flex = `0 0 ${column.width}`; } else { // 没有设置宽度的列均分剩余宽度 style.flex = '1'; style.minWidth = '120px'; } // 如果是 look 模式,序号列特殊处理 if (type === 'look' && column.key === 'serialNumber') { style.width = '50px'; style.flex = '0 0 50px'; } return style; }; /** 【计算】表格总宽度 */ const calculateTotalWidth = useMemo(() => { if (type === 'look') { return '100%'; // look 模式使用百分比宽度 } // 计算所有固定宽度列的总宽度 const fixedWidthColumns = visibleColumns.filter(col => col.width); const totalFixedWidth = fixedWidthColumns.reduce((sum, col) => { const widthMatch = col.width?.match(/(\d+)/); const width = widthMatch ? parseInt(widthMatch[1]) : 0; return sum + width; }, 0); // 为均分列预留最小宽度(假设每列最小120px) const autoWidthColumns = visibleColumns.filter(col => !col.width); const minAutoWidth = autoWidthColumns.length * 120; return totalFixedWidth + minAutoWidth + 20; // 加一些缓冲 }, [visibleColumns, type]); return ( <div className={styles.prmRcTableWrapper}> <div className={styles.prmRcTableWrapper__tableContainer} style={{ minWidth: type === 'look' ? '100%' : `${calculateTotalWidth}px`, }} > {/* 表头 */} <div className={styles.prmRcTableWrapper__tableHeader}> <div className={styles.prmRcTableWrapper__headerRow}> {visibleColumns.map(column => ( <div key={column.key} className={styles.prmRcTableWrapper__headerCell} style={getCellStyle(column)} > {column.label} </div> ))} </div> </div> {/* 表格内容 */} <div className={styles.prmRcTableWrapper__tableBody}> {tableData?.length > 0 ? ( tableData.map((item, index) => { // 设置的背景是整行的背景 const rowStyle = { // 设置的背景是整行的背景 background: item.isIssueRebate === 'Y' ? 'linear-gradient(90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70%)' : undefined, // 设置整行的文字颜色 color: item.isIssueRebate === 'Y' ? '#222222' : '#788295', }; return ( <div key={index} className={styles.prmRcTableWrapper__tableRow} style={rowStyle} > {visibleColumns.map(column => { const content = column.render ? column.render(item, index) : null; return ( <div key={column.key} className={styles.prmRcTableWrapper__tableCell} style={getCellStyle(column)} > {renderCellContent(content, column)} </div> ); })} </div> ); }) ) : ( <TableEmpty /> )} </div> </div> </div> ); }; export default PersonnelList; // main.less // 表格容器 - BEM风格 .prmRcTableWrapper { width: 100%; background: #fff; display: flex; flex-direction: column; overflow: hidden; position: relative; z-index: 1; // 表格主容器 &__tableContainer { flex: 1; overflow: auto; position: relative; &::-webkit-scrollbar { width: 6px; height: 6px; } &::-webkit-scrollbar-track { background: #f5f5f5; border-radius: 3px; } &::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 3px; &:hover { background: #bfbfbf; } } // 表头 .prmRcTableWrapper__tableHeader { position: sticky; top: 0; z-index: 10; background: #fafafa; border-bottom: 1px solid #e5e6e8; .prmRcTableWrapper__headerRow { display: flex; width: 100%; .prmRcTableWrapper__headerCell { flex-shrink: 0; line-height: 36px; height: 36px; background: #f5faff; font-size: 12px; color: #000000; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; padding: 0 12px; &:last-child { border-right: none; } } } } // 表格内容 .prmRcTableWrapper__tableBody { .prmRcTableWrapper__tableRow { display: flex; width: 100%; background: #fff; &:nth-child(even) { background: #fcfcfc; } &[style*='background:'] { background: linear-gradient( 90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70% ) !important; color: #222222 !important; &:nth-child(even) { background: linear-gradient( 90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70% ) !important; } } &:last-child { border-bottom: none; } .prmRcTableWrapper__tableCell { flex-shrink: 0; line-height: 36px; height: 36px; color: inherit; overflow: hidden; background: none !important; padding: 0 12px; border-bottom: 1px solid #e5e6e8; &:last-child { border-right: none; } // 单元格内容 .prmRcTableWrapper__cellContent { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 36px; color: inherit; } } } } } }(2)问题版本做参考
使用本段代码,出现问题,给了宽度,但是表格宽度过长,列设置的宽度总和不达表宽度,会出现空白,如下图:
效果图:
// index.tsx import React, { useEffect, useMemo, useState } from 'react'; import { languageConfig } from '@/language/language'; import { rebatePersonnelInformationListChildApi } from '@/api/rebatePersonnelInformation'; import { queryMapIdpValue } from 'services/api'; import { message, Tooltip } from 'choerodon-ui/pro'; import { handleFormatAmount } from '@/public/store'; import TableEmpty from '../TableEmpty'; import { ColumnConfig, StatusItems, TableData } from './store'; import styles from './main.less'; // 表头配置 const tableHeaders = [ { key: 'isIssueRebate', label: '', width: '50px', align: 'center' as const, visible: true, render: (item: TableData) => item.isIssueRebate === 'Y' ? ( <img src={require('@/assets/imgs/icon_success.svg')} alt="选中" style={{ width: '16px', height: '16px' }} /> ) : null, }, { key: 'serialNumber', label: languageConfig('', '序号'), width: '60px', align: 'center' as const, visible: true, render: (item: TableData, index: number) => index + 1, }, { key: 'personalName', label: languageConfig('', '认证人员'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => item.personalName, }, { key: 'certificationLevel', label: languageConfig('', '认证等级'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: () => '', // 动态计算 }, { key: 'baseNumber', label: languageConfig('', '等级基数'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => handleFormatAmount(item.baseNumber), }, { key: 'evaluationCoefficient', label: languageConfig('', '年度贡献评价'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => item.evaluationCoefficient, }, { key: 'rebateAmount', label: languageConfig('', '返点金额'), width: '120px', align: 'left' as const, visible: true, tooltip: true, render: (item: TableData) => handleFormatAmount(item.rebateAmount), }, ]; const PersonnelList: React.FC<{ id?: number; data?: TableData[] | null; type?: string; // styleClass?: any; }> = ({ id, data, type = '' }) => { const [tableData, setTableData] = useState<TableData[]>([]); // 表格数据 const [hasChecked, setHasChecked] = useState<boolean>(false); // 表格是否有勾选数据 // 认证类型、认证等级 const [certOptions, setCertOptions] = useState<{ type: StatusItems[]; level: StatusItems[]; }>({ type: [], level: [] }); /** 获取值集:'认证类型'、'认证等级' */ useEffect(() => { const fetchCertOptions = async () => { const res = await queryMapIdpValue([ 'PRM_AE_CERTIFICATION_LEVEL_2', 'PRM_AE_CERTIFICATION_LEVEL_3', ]); setCertOptions({ type: res?.[0] || [], level: res?.[1] || [] }); }; fetchCertOptions(); }, []); useEffect(() => { const fetchTableData = async () => { let resData: TableData[] = []; // 优先使用传入的 data,无则调用接口 if (data) { resData = data; } else if (id) { const res = await rebatePersonnelInformationListChildApi({ initialId: id, }); if (res.failed) { message.error(res.message, undefined, undefined, 'top'); return; } resData = res; } // 统一赋值 + 判断勾选状态 setTableData(resData); setHasChecked(resData.some(item => item.isIssueRebate === 'Y')); }; fetchTableData(); }, [id, data]); /** 【渲染】认证等级:认证类型-认证等级 */ const renderCertLevel = (item: TableData) => { const type = certOptions.type.find(v => v.value === item.certificationType)?.meaning || ''; const level = certOptions.level.find(v => v.value === item.certificationLevel) ?.meaning || ''; return `${type}-${level}`; }; /** 【渲染】单元格内容-认证等级 */ const renderCellContent = ( content: React.ReactNode, column: ColumnConfig, ) => { if (column.tooltip && content) { return ( <Tooltip theme="dark" title={content}> <span className={styles.tableCell_cellContent}>{content}</span> </Tooltip> ); } return <span className={styles.tableCell_cellContent}>{content}</span>; }; /** 【获取】根据type计算列宽配置 */ const getTableHeaders = useMemo(() => { if (type === 'look') { // 当 type='look' 时,序号固定50px,其他列均分剩余宽度 return tableHeaders.map((header, index) => { if (header.key === 'serialNumber') { return { ...header, width: '50px', }; } else if (index > 1) { // 从第三列开始(跳过isIssueRebate和serialNumber) // 其他列使用 flex: 1 自动均分 return { ...header, width: 'auto', // 宽度设为auto,使用flex: 1 }; } return header; }); } // 默认使用原始宽度配置 return tableHeaders; }, [type]); /** 【获取】可见的列配置(根据 hasChecked 过滤) */ const visibleColumns = useMemo(() => { const headers = getTableHeaders.map(col => { // 如果是认证等级列,更新render函数 if (col.key === 'certificationLevel') { return { ...col, render: (item: TableData) => renderCertLevel(item), }; } return col; }); return headers.filter(col => { // 如果 hasChecked 为 false,隐藏 isIssueRebate 列 if (col.key === 'isIssueRebate' && !hasChecked) { return false; } return col.visible !== false; }); }, [getTableHeaders, hasChecked, certOptions]); /** 【渲染】单元格样式 */ const getCellStyle = (column: ColumnConfig) => { const style: React.CSSProperties = { textAlign: column.align || 'left', }; if (type === 'look') { if (column.key === 'serialNumber') { // 序号列固定50px style.width = '50px'; style.flex = '0 0 50px'; } else if (column.key !== 'isIssueRebate') { // 其他列(除了isIssueRebate)均分宽度 style.flex = '1'; style.minWidth = '80px'; // 最小宽度防止过窄 } else { // isIssueRebate列保持原样 style.width = column.width; style.flex = `0 0 ${column.width}`; } } else { // 默认模式:使用配置的宽度 style.width = column.width; style.flex = `0 0 ${column.width}`; } return style; }; return ( <> <div className={styles.prmRcTableWrapper}> <div className={styles.prmRcTableWrapper__tableContainer}> {/* 表头 */} <div className={styles.prmRcTableWrapper__tableHeader}> <div className={styles.prmRcTableWrapper__headerRow}> {visibleColumns.map((column, index) => ( <div key={column.key} className={styles.prmRcTableWrapper__headerCell} // style={{ // width: column.width, // flex: `0 0 ${column.width}`, // textAlign: column.align || 'left', // }} style={getCellStyle(column, index)} > {column.label} </div> ))} </div> </div> {/* 表格内容 */} <div className={styles.prmRcTableWrapper__tableBody}> {tableData.map((item, index) => { // 设置的背景是整行的背景 const rowStyle = { // 设置的背景是整行的背景 background: item.isIssueRebate === 'Y' ? 'linear-gradient(90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70%)' : undefined, // 设置整行的文字颜色 color: item.isIssueRebate === 'Y' ? '#222222' : '#788295', }; return ( <div key={index} className={styles.prmRcTableWrapper__tableRow} style={rowStyle} > {visibleColumns.map((column, colIndex) => { const content = column.render ? column.render(item, index) : null; return ( <div key={column.key} className={styles.prmRcTableWrapper__tableCell} // style={{ // width: column.width, // flex: `0 0 ${column.width}`, // textAlign: column.align || 'left', // }} style={getCellStyle(column, colIndex)} > {renderCellContent(content, column)} </div> ); })} </div> ); })} </div> </div> </div> </> ); }; export default PersonnelList; // main.less // 表格容器 - BEM风格 .prmRcTableWrapper { width: 100%; background: #fff; display: flex; flex-direction: column; overflow: hidden; position: relative; z-index: 1; // 表格主容器 &__tableContainer { flex: 1; overflow: auto; position: relative; &::-webkit-scrollbar { width: 6px; height: 6px; } &::-webkit-scrollbar-track { background: #f5f5f5; border-radius: 3px; } &::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 3px; &:hover { background: #bfbfbf; } } // 表头 .prmRcTableWrapper__tableHeader { position: sticky; top: 0; z-index: 10; background: #fafafa; border-bottom: 1px solid #e5e6e8; min-width: fit-content; .prmRcTableWrapper__headerRow { display: flex; width: 100%; min-width: 100%; .prmRcTableWrapper__headerCell { flex-shrink: 0; line-height: 36px; height: 36px; background: #f5faff; font-size: 12px; color: #000000; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; padding: 0 12px; // border-right: 1px solid #E5E6E8; &:last-child { border-right: none; } } } } // 表格内容 .prmRcTableWrapper__tableBody { min-width: 100%; .prmRcTableWrapper__tableRow { display: flex; width: 100%; min-width: 100%; background: #fff; &:nth-child(even) { background: #fcfcfc; } &[style*='background:'] { background: linear-gradient( 90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70% ) !important; color: #222222 !important; &:nth-child(even) { background: linear-gradient( 90deg, rgba(212, 247, 231, 0.8), rgba(212, 247, 231, 0) 70% ) !important; } } // &:hover { // background: #f0f9ff !important; // &[style*='background:']:hover { // background: linear-gradient( // 90deg, // rgba(200, 237, 221, 0.9), // rgba(200, 237, 221, 0) 70% // ) !important; // } // } &:last-child { border-bottom: none; } .prmRcTableWrapper__tableCell { flex-shrink: 0; line-height: 36px; height: 36px; color: inherit; overflow: hidden; background: none !important; padding: 0 12px; // border-right: 1px solid #E5E6E8; border-bottom: 1px solid #e5e6e8; &:last-child { border-right: none; } // 单元格内容 .prmRcTableWrapper__cellContent { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 36px; color: inherit; } } } } } }