跳到主要内容

2 篇博文 含有标签「react」

查看所有标签

React实战案例:操作数据将其应用到antd的Cascader组件中并通过Cascader组件选项切换日历数据

· 阅读需 3 分钟
Web developer & UI designer

一、需求

将获得的数据转换为antd中Cascader组件的option选项数据,然后再通过Cascader组件某一选项过滤日历的传入数据,在日历上显示。

二、思路

我们先来看看获得的原始数据,calendarEvents是包含着对象的数组类型:

[{
"id": "11ec-b68b-1df198ede54e",
"start": "2022-03-09T05:00:00.000Z",
"end": "2022-03-10T04:59:59.999Z",
"visible_title": null,
"status": "not_started",
"notes": null,
"site_links": [],
"cycle": {
"id": "afb444a0-b68b-1df198ede54e",
"name": "1941",
"__typename": "Cycle"
},
"task_type": {
"id": "ad3f075b-960f789-f74a7e816e7e",
"name": "Scouting B6",
"__typename": "TaskType"
},
"schedule_events": [],
"__typename": "CycleEvent",
"title": "Scouting B6 - 1941",
"allDay": true
},
...
]

而antd的Cascader组件需要的选项Option数据格式是一个有特定键值的数组:

const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];

根据需求我们分为两步:

第一步: 我们需要把calendarEvents中键值为"__typename"的值取出,包装成Option数据格式,附上新的键值,传给Cascader组件。

第二步: 获取Cascader组件选择的值,使用它对calendarEvents原始数据进行过滤,获取新的数组,传递给日历组件。当然,此时我们需要声明一个新的组件状态。

三、涉及到的技术

  • lodash中的_.filter_.uniqBy_.orderBy_.map方法
  • react-big-calendar库的使用

四、解决方案

1. mapTaskTypesToCascadeOptions函数将calendarEvents转换为Cascader需要的格式

function mapTaskTypesToCascadeOptions() {
// 过滤原始数据,将calendarEvents中对象task_type属性值不为空的对象取出,形成新数组
const calendarEventsWithTaskType = _.filter(calendarEvents, obj => obj.task_type !== null);
// 再将id重复的去掉
let allTaskTypes = _.uniqBy(_.map(calendarEventsWithTaskType, obj => obj.task_type), 'id');
// 接下来对数组进行排序,按照name升序排序
allTaskTypes = _.orderBy(allTaskTypes, 'name', 'asc');
// 最后赋予新的键值将该数组映射为符合Cascade选项的数组
return _.map(allTaskTypes, task => {
return {
id: _.get(task, 'id', ''),
value: _.get(task, 'name', ''),
label: _.get(task, 'name', ''),
}
})
};

const actionItemCascadeOptions = mapTaskTypesToCascadeOptions();

2. 给Cascader组件传入准备好的数据

<Cascader
{...classes('calendar-filter')}
// 这里option就可以使用已经整理好的数据了
options={actionItemCascadeOptions}
expandTrigger='hover'
onChange={onActionItemChange}
changeOnSelect={true}
placeholder='Select Task Type'
showSearch={{ filter }}
/>

3. 在Cascader的回调函数中,对calendarEvents进行过滤

function onActionItemChange(value) {
// 如果value有值
if (value.length === 1) {
// 对calendarEvents进行过滤,返回其中的对象的task_type.name和值和选择的value值相等的数组
const filteredEvents = _.filter(calendarEvents, (event) => {
return _.get(event, 'task_type.name', '') === value[0];
});
// 更改日历的数据为filteredEvents
setCalendarData(filteredEvents);
} else {
// 否则日历使用原来的calendarEvents
setCalendarData(calendarEvents);
}
};

4. 声明一个新的状态

// 用来保存日历数据的状态
let [calendarData, setCalendarData] = useState(calendarEvents);

5. 给日历组件传递数据

<Calendar
ref={printableRef}
// 将calendarData传入组件中
events={calendarData}
step={60}
showMultiDayTimes
localizer={localizer}
/>

React实战案例:给表格添加总结栏,并自动求和

· 阅读需 4 分钟
Web developer & UI designer

一、需求

