react + zarm 实现账单详情页以及编辑删除功能

Posted 凯小默:树上的男爵

tags:

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

需要实现的交互效果

编辑的功能点击还是弹出新增账单窗口那个模块,不过需要稍微改动一下。

实现过程

1.封装公用的头部 Header

在 components 目录下新建 Header 目录,添加两个文件 index.jsxstyle.module.less

import React from 'react';
import PropTypes from 'prop-types';
import  useNavigate  from 'react-router-dom'
import  NavBar, Icon  from 'zarm';

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

const Header = ( title = '' ) => 
  const navigate = useNavigate()
  return <div className=s.headerWarp>
    <div className=s.block>
      <NavBar
        className=s.header
        left=<Icon type="arrow-left" theme="primary" onClick=() => navigate(-1) />
        title=title
      />
    </div>
  </div>
;

Header.propTypes = 
  title: PropTypes.string, // 标题
;

export default Header;
.header-warp 
  border-bottom: 1px solid #e9e9e9;
  .block 
    width: 100%;
    height: 46px;
    :global 
      .za-nav-bar__title 
        font-size: 14px;
        color: rgba(0, 0, 0, 0.9);
      
      .za-icon--arrow-left 
        font-size: 20px;
      
    
  
  .header 
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    .more 
      font-size: 20px;
    
  

2.安装 query-string

npm i query-string -s

用法请参考:https://github.com/sindresorhus/query-string

const queryString = require('query-string');

console.log(location.search);
//=> '?foo=bar'

const parsed = queryString.parse(location.search);
console.log(parsed);
//=> foo: 'bar'

console.log(location.hash);
//=> '#token=bada55cafe'

const parsedHash = queryString.parse(location.hash);
console.log(parsedHash);
//=> token: 'bada55cafe'

parsed.foo = 'unicorn';
parsed.ilike = 'pizza';

const stringified = queryString.stringify(parsed);
//=> 'foo=unicorn&ilike=pizza'

location.search = stringified;
// note that `location.search` automatically prepends a question mark
console.log(location.search);
//=> '?foo=unicorn&ilike=pizza'

3.添加 Detail 路由

src\\router\\index.js 里添加路由:

import Login from '@/container/Login'
import Home from '@/container/Home'
import Data from '@/container/Data'
import User from '@/container/User'
import Detail from '@/container/Detail'

const routes = [
  
    path: "/login",
    component: Login
  ,
    path: "/",
    component: Home
  ,
    path: "/data",
    component: Data
  ,
    path: "/user",
    component: User
  ,
    path: "/detail",
    component: Detail
  
];

export default routes

4.添加 Detail 模块代码

在 container 目录下新建 Detail 目录,添加文件 index.jsxstyle.module.less,以及 api/index.js 接口配置文件。

import React,  useEffect, useState, useRef  from 'react';
import  useLocation, useNavigate  from 'react-router-dom';
import  Modal, Toast  from 'zarm';
import qs from 'query-string';
import cx from 'classnames';
import Header from '@/components/Header';
import CustomIcon from '@/components/CustomIcon';
import PopupAddBill from '@/components/PopupAddBill';
import  typeMap  from '@/utils';
import  billDetails, billDelete  from './api/index.js';

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

const Detail = () => 
  const editRef = useRef();
  const navigate = useNavigate();
  const location = useLocation(); // 路由 location 实例
  const  id  = qs.parse(location.search); // 查询字符串反序列化

  const [detail, setDetail] = useState(); // 订单详情数据

  useEffect(() => 
    getDetail()
  , []);

  const getDetail = async () => 
    const  data  = await billDetails( id );
    setDetail(data);
  

  // 删除方法
const deleteDetail = () => 
  Modal.confirm(
    title: '删除',
    content: '确认删除账单?',
    onOk: async () => 
      const  data  = await billDelete( id )
      Toast.show('删除成功')
      navigate(-1)
    ,
  );

  return <div className=s.detail>
    <Header title='账单详情' />
    <div className=s.card>
      <div className=s.type>
        /* 通过 pay_type 属性,判断是收入或指出,给出不同的颜色*/
        <span className=cx( [s.expense]: detail.pay_type == 1, [s.income]: detail.pay_type == 2 )>
          /* typeMap 是我们事先约定好的 icon 列表 */
          <CustomIcon className=s.iconfont type=detail.type_id ? typeMap[detail.type_id].icon : 1 />
        </span>
        <span> detail.type_name || '' </span>
      </div>
      
        detail.pay_type == 1
          ? <div className=cx(s.amount, s.expense)>- detail.amount </div>
          : <div className=cx(s.amount, s.incom)>+ detail.amount </div>
      
      <div className=s.info>
        <div className=s.time>
          <span>记录时间</span>
          <span>detail.date</span>
        </div>
        <div className=s.remark>
          <span>备注</span>
          <span> detail.remark || '-' </span>
        </div>
      </div>
      <div className=s.operation>
        <span onClick=deleteDetail><CustomIcon type='shanchu' />删除</span>
        <span onClick=() => editRef.current && editRef.current.show()><CustomIcon type='tianjia' />编辑</span>
      </div>
    </div>
    <PopupAddBill ref=editRef detail=detail onReload=getDetail />
  </div>


