react + zarm 实现账单列表展示页

Posted 凯小默:树上的男爵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react + zarm 实现账单列表展示页相关的知识,希望对你有一定的参考价值。

需要实现的效果

实现过程

1.安装 dayjs

npm i dayjs -S

https://dayjs.fenxianglu.cn/category/display.html#%E6%A0%BC%E5%BC%8F%E5%8C%96

2.配置接口

找到账单列表接口

我们在 src\\container\\Home\\api\\index.js 里添加下面代码

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

// 获取账单列表
export function queryBillList(data) 
  return fetchData('/api/bill/list', 'get', data);

接口返回的数据结构如下:

http://localhost:3000/api/bill/list?r=663.1504597032896&curPage=3&pageSize=5&typeId=all&billDate=2022-02

3.准备账单类型图标

我们去 iconfont 里找一些对应的图标,然后添加到我们项目的字体图标,然后更新链接

4.配置下拉刷新以及类型的字典

我们在 src\\utils\\index.js 里添加下面配置代码

// 刷新状态
export const REFRESH_STATE = 
  normal: 0, // 普通
  pull: 1, // 下拉刷新(未满足刷新条件)
  drop: 2, // 释放立即刷新(满足刷新条件)
  loading: 3, // 加载中
  success: 4, // 加载成功
  failure: 5, // 加载失败
;
// 加载状态
export const LOAD_STATE = 
  normal: 0, // 普通
  abort: 1, // 中止
  loading: 2, // 加载中
  success: 3, // 加载成功
  failure: 4, // 加载失败
  complete: 5, // 加载完成(无新数据)
;
// 类型
export const typeMap = 
  1: 
    icon: 'canyin'
  ,
  2: 
    icon: 'fushi'
  ,
  3: 
    icon: 'jiaotong'
  ,
  4: 
    icon: 'riyong'
  ,
  5: 
    icon: 'gouwu'
  ,
  6: 
    icon: 'xuexi'
  ,
  7: 
    icon: 'yiliao'
  ,
  8: 
    icon: 'lvxing'
  ,
  9: 
    icon: 'renqing'
  ,
  10: 
    icon: 'qita'
  ,
  11: 
    icon: 'gongzi'
  ,
  12: 
    icon: 'jiangjin'
  ,
  13: 
    icon: 'zhuanzhang'
  ,
  14: 
    icon: 'licai'
  ,
  15: 
    icon: 'tuikuang'
  ,
  16: 
    icon: 'qita'
  

5.编写账单页

这里我们用到了 Pull 组件,用法可以查看 https://zarm.design/#/components/pull

我们在 src\\container\\Home\\index.jsx 添加下面代码:(这里我写死了 billDate: “2022-02”,为了测试用的,这个条件有数据,后面会删掉)

import React,  useState, useEffect  from 'react'
import  Icon, Pull  from 'zarm'
import dayjs from 'dayjs'
import BillItem from '@/components/BillItem'
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 [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])

  // 获取账单方法
  const getBillList = async () => 
    const  data  = await queryBillList(
      curPage: page,
      pageSize: 5,
      typeId: "all",
      billDate: "2022-02" || 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);
    
  

  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>
          <span className=s.title>类型 <Icon className=s.arrow type="arrow-bottom" /></span>
        </div>
        <div className=s.right>
          <span className=s.time>2022-05<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, index) => <BillItem
              bill=item
              key=index
            />)
          
        </Pull> : null
      
    </div>
  </div>


export default Home

src\\container\\Home\\style.module.less 添加样式