要实现antd表格的自动汇总功能,如果表格中单元格的值为数字的话,将其相加,生成总结栏,展示在表格底部。类似效果如下图所示:

表格总结栏效果图

先来观察我们拿到的表格数据,它的格式是一个包含着对象的数组,其中值为数字的属性是我们要获取的数据,如下所示:

const filledLogs = [
{
"id": "2d1e7f10-57ac-477a-9b1a-6f537e915a30",
"timestamp": "1/27/2022 @ 3:16 AM",
"Tare Weight_unit": "Grams",
"name": "Batch 1844",
"cycle": {
"start": "2021-11-03T15:16:39.024+00:00",
"end": "2022-02-15T16:16:39.024+00:00",
},
"Net Weight_unit": "Grams",
"Net Weight": 77230,
"Tare Weight": 10000,
"Gross Weight": 87230,
"is_queued": false,
"manager": {
"id": "75597beb-769e-4b04-81ae-ef7f5b4e1aa0",
"name": "Jack White",
},
"plant_links": [],
},
// ... 更多数据
]

这里的难点在于我们要实现的表格的数据结构虽然一样,但具体的数据不是固定的,因此不能通过静态的键值来获取数据。

二、思路

由于键值不固定,因此我们需要通过判断对象的值是否为数字来获得对应的key和它的value。

我们的目标是最好能够得到如下的一个对象类型的数据,其中的key为数组中原本的key,它的值为该数组中同一个key的值的总和。

{
"Net Weight": 158101,
"Tare Weight": 54567,
"Gross Weight": 212668
}

三、涉及到的技术

  • lodash 中的 _.forEach()_.isNumber()_.get()_.has() 方法
  • antd 表格组件总结栏 <Table.Summary.Row><Table.Summary.Cell> 和 Typography 组件中的 <Text>

四、解决方案

以下是完整的实现代码:

// filledLog的格式必须是一个包含对象的数组
summary={(filledLogs) => {
// 首先定义一个空对象
let numberSums: { [key: string]: number; } = {};

// 遍历数组filledLogs,获取到其中的每一个对象
_.forEach(filledLogs, obj => {
// 继续遍历每一个对象,获取其中的key和value
_.forEach(obj, (value, key) => {
// 判断该value是否为数字,如果为数字
if (_.isNumber(value)) {
// 通过该数字对应的key在numberSums中取值,默认值为0,赋值给sum变量
const sum = _.get(numberSums, `${key}`, 0)
// 然后将sum和该key对应的值相加,再以该key为键值,保存到numberSums中
// 例如,遍历时,第一次,sum值为0,obj[key]为数组中该key对应的数字,相加后保存到numberSums对象中
// 第二次,sum被取出,再和key值一致的值相加后保存,覆盖掉原值
numberSums[key] = sum + obj[key];
}
})
});

return (
<Table.Summary.Row>
<Table.Summary.Cell index={0}>Total</Table.Summary.Cell>
<Table.Summary.Cell index={1}></Table.Summary.Cell>
{
// filledLogHeaders是表头的数据,格式也是包含对象的数组
// 遍历filledLogHeaders数组,拿到每一个对象
filledLogHeaders.map((item, index) => {
return (
<Table.Summary.Cell index={index}>
<Text>
{
// 检查numberSums是否有以item.title为key的值
_.has(numberSums, item.title)
// 如果有则显示该值,否则显示'-'
? numberSums[item.title] : '-'
}
</Text>
</Table.Summary.Cell>
)
})
}
</Table.Summary.Row>
)
}}

五、总结

这个解决方案的核心思路是:

  1. 动态识别数字字段:通过 _.isNumber() 判断对象中的值是否为数字类型
  2. 累加计算:使用 _.forEach() 遍历数据,将相同键名的数字值进行累加
  3. 渲染总结行:使用 antd 的 Table.Summary 组件在表格底部显示汇总结果

这种方法的优势在于:

  • 通用性强:不依赖固定的字段名,适用于任何包含数字字段的数据结构
  • 可维护性好:代码逻辑清晰,易于理解和修改
  • 性能良好:使用 lodash 的高效方法进行数据处理

通过这种方式,我们可以轻松地为任何 antd 表格添加自动汇总功能,提升用户体验。