🚀 feat: update bill confirm

This commit is contained in:
LittleBoy 2024-08-10 16:02:22 +08:00
parent 551bd7d10c
commit b16d6a237d
6 changed files with 97 additions and 143 deletions

View File

@ -252,7 +252,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
) : 'N/A'), ) : 'N/A'),
}, },
{ {
title: t('bill.title_bill_status'), title: t('bill.pay_status'),
dataIndex: 'status', dataIndex: 'status',
width: 150, width: 150,
render: value => billStatusText(value), render: value => billStatusText(value),
@ -304,7 +304,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
title={<Space> title={<Space>
<span>{t('bill.title_bill_list')}</span> <span>{t('bill.title_bill_list')}</span>
<span <span
className={'cursor-pointer'} style={{color:'pink'}} className={'cursor-pointer'} style={{color:'#0062d6'}}
onClick={() => setState({showColumnsConfig: !state.showColumnsConfig})} onClick={() => setState({showColumnsConfig: !state.showColumnsConfig})}
><IconSetting /></span> ><IconSetting /></span>
</Space>} </Space>}

View File

@ -11,6 +11,7 @@
"operate_success": "Operation success", "operate_success": "Operation success",
"please_enter": "Please Enter", "please_enter": "Please Enter",
"please_select": "Please Select", "please_select": "Please Select",
"please_select_bill_type": "Please Select Bill Type",
"qr-code": "QRCode", "qr-code": "QRCode",
"query_bill": "Failed to query bill:", "query_bill": "Failed to query bill:",
"remove": "Remove", "remove": "Remove",
@ -31,6 +32,7 @@
"confirm_bill_number": "Confirm Bill Number", "confirm_bill_number": "Confirm Bill Number",
"confirm_bill_type": "Confirm Bill", "confirm_bill_type": "Confirm Bill",
"confirm_bill_type_batch": "Batch confirm Bill Type", "confirm_bill_type_batch": "Batch confirm Bill Type",
"confirm_bill_warning_amount": "The bill amount and actual payment amount are inconsistent",
"confirm_confirm_title": "Confirm check and sync the Bill?", "confirm_confirm_title": "Confirm check and sync the Bill?",
"confirm_select_empty": "Require confirm bill data", "confirm_select_empty": "Require confirm bill data",
"confirm_student_number": "Confirm Student Number", "confirm_student_number": "Confirm Student Number",
@ -38,12 +40,12 @@
"confirmed": "Confirmed", "confirmed": "Confirmed",
"download-qr-code": "Download QR Code", "download-qr-code": "Download QR Code",
"download_receipt": "Download receipt", "download_receipt": "Download receipt",
"export_excel": "Export Excel", "export_excel": "Export Transaction Excel",
"import_bill": "Add Bill", "import_bill": "Add Transaction Record",
"import_excel": "Import Bill", "import_excel": "Import Transaction Excel",
"paid": "Paid", "paid": "Paid",
"paid_confirm": "Please confirm the order status is set to paid", "paid_confirm": "Please confirm the order status is set to paid",
"pay_status": "Bill Status", "pay_status": "Bill Payment Status",
"pay_status_canceled": "CANCELED", "pay_status_canceled": "CANCELED",
"pay_status_expired": "EXPIRED", "pay_status_expired": "EXPIRED",
"pay_status_paid": "PAID", "pay_status_paid": "PAID",

View File

@ -11,6 +11,7 @@
"operate_success": "操作成功", "operate_success": "操作成功",
"please_enter": "请输入", "please_enter": "请输入",
"please_select": "请选择", "please_select": "请选择",
"please_select_bill_type": "请选择账单类型",
"qr-code": "二维码", "qr-code": "二维码",
"query_bill": "查询账单失败:", "query_bill": "查询账单失败:",
"remove": "删除", "remove": "删除",
@ -31,6 +32,7 @@
"confirm_bill_number": "确认账单编号", "confirm_bill_number": "确认账单编号",
"confirm_bill_type": "确认账单", "confirm_bill_type": "确认账单",
"confirm_bill_type_batch": "批量确认账单", "confirm_bill_type_batch": "批量确认账单",
"confirm_bill_warning_amount": "账单金额和实付金额不一致",
"confirm_confirm_title": "请确定对账并同步此账单?", "confirm_confirm_title": "请确定对账并同步此账单?",
"confirm_select_empty": "对账账单为空", "confirm_select_empty": "对账账单为空",
"confirm_student_number": "确认学号", "confirm_student_number": "确认学号",
@ -38,12 +40,12 @@
"confirmed": "已对账", "confirmed": "已对账",
"download-qr-code": "下载二维码", "download-qr-code": "下载二维码",
"download_receipt": "下载收据", "download_receipt": "下载收据",
"export_excel": "导出账单", "export_excel": "导出交易记录",
"import_bill": "添加账单", "import_bill": "添加交易记录",
"import_excel": "导入账单", "import_excel": "导入交易记录",
"paid": "已支付", "paid": "已支付",
"paid_confirm": "是否将此订单状态设为已支付", "paid_confirm": "是否将此订单状态设为已支付",
"pay_status": "账单状态", "pay_status": "账单支付状态",
"pay_status_canceled": "已作废", "pay_status_canceled": "已作废",
"pay_status_expired": "已过期", "pay_status_expired": "已过期",
"pay_status_paid": "已支付", "pay_status_paid": "已支付",

View File

@ -11,6 +11,7 @@
"operate_success": "操作成功", "operate_success": "操作成功",
"please_enter": "請輸入", "please_enter": "請輸入",
"please_select": "請選擇", "please_select": "請選擇",
"please_select_bill_type": "請選擇帳單類型",
"qr-code": "QRCode", "qr-code": "QRCode",
"query_bill": "查詢帳單失敗:", "query_bill": "查詢帳單失敗:",
"remove": "刪除", "remove": "刪除",
@ -31,6 +32,7 @@
"confirm_bill_number": "確認帳單編號", "confirm_bill_number": "確認帳單編號",
"confirm_bill_type": "確認賬單", "confirm_bill_type": "確認賬單",
"confirm_bill_type_batch": "批次確認帳單", "confirm_bill_type_batch": "批次確認帳單",
"confirm_bill_warning_amount": "账单金额和实付金额不一致",
"confirm_confirm_title": "請確定對帳并同步此帳單?", "confirm_confirm_title": "請確定對帳并同步此帳單?",
"confirm_select_empty": "對帳帳單為空", "confirm_select_empty": "對帳帳單為空",
"confirm_student_number": "確認學號", "confirm_student_number": "確認學號",
@ -38,12 +40,12 @@
"confirmed": "已對帳", "confirmed": "已對帳",
"download-qr-code": "下載二維碼", "download-qr-code": "下載二維碼",
"download_receipt": "下載收據", "download_receipt": "下載收據",
"export_excel": "導出賬單", "export_excel": "導出交易记录",
"import_bill": "新增帳單", "import_bill": "新增交易记录",
"import_excel": "導入賬單", "import_excel": "導入交易记录",
"paid": "已支付", "paid": "已支付",
"paid_confirm": "是否將此訂單狀態設為已支付", "paid_confirm": "是否將此訂單狀態設為已支付",
"pay_status": "帳單狀態", "pay_status": "帳單支付狀態",
"pay_status_canceled": "已作廢", "pay_status_canceled": "已作廢",
"pay_status_expired": "已過期", "pay_status_expired": "已過期",
"pay_status_paid": "已付款", "pay_status_paid": "已付款",

View File

@ -1,5 +1,5 @@
import {Button, Select, Space, Divider, InputNumber, Modal,} from "@douyinfe/semi-ui"; import {Button, Select, Space, Divider, InputNumber, Modal,} from "@douyinfe/semi-ui";
import React, {useMemo} from "react"; import React from "react";
import {useSetState} from "ahooks"; import {useSetState} from "ahooks";
import MoneyFormat from "@/components/money-format.tsx"; import MoneyFormat from "@/components/money-format.tsx";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
@ -7,7 +7,7 @@ import {useBillTypes} from "@/hooks/useBillTypes.ts";
import {NumberConfirm} from "@/pages/bill/components/number_confirm.tsx"; import {NumberConfirm} from "@/pages/bill/components/number_confirm.tsx";
import {BillDetailItems} from "@/components/bill"; import {BillDetailItems} from "@/components/bill";
import {BillDetailItem} from "@/components/bill/bill-detail-items.tsx"; import {BillDetailItem} from "@/components/bill/bill-detail-items.tsx";
import {IconStudentId} from "@/components/icons"; import {IconMoney, IconStudentId} from "@/components/icons";
import {confirmBillType} from "@/service/api/bill.ts"; import {confirmBillType} from "@/service/api/bill.ts";
type BillTypeConfirmProps = { type BillTypeConfirmProps = {
@ -16,78 +16,52 @@ type BillTypeConfirmProps = {
onChange?: (confirms: ConfirmedBillDetail[]) => void; onChange?: (confirms: ConfirmedBillDetail[]) => void;
} }
const BillTypeConfirmItem = (props: { data: BillDetail; onChange: (confirms: ConfirmedBillDetail[]) => void; }) => { export const BillTypeConfirm: React.FC<BillTypeConfirmProps> = (props) => {
const it = props.data;
const BillTypes = useBillTypes()
const {t} = useTranslation() const {t} = useTranslation()
const confirmed: ConfirmedBillDetail[] = props.bill.details.map(it=>({
const [state, setState] = useSetState<{ bill_type: it.bill_type,
billTypeList: ConfirmedBillDetail[]; bill_detail_id: it.id,
loading?: boolean, amount: it.amount
confirmed?: boolean, }))
}>({ const [state, setState] = useSetState({
loading: false, confirm_application_number: '', confirmed
billTypeList: [
{bill_type: props.data.bill_type, bill_detail_id: it.id, amount: Number(props.data.amount)}
]
}) })
const BillTypes = useBillTypes()
const onChange = (value: string, index: number, type: 'type' | 'amount') => { const onChange = (value: string, index: number, type: 'type' | 'amount') => {
if (state.billTypeList.length <= index || !value) return; if (state.confirmed.length <= index || !value) return;
const billTypeList = [...state.billTypeList] const confirmed = [...state.confirmed]
if (type == 'type') { if (type == 'type') {
billTypeList[index].bill_type = value confirmed[index].bill_type = value
} else { } else {
billTypeList[index].amount = Number(value) confirmed[index].amount = Number(value)
} }
// 计算 confirmedTypes 中所有 amount 的总金额 setState({confirmed})
const totalAmount = billTypeList.reduce((total, item) => { props.onChange?.(confirmed)
return total + Number(item.amount)
}, 0)
// 判断是否已经超出账单实际金额
if (totalAmount > Number(it.amount)) {
Modal.warning({
title: 'Warning',
content: t('bill.confirm_amount_exceed_content'),
hasCancel: false
})
return;
} }
setState({billTypeList})
props.onChange(billTypeList) const addOrRemove = (index:number)=>{
} // 不允许删除最后一个
const onRemove = (index: number) => { if (index > -1 && state.confirmed.length <= 1) return;
if (state.billTypeList.length <= 1) return; const confirmed = [...state.confirmed,...(index==-1?[{bill_type: '', amount: 0, bill_detail_id: 0}]:[])]
const billTypeList = [...state.billTypeList] if(index > -1) confirmed.splice(index, 1)
billTypeList.splice(index, 1) setState({confirmed})
setState({billTypeList}) props.onChange?.(confirmed)
}
const onAdd = () => {
const billTypeList = [
...state.billTypeList,
{bill_type: props.data.bill_type, amount: 0, bill_detail_id: it.id}
]
setState({
billTypeList
})
props.onChange(billTypeList)
} }
return (<> return (<>
<div className="confirm-item" style={{margin: '20px 0'}}> <Divider>Bill Type Confirm</Divider>
<div style={{lineHeight: 1.1}} className={'align-center'}> {
<div>{it.bill_type}</div> state.confirmed.map((item, index) => (
<span style={{margin: '0 10px 0 120px'}}>Total Amount:</span> <div key={index} className="confirm-item-btn align-center space-between" style={{marginTop: 20}}>
<MoneyFormat money={it.amount}/>
</div>
{state.billTypeList.map((item, index) => {
return (
<div key={index} className="confirm-item-btn align-center space-between" style={{marginTop: 10}}>
<Select <Select
value={item.bill_type || it.bill_type} value={item.bill_type}
style={{width: 200}} style={{width: 240}}
onChange={v => onChange(String(v), index, 'type')} onChange={v => onChange(String(v), index, 'type')}
placeholder={t('manual.bill_type')}> placeholder={t('base.please_select_bill_type')}>
{ {
BillTypes.map((it, idx) => ( BillTypes.map((it, idx) => (
<Select.Option key={idx} value={it.label}>{it.label}</Select.Option>)) <Select.Option key={idx} value={it.label}>{it.label}</Select.Option>))
@ -97,60 +71,18 @@ const BillTypeConfirmItem = (props: { data: BillDetail; onChange: (confirms: Con
<Space spacing={10}> <Space spacing={10}>
<InputNumber <InputNumber
hideButtons precision={2} value={item.amount} type={'number'} hideButtons precision={2} value={item.amount} type={'number'}
onChange={v => onChange(String(v), index, 'amount')} style={{width: 120}}/> onChange={v => onChange(String(v), index, 'amount')} style={{width: 140}}/>
<Button <Button
disabled={state.billTypeList.length <= 1} onClick={() => onRemove(index)} disabled={state.confirmed.length <= 1} onClick={() => addOrRemove(index)}
theme={'solid'} type={'secondary'}>{t('base.remove')}</Button> theme={'solid'} type={'secondary'}>{t('base.remove')}</Button>
</Space> </Space>
</div>)
})}
<div style={{marginTop: 10}}>
<Button onClick={onAdd}>{t('base.add')}</Button>
</div> </div>
))
}
<div style={{marginTop: 10,marginBottom:20}}>
<Button onClick={()=>addOrRemove(-1)}>{t('base.add')}</Button>
</div> </div>
<Divider margin='12px'/> <Divider />
</>)
}
export const BillTypeConfirm: React.FC<BillTypeConfirmProps> = (props) => {
const [state, setState] = useSetState<{
confirm_application_number: string;
confirmed: {
[key: number]: ConfirmedBillDetail[]
}
}>({
confirm_application_number: '', confirmed: {}
})
const details = useMemo(() => {
const {details, detail_confirms} = props.bill;
if (!details) return [];
details.forEach(it => {
if (!detail_confirms) it.confirmed = [];
else it.confirmed = detail_confirms.filter(s => s.bill_detail_id == it.id)
})
return details;
}, [props.bill])
const onChange = (id: number, confirmedTypes: ConfirmedBillDetail[]) => {
const confirmed = {
...state.confirmed
};
confirmed[id] = confirmedTypes
setState({confirmed})
// trigger
const allConfirmed: ConfirmedBillDetail[] = [];
Object.keys(confirmed).forEach(key => {
allConfirmed.push(...confirmed[Number(key)])
})
props.onChange?.(allConfirmed)
}
return (<>
<Divider>Bill Type Confirm</Divider>
{
details.map((it, idx) => (<BillTypeConfirmItem
onChange={(confirmed) => onChange(it.id, confirmed)} data={it} key={idx}/>))
}
</>) </>)
} }
@ -166,6 +98,19 @@ export const BillTypeConfirmModal: React.FC<BillTypeConfirmProps> = (props) => {
}) })
const onBillConfirm = () => { const onBillConfirm = () => {
// 判断confirm的总金额是否和实付金额相等
const total = state.detail_confirms.reduce((total, item) => {
return total + item.amount
}, 0)
if(total != props.bill.actual_payment_amount){
Modal.warning({
title: 'Warning',
content: t('bill.confirm_bill_warning_amount')
})
return;
}
setState({loading: true}) setState({loading: true})
confirmBillType([{ confirmBillType([{
id: props.bill.id, id: props.bill.id,
@ -194,6 +139,9 @@ export const BillTypeConfirmModal: React.FC<BillTypeConfirmProps> = (props) => {
icon={<IconStudentId/>} title={t('base.bill_number')} icon={<IconStudentId/>} title={t('base.bill_number')}
value={props.bill.application_number || '-'}/> value={props.bill.application_number || '-'}/>
</>}/> </>}/>
<BillDetailItem
icon={<IconMoney/>} title={t('bill.title_actual_payment_amount')}
value={<MoneyFormat money={props.bill.actual_payment_amount}/>}/>
</div> </div>
<div className="confirm-number-container" style={{padding: '15px 0'}}> <div className="confirm-number-container" style={{padding: '15px 0'}}>
<Divider>Bill Number Confirm</Divider> <Divider>Bill Number Confirm</Divider>
@ -206,7 +154,7 @@ export const BillTypeConfirmModal: React.FC<BillTypeConfirmProps> = (props) => {
bill={props.bill} type={'application_number'}/> bill={props.bill} type={'application_number'}/>
</div> </div>
<BillTypeConfirm bill={props.bill} onChange={detail_confirms => setState({detail_confirms})}/> <BillTypeConfirm bill={props.bill} onChange={detail_confirms => setState({detail_confirms})}/>
<div className={'text-center'} style={{paddingBottom: 20}}> <div className={'text-center'} style={{paddingBottom: 20,marginTop:20}}>
<Button <Button
onClick={onBillConfirm} loading={state.loading} type={'primary'} onClick={onBillConfirm} loading={state.loading} type={'primary'}
theme={'solid'}>{t('base.confirm')}</Button> theme={'solid'}>{t('base.confirm')}</Button>

View File

@ -36,7 +36,7 @@ export const NumberConfirm: React.FC<NumberConfirmProps> = ({bill, type, onChang
<div className="confirm-item-btn"> <div className="confirm-item-btn">
<Space spacing={15}> <Space spacing={15}>
<Input <Input
onChange={onValueChange} style={{width: 180}} onChange={onValueChange} style={{width: 200}}
value={state.confirmNumber} placeholder={t('base.please_enter')}/> value={state.confirmNumber} placeholder={t('base.please_enter')}/>
{/*{*/} {/*{*/}