react + zarm 实现账单详情页以及编辑删除功能
Posted 凯小默:树上的男爵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react + zarm 实现账单详情页以及编辑删除功能相关的知识,希望对你有一定的参考价值。
需要实现的交互效果
编辑的功能点击还是弹出新增账单窗口那个模块,不过需要稍微改动一下。
实现过程
1.封装公用的头部 Header
在 components 目录下新建 Header 目录,添加两个文件 index.jsx
和 style.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.jsx
和 style.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 实现账单数据统计饼图效果
react + zarm + rc-form + crypto-js 实现个人中心页面,头像上传,密码重置,登录退出功能