需求:
从后端返回的数据为层层嵌套的树状结构,前端如何层层遍历并展示?
json 文件
items: [
{
fieldName: 'allFlowMoney',
fieldChineseName: '本期账单消费',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'fltConMoneyHasPost',
fieldChineseName: '月结机票',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'fltTicketFee',
fieldChineseName: '机票金额',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'fltRefund',
fieldChineseName: '退票费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'fltInsurancefee',
fieldChineseName: '保险',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'fltServicePackagePrice',
fieldChineseName: '服务包',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'fltCoupon',
fieldChineseName: '优惠券',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'fltServiceFeeSummary',
fieldChineseName: '服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'fltPostServiceFeeX',
fieldChineseName: '现付机票服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlXconMoney',
fieldChineseName: '月结协议酒店',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'htlXRoomCharge',
fieldChineseName: '酒店房费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlXServiceFeeSummary',
fieldChineseName: '酒店服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlXServiceCharge',
fieldChineseName: '手续费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'htlHconMoney',
fieldChineseName: '月结会员酒店',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'htlHRoomCharge',
fieldChineseName: '酒店房费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlHServiceFeeSummary',
fieldChineseName: '酒店服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlHServiceCharge',
fieldChineseName: '手续费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'htlXPaySummaryX',
fieldChineseName: '月结协议酒店',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'htlXAfterServiceFeeX',
fieldChineseName: '酒店服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlXServiceChargeX',
fieldChineseName: '手续费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'htlHPaySummaryX',
fieldChineseName: '现付会员酒店',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'htlHAfterServiceFeeX',
fieldChineseName: '酒店服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'htlHServiceChargeX',
fieldChineseName: '手续费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'trainRealAmountHasPost',
fieldChineseName: '月结火车票',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'trainRealAmount',
fieldChineseName: '火车票金额',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'fltRefund',
fieldChineseName: '退票费服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'trainPostServiceFeeX',
fieldChineseName: '现付火车票服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'vasRealAmountHasPost',
fieldChineseName: '月结增值',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'vasRealAmount',
fieldChineseName: '增值金额',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'vasPostServiceFee',
fieldChineseName: '增值服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'vasCommissionFee',
fieldChineseName: '增值手续费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'carPostServiceFeeX',
fieldChineseName: '现付用车服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'busRealFeeHasPost',
fieldChineseName: '月结汽车票',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'busRealFee',
fieldChineseName: '汽车票金额',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'busServiceFeeSummary',
fieldChineseName: '汽车票服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'busLaterServiceFeeX',
fieldChineseName: '现付汽车票服务费',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'otherRebateSummary',
fieldChineseName: '返利',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'otherFltRebate',
fieldChineseName: 'FLIGHTREBATE',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'otherHtlRebate',
fieldChineseName: '酒店返利',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'otherRebateAdjust',
fieldChineseName: '调整返利',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
{
fieldName: 'otherAdjustSummary',
fieldChineseName: '其他调整',
totalFee: 0,
existRebateReport: false,
remark: '10000000000000',
subItems: [],
},
{
fieldName: 'otherLateFeeSummary',
fieldChineseName: '滞纳金',
totalFee: 0,
existRebateReport: false,
subItems: [
{
fieldName: 'otherLateFee',
fieldChineseName: '滞纳金应收',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
{
fieldName: 'otherOxlf',
fieldChineseName: '滞纳金调整',
totalFee: 0,
existRebateReport: false,
subItems: [],
},
],
},
],
前端展示
解决方案:
ant design collapse组件 + 递归
import React, { Fragment } from 'react';
import { Collapse } from 'antd';
import toThousands from '../../../utils/function';
import './index.less';
import { CaretRightOutlined } from '@ant-design/icons';
const { Panel } = Collapse;
function callback(key: any) {
// console.log(key)
}
function subItem(dataSource: any[]) {
return Array.isArray(dataSource) ? (
<Collapse
bordered={false}
onChange={callback}
className="collapse"
expandIcon={({ isActive }) => (
<CaretRightOutlined rotate={isActive ? 90 : 0} />
)}
>
{dataSource.map((v, index) =>
v?.subItems?.length > 0 ? (
<Panel
header={
<Fragment>
<span className="chineseName">{v.fieldChineseName}</span>
<span className="fee">{toThousands(v.totalFee)}</span>
{v.existRebateReport && (
<span className="downloadReport">下载报表</span>
)}
{v.hasOwnProperty('remark') ? <div>{v.remark}</div> : ''}
</Fragment>
}
key={`${v.fieldChineseName}-${index}`}
>
{subItem(v.subItems)}
</Panel>
) : (
<div className="detailBox clearfix">
<span className="chineseNameLast">{v.fieldChineseName}</span>
<span className="feeLast">{toThousands(v.totalFee)}</span>
{v.existRebateReport && (
<span className="downloadReport">下载报表</span>
)}
{v.hasOwnProperty('remark') ? (
<div className="remark">{v.remark}</div>
) : (
''
)}
</div>
),
)}
</Collapse>
) : null;
}
export default function ConsumeDetail(props: any) {
let { dataSource = [] } = props;
// console.log('dataSource', dataSource);
return (
<Fragment>
<div className="showCash">{subItem(dataSource)}</div>
</Fragment>
);
}
index.less
.ant-collapse {
width : 100%;
.ant-collapse-content>.ant-collapse-content-box {
padding : 0;
padding-left: 14px;
}
}
.ant-collapse-borderless > .ant-collapse-item {
border: none;
}
.ant-collapse>.ant-collapse-item>.ant-collapse-header {
border-bottom: 1px solid #d9d9d9;
}
// .ant-collapse-borderless > .ant-collapse-item:last-child,
// .ant-collapse-borderless > .ant-collapse-item:last-child .ant-collapse-header {
// //border: none;
// //padding-bottom: 0;
// }
.clearfix {
display: block;
content: "";
clear : both;
}
.showCash {
margin: 12px 0 0 12px;
.collapse {
border: none;
.chineseName {
color : #1b234d;
font-family : 'PingFang SC';
font-size : 14px;
font-weight : 500;
height : 22px;
letter-spacing: px;
}
.fee {
color : #1b234d;
font-family : 'PingFang SC';
font-size : 16px;
font-weight : 500;
height : 24px;
letter-spacing : px;
float : right;
//padding-right: 14px;
}
.detailBox {
padding : 10px 0;
border-bottom: 1px solid #e9e9e9;
.chineseNameLast {
color : #1b234d;
font-family: 'PingFang SC';
font-size : 14px;
font-weight: 500;
height : 22px;
margin-left: 40px;
}
.feeLast {
color : #1b234d;
font-family : 'PingFang SC';
font-size : 16px;
font-weight : 500;
height : 24px;
float : right;
margin-right: 16px;
}
}
.subChineseName {
color : #4d5580;
font-family: 'PingFang SC';
font-size : 13px;
font-weight: normal;
height : 22px;
}
.subFee {
color : #4c5a75;
font-family : 'PingFang SC';
font-size : 14px;
font-weight : normal;
height : 22px;
float : right;
//padding-right: 14px;
}
.remark {
color : #8790a3;
font-family: 'PingFang SC';
font-size : 12px;
font-weight: 500;
height : 20px;
margin : 10px 0 0 30px;
}
.downloadReport {
color : #1658dc;
font-family: 'PingFang SC';
font-size : 12px;
font-weight: 500;
height : 20px;
width : 48px;
margin-left: 24px;
}
}
}