export default Detail
.detail 
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: #f5f5f5;
  padding: 12px 24px 0 24px;


.card 
  border-radius: 12px;
  background-color: #fff;
  padding: 0 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  .type 
    padding: 24px 0 12px 0;
    span:nth-of-type(1) 
      display: inline-block;
      width: 22px;
      height: 22px;
      color: #fff;
      border-radius: 50%;
      text-align: center;
      line-height: 24px;
      margin-right: 8px;
    
    .expense 
      background-color: #007fff;
    
    .income 
      background-color: rgb(236, 190, 37);
    
    .iconfont 
      font-size: 16px;
    
  
  .amount 
    font-size: 24px;
    font-weight: 600;
    margin-bottom: 24px;
  
  .info 
    width: 100%;
    font-size: 14px;
    text-align: left;
    .time 
      display: flex;
      align-items: center;
      justify-content: flex-start;
      margin-bottom: 12px;
      span:nth-of-type(1) 
        flex: 3;
        color: rgba(0,0,0,0.5)
      
      span:nth-of-type(2) 
        flex: 9;
      
    
    .remark 
      display: flex;
      align-items: center;
      justify-content: flex-start;
      margin-bottom: 12px;
      span:nth-of-type(1) 
        flex: 3;
        color: rgba(0,0,0,0.5)
      
      span:nth-of-type(2) 
        flex: 9;
        color: rgba(0,0,0,0.9)
      
    
  
  .operation 
    width: 100%;
    height: 50px;
    display: flex;
    align-items: center;
    font-size: 16px;
    .van-icon 
      margin-right: 4px;
    
    span 
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100%;
      flex: 1
    
    span:nth-of-type(1) 
      color: red;
    
  

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

// 获取账单详情
export function billDetails(data) 
  return fetchData('/api/bill/details', 'get', data);


// 删除账单
export function billDelete(data) 
  return fetchData('/api/bill/delete', 'post', data);

5.更新 PopupAddBill 模块逻辑

里面添加了编辑功能的相关逻辑,以及接口的配置

import React,  forwardRef, useEffect, useRef, useState  from 'react';
import  Popup, Icon, Keyboard, Input, Toast   from 'zarm';
import CustomIcon from '@/components/CustomIcon'
import PopupDate from '../PopupDate'
import dayjs from 'dayjs';
import PropTypes from 'prop-types';
import  queryTypeList, billAdd, billUpdate  from './api/index.js'
import  typeMap  from '@/utils';
import cx from 'classnames';

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

const PopupAddBill = forwardRef(( detail = , onReload , ref) => 
  const dateRef = useRef();
  const id = detail && detail.id // 外部传进来的账单详情 id
  const [show, setShow] = useState(false) // 内部控制弹窗显示隐藏。
  const [date, setDate] = useState(new Date()); // 日期
  const [payType, setPayType] = useState('expense'); // 支出或收入类型
  const [currentType, setCurrentType] = useState(); // 当前选中账单类型
  const [amount, setAmount] = useState(''); // 账单金额
  const [expense, setExpense] = useState([]); // 支出类型数组
  const [income, setIncome] = useState([]); // 收入类型数组
  const [remark, setRemark] = useState(''); // 备注
  const [showRemark, setShowRemark] = useState(false); // 备注输入框

  // 详情信息回显
  useEffect(() => 
    if (detail.id) 
      setPayType(detail.pay_type == 1 ? 'expense' : 'income')
      setCurrentType(
        id: detail.type_id,
        name: detail.type_name
      )
      setRemark(detail.remark)
      setAmount(detail.amount)
      setDate(detail.date)
    
  , [detail])

  // 通过 forwardRef 拿到外部传入的 ref,并添加属性,使得父组件可以通过 ref 控制子组件。
  if (ref) 
    ref.current = 
      show: () => 
        setShow(true);
      ,
      close: () => 
        setShow(false);
      
    
  ;

  useEffect(async () => 
    const  data  = await queryTypeList();
    const _expense = data.filter(i => i.type == 1); // 支出类型
    const _income = data.filter(i => i.type == 2); // 收入类型
    setExpense(_expense);
    setIncome(_income);
    // 没有 id 的情况下,说明是新建账单。
    if(!id) 
      setCurrentType(_expense[0]); // 新建账单,类型默认是

以上是关于react + zarm 实现账单详情页以及编辑删除功能的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

eggjs 怎么实现账单详情页的获取详情接口?

eggjs 怎么实现账单详情页的删除接口?

react + zarm + rc-form + crypto-js 实现个人中心页面,头像上传,密码重置,登录退出功能