0
点赞
收藏
分享

微信扫一扫

react + zarm 实现账单列表类型以及时间条件弹窗封装

凯约 2022-08-18 阅读 74


需要实现的效果

点击类型,出现下面的条件弹窗

react + zarm 实现账单列表类型以及时间条件弹窗封装_f5

点击时间,出现下面的弹窗

react + zarm 实现账单列表类型以及时间条件弹窗封装_数据_02

实现过程

这里用到 popup 组件 ​​https://zarm.design/#/components/popup​​

1.封装类型条件组件

新建 ​​components/PopupType​​​,在其内部新建 ​​index.jsx​​​ 和 ​​style.module.less​​ 内容如下:

import React, { forwardRef, useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import { Popup, Icon } from 'zarm'
import cx from 'classnames'
import { queryTypeList } from './api/index.js'

import s from './style.module.less'

// forwardRef 用于拿到父组件传入的 ref 属性,这样在父组件便能通过 ref 控制子组件。
const PopupType = forwardRef(({ onSelect },) => {
const [show, setShow] = useState(false); // 组件的显示和隐藏
const [active, setActive] = useState('all'); // 激活的 type
const [expense, setExpense] = useState([]); // 支出类型标签
const [income, setIncome] = useState([]); // 收入类型标签

useEffect(async () => {
// 请求标签接口放在弹窗内,这个弹窗可能会被复用,所以请求如果放在外面,会造成代码冗余。
const { data } = await queryTypeList({});
console.log(data);
setExpense(data.filter(i => i.type == 1))
setIncome(data.filter(i => i.type == 2))
}, [])

if (ref) {
ref.current = {
// 外部可以通过 ref.current.show 来控制组件的显示
show: () => {
setShow(true)
},
// 外部可以通过 ref.current.close 来控制组件的显示
close: () => {
setShow(false)
}
}
};

// 选择类型回调
const choseType = (item) => {
setActive(item.id)
setShow(false)
// 父组件传入的 onSelect,为了获取类型
onSelect(item)
};

return <Popup
visible={show}
direction="bottom"
onMaskClick={() => setShow(false)}
destroy={false}
mountContainer={() => document.body}
>
<div className={s.popupType}>
<div className={s.header}>
请选择类型
<Icon type="wrong" className={s.cross} onClick={() => setShow(false)} />
</div>
<div className={s.content}>
<div onClick={() => choseType({ id: 'all' })} className={cx({ [s.all]: true, [s.active]: active == 'all' })}>全部类型</div>
<div className={s.title}>支出</div>
<div className={s.expenseWrap}>
{
expense.map((item,) => <p key={index} onClick={() => choseType(item)} className={cx({[s.active]: active == item.id})} >{ item.name }</p>)
}
</div>
<div className={s.title}>收入</div>
<div className={s.incomeWrap}>
{
income.map((item,) => <p key={index} onClick={() => choseType(item)} className={cx({[s.active]: active == item.id})} >{ item.name }</p>)
}
</div>
</div>
</div>
</Popup>
});

PopupType.propTypes = {
onSelect: PropTypes.func
}

export default PopupType;

.popup-type {
height: 500px;
background-color: #f5f5f5;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
.header {
position: sticky;
top: 0;
left: 0;
z-index: 1000;
width: 100%;
height: 56px;
text-align: center;
font-size: 14px;
line-height: 56px;
color: rgba(0, 0, 0, 0.9);
background-color: #fff;
.cross {
position: absolute;
right: 10px;
top: 50%;
font-size: 20px;
transform: translateY(-50%);
color: rgba(0, 0, 0, 0.6);
}
}
.content {
padding: 20px;
.all {
display: inline-block;
padding: 12px 20px;
font-size: 16px;
color: rgba(0, 0, 0, 0.9);
background-color: #fff;
}
.title {
color: rgba(0, 0, 0, 0.9);
margin: 10px 0;
font-size: 14px;
}
.expense-wrap, .income-wrap {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
p {
width: calc(~"(100% - 20px) / 3");
text-align: center;
padding: 12px 0;
margin-bottom: 10px;
background-color: #fff;
font-size: 16px;
}
}
.active {
background-color: #007fff!important;
color: #fff;
}
}
}

然后新建 ​​components/PopupType/api​​​,在其内部新建 ​​index.js​​ 添加如下:

import { fetchData } from "@/utils/axios.js";

// 获取类型字典列表
export function queryTypeList(data) {
return fetchData('/api/type/list', 'get', data);
}

2.封装时间条件组件

新建 ​​components/PopupDate​​​,在其内部新建 ​​index.jsx​​ 代码如下:

import React, { forwardRef, useState } from 'react'
import PropTypes from 'prop-types'
import { Popup, DatePicker } from 'zarm'
import dayjs from 'dayjs'

const PopupDate = forwardRef(({ onSelect, mode = 'date' }, ref) => {
const [show, setShow] = useState(false)
const [now, setNow] = useState(new Date())

const choseMonth = (item) => {
setNow(item)
setShow(false)
if (mode == 'month') {
onSelect(dayjs(item).format('YYYY-MM'))
} else if (mode == 'date') {
onSelect(dayjs(item).format('YYYY-MM-DD'))
}
}

if (ref) {
ref.current = {
show: () => {
setShow(true)
},
close: () => {
setShow(false)
}
}
};
return <Popup
visible={show}
direction="bottom"
onMaskClick={() => setShow(false)}
destroy={false}
mountContainer={() => document.body}
>
<div>
<DatePicker
visible={show}
value={now}
mode={mode}
onOk={choseMonth}
onCancel={() => setShow(false)}
/>
</div>
</Popup>
});

PopupDate.propTypes = {
mode: PropTypes.string, // 日期模式
onSelect: PropTypes.func, // 选择后的回调
}

export default PopupDate;

3.账单列表组件改动

import React, { useState, useEffect, useRef } from 'react'
import { Icon, Pull } from 'zarm'
import dayjs from 'dayjs'
import BillItem from '@/components/BillItem'
import PopupType from '@/components/PopupType'
import PopupDate from '@/components/PopupDate'
import { queryBillList } from './api/index.js'
import { REFRESH_STATE, LOAD_STATE } from '@/utils/index.js' // Pull 组件需要的一些常量

import s from './style.module.less'

const Home = () => {
const typeRef = useRef(); // 账单类型 ref
const monthRef = useRef(); // 月份筛选 ref
const [currentSelect, setCurrentSelect] = useState({}); // 当前筛选类型
const [currentTime, setCurrentTime] = useState(dayjs().format('YYYY-MM')); // 当前筛选时间
const [totalExpense, setTotalExpense] = useState(0); // 总支出
const [totalIncome, setTotalIncome] = useState(0); // 总收入
const [page, setPage] = useState(1); // 分页
const [dataList, setDataList] = useState([]); // 账单列表
const [totalPage, setTotalPage] = useState(0); // 分页总数
const [refreshing, setRefreshing] = useState(REFRESH_STATE.normal); // 下拉刷新状态
const [loading, setLoading] = useState(LOAD_STATE.normal); // 上拉加载状态

useEffect(() => {
getBillList() // 初始化
}, [page, currentSelect, currentTime])

// 获取账单方法
const getBillList = async () => {
const { data } = await queryBillList({
curPage: page,
pageSize: 5,
typeId: currentSelect.id || "all",
billDate: currentTime
});
// 下拉刷新,重制数据
if (page == 1) {
setDataList(data.dataList);
} else {
setDataList(dataList.concat(data.dataList));
}
setTotalExpense(data.totalExpense);
setTotalIncome(data.totalIncome);
setTotalPage(data.pageObj.totalPage);
// 上滑加载状态
setLoading(LOAD_STATE.success);
setRefreshing(REFRESH_STATE.success);
}

// 请求列表数据
const refreshData = () => {
setRefreshing(REFRESH_STATE.loading);
if (page != 1) {
setPage(1);
} else {
getBillList();
};
};

const loadData = () => {
if (page < totalPage) {
setLoading(LOAD_STATE.loading);
setPage(page + 1);
}
}

// 添加账单弹窗
const toggle = () => {
typeRef.current && typeRef.current.show()
};
// 选择月份弹窗
const monthToggle = () => {
monthRef.current && monthRef.current.show()
};

// 筛选类型
const select = (item) => {
setRefreshing(REFRESH_STATE.loading);
setPage(1);
setCurrentSelect(item)
}
// 筛选月份
const selectMonth = (item) => {
setRefreshing(REFRESH_STATE.loading);
setPage(1);
setCurrentTime(item)
}

return <div className={s.home}>
<div className={s.header}>
<div className={s.dataWrap}>
<span className={s.expense}>总支出:<b>¥ { totalExpense }</b></span>
<span className={s.income}>总收入:<b>¥ { totalIncome }</b></span>
</div>
<div className={s.typeWrap}>
<div className={s.left} onClick={toggle}>
<span className={s.title}>{ currentSelect.name || '全部类型' } <Icon className={s.arrow} type="arrow-bottom" /></span>
</div>
<div className={s.right} onClick={monthToggle}>
<span className={s.time}>{ currentTime } <Icon className={s.arrow} type="arrow-bottom" /></span>
</div>
</div>
</div>
<div className={s.contentWrap}>
{
dataList.length ? <Pull
animationDuration={200}
stayTime={400}
refresh={{
state: refreshing,
handler: refreshData
}}
load={{
state: loading,
distance: 200,
handler: loadData
}}
>
{
dataList.map((item,) => <BillItem
bill={item}
key={index}
/>)
}
</Pull> : <div className={s.noData}>暂无账单数据</div>
}
<PopupType ref={typeRef} onSelect={select} />
<PopupDate ref={monthRef} mode="month" onSelect={selectMonth} />
</div>
</div>
}

export default

样式添加了没有数据的情况

.no-data {
text-align: center;
font-size: 12px;
padding: 10px 0;
}

4.测试

当前这个月没有数据,暂时如下:

react + zarm 实现账单列表类型以及时间条件弹窗封装_zarm_03

点击时间,选择到2022年的2月份

react + zarm 实现账单列表类型以及时间条件弹窗封装_数据_04

发现就有数据了:

react + zarm 实现账单列表类型以及时间条件弹窗封装_react.js_05

切换类型到学习:

react + zarm 实现账单列表类型以及时间条件弹窗封装_zarm_06

选中之后:

react + zarm 实现账单列表类型以及时间条件弹窗封装_ico_07

参考资料

  • ​​https://zarm.design/#/components/popup​​
  • ​​https://zarm.design/#/components/date-picker​​


举报

相关推荐

0 条评论