.home 
    height: 100%;
    display: flex;
    flex-direction: column;
    padding-top: 80px;
    .header 
      position: fixed;
      top: 0;
      left: 0;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      width: 100%;
      height: 80px;
      background-color: #007fff;
      color: #fff;
      font-size: 14px;
      z-index: 100;
      padding: 10px;
      .data-wrap 
        font-size: 14px;
        > span 
          font-size: 12px;
          > b 
            font-size: 26px;
            font-family: DINCondensed-Bold, DINCondensed;
            margin-left: 4px;
          
        
        .income 
          margin-left: 10px;
        
      
      .type-wrap 
        display: flex;
        justify-content: flex-end;
        align-items: flex-end;
        > div 
          align-self: flex-start;
          background: rgba(0, 0, 0, 0.1);
          border-radius: 30px;
          padding: 3px 8px;
          font-size: 12px;
        
        .left 
          margin-right: 6px;
        
        .arrow 
            font-size: 12px;
            margin-left: 4px;
        
      
    
    .content-wrap 
      height: calc(~"(100% - 50px)");
      overflow: hidden;
      overflow-y: scroll;
      background-color: #f5f5f5;
      padding: 10px;
      :global 
        .za-pull 
          overflow: unset;
        
      
    
  

6.编写账单BillItem子组件

下面需要实现

src\\components\\BillItem\\index.jsx 添加代码

import React,  useEffect, useState  from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import  Cell  from 'zarm';
import  useNavigate  from 'react-router-dom'
import CustomIcon from '../CustomIcon';
import  typeMap  from '@/utils';

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

const BillItem = ( bill ) => 
  const [income, setIncome] = useState(0); // 收入
  const [expense, setExpense] = useState(0); // 支出
  const navigate = useNavigate(); // 路由实例

  // 当添加账单是,bill.bills 长度变化,触发当日收支总和计算。
  useEffect(() => 
    // 初始化将传入的 bill 内的 bills 数组内数据项,过滤出支出和收入。
    // pay_type:1 为支出;2 为收入
    // 通过 reduce 累加
    const _income = bill.bills.filter(i => i.pay_type == 2).reduce((curr, item) => 
      curr += Number(item.amount);
      return curr;
    , 0);
    setIncome(_income);
    const _expense = bill.bills.filter(i => i.pay_type == 1).reduce((curr, item) => 
      curr += Number(item.amount);
      return curr;
    , 0);
    setExpense(_expense);
  , [bill.bills]);

  // 前往账单详情
  const goToDetail = (item) => 
    navigate(`/detail?id=$item.id`)
  ;

  return <div className=s.item>
    <div className=s.headerDate>
      <div className=s.date>bill.day</div>
      <div className=s.money>
        <span>
          <img src=`src/assets/images/zhi.png` alt='支' />
            <span>¥ expense.toFixed(2) </span>
        </span>
        <span>
          <img src=`src/assets/images/shou.png` alt="收" />
          <span>¥ income.toFixed(2) </span>
        </span>
      </div>
    </div>
    
      bill && bill.bills.map(item => <Cell
        className=s.bill
        key=item.id
        onClick=() => goToDetail(item)
        title=
          <>
            <CustomIcon
              className=s.itemIcon
              type=item.type_id ? typeMap[item.type_id].icon : 1
            />
            <span> item.type_name </span>
          </>
        
        description=<span style= color: item.pay_type == 2 ? 'red' : '#39be77' >`$item.pay_type == 1 ? '-' : '+'$item.amount`</span>
        help=<div>dayjs(item.date).format('HH:mm') item.remark ? `| $item.remark` : ''</div>
      >
      </Cell>)
    
  </div>
;

BillItem.propTypes = 
  bill: PropTypes.object
;

export default BillItem;

src\\components\\BillItem\\style.module.less 里添加子组件的样式

.item 
    border-radius: 10px;
    react + zarm 实现账单列表类型以及时间条件弹窗封装

react + zarm + antV F2 实现账单数据统计饼图效果

eggjs 怎么实现获取账单列表接口并且实现列表数据分页查询功能?

Android App开发实战之实现微信记账本(附源码 超详细必看)

eggjs 怎么实现账单详情页的编辑接口?

超市帐单系统