280 lines
8.1 KiB
TypeScript

import {Button, Col, Divider, Form, InputNumber, Modal, Row, Select, Space} from "@douyinfe/semi-ui";
import {IconAlertCircle} from "@douyinfe/semi-icons";
import React from "react";
import {useTranslation} from "react-i18next";
import {useSetState} from "ahooks";
import {useBillTypes} from "@/hooks/useBillTypes.ts";
import {usePaymentChannels} from "@/hooks/usePaymentChannels.ts";
import {addBillRecord} from "@/service/api/bill.ts"
import dayjs from "dayjs";
type BillPaidModalProps = {
onConfirm: () => void
onCancel?: () => void
}
type BillTypeListProps = {
onClose?: (refresh?: boolean) => void;
onChange?: (confirms: ConfirmedBillDetail[]) => void;
}
export const BillTypeList: React.FC<BillTypeListProps> = (props) => {
const {t} = useTranslation()
const [state, setState] = useSetState<{
confirmed: ConfirmedBillDetail[]
}>({
confirmed: [{bill_type: '', amount: 0}]
})
const BillTypes = useBillTypes()
const onChange = (value: string, index: number, type: 'type' | 'amount') => {
if (state.confirmed.length <= index || !value) return;
const confirmed = [...state.confirmed]
if (type == 'type') {
confirmed[index].bill_type = value
} else {
confirmed[index].amount = Number(value)
}
setState({confirmed})
props.onChange?.(confirmed)
}
const addOrRemove = (index: number) => {
// 不允许删除最后一个
if (index > -1 && state.confirmed.length <= 1) return;
const confirmed = [...state.confirmed, ...(index == -1 ? [{bill_type: '', amount: 0}] : [])]
if (index > -1) confirmed.splice(index, 1)
setState({confirmed})
props.onChange?.(confirmed)
}
return (<div className={'bill-type-list'} style={{marginTop: 10}}>
<Divider>{t('bill.title_bill_detail')}</Divider>
{
state.confirmed.map((item, index) => (
<div key={index} className="confirm-item-btn align-center space-between" style={{marginTop: 20}}>
<Select
value={item.bill_type}
style={{width: 240}}
onChange={v => onChange(String(v), index, 'type')}
placeholder={t('base.please_select_bill_type')}>
{
BillTypes.map((it, idx) => (
<Select.Option key={idx} value={it.label}>{it.label}</Select.Option>))
}
</Select>
<Space spacing={10}>
<InputNumber
hideButtons precision={2} value={item.amount} type={'number'}
onChange={v => onChange(String(v), index, 'amount')} style={{width: 140}}/>
<Button
disabled={state.confirmed.length <= 1} onClick={() => addOrRemove(index)}
theme={'solid'} type={'secondary'}>{t('base.remove')}</Button>
</Space>
</div>
))
}
<div style={{marginTop: 10, marginBottom: 20}}>
<Button onClick={() => addOrRemove(-1)}>{t('base.add')}</Button>
</div>
</div>)
}
export const AddBillModal: React.FC<BillPaidModalProps> = (props) => {
const {t} = useTranslation()
const {paymentChannelList, paymentMethodList} = usePaymentChannels();
const [state, setState] = useSetState<{
loading?: boolean;
open?: boolean;
details: ConfirmedBillDetail[];
errorMessage?: string;
}>({
details: []
})
const onSubmit = (values: CreateBillRecordModel) => {
if (state.details.length == 0) {
setState({errorMessage: t('base.validate.error_details_message')})
return;
}
for (const it of state.details) {
if (!it.bill_type || it.amount <= 0) {
setState({errorMessage: t('base.validate.error_details_message')})
return;
}
}
setState({
loading: true, errorMessage: undefined
})
values.details = state.details
values.check_student = true;
values.initiated_paid_date = dayjs(values.initiated_paid_date).format("YYYY-MM-DD")
values.paid_date = dayjs(values.paid_date).format("YYYY-MM-DD")
values.delivered_date = dayjs(values.delivered_date).format("YYYY-MM-DD")
addBillRecord(values).then(()=>{
setState({open:false})
props.onConfirm()
}).catch(e => {
setState({errorMessage: e.message})
}).finally(() => {
setState({
loading: false
})
})
//
}
const handleClose = ()=>{
setState({
loading: false,
open:false,
errorMessage:undefined
})
}
return (<>
<Button onClick={() => setState({open: true})} theme={'solid'}>{t('bill.add_bill_record')}</Button>
<Modal
title={t('bill.add_bill_record')}
visible={state.open}
closeOnEsc={true}
onCancel={handleClose}
footer={null}
width={600}
maskClosable={false}
>
<Form<CreateBillRecordModel> onSubmit={onSubmit} initValues={{
merchant_ref: '',
application_number: '',
student_email: '',
paid_area: '',
initiated_paid_date: '',
paid_date: '',
delivered_date: '',
payment_method: '',
payment_channel: '',
check_student: true,
details: []
}}>
<Row gutter={20}>
<Col span={12}>
<Form.Input
rules={[
{required: true, message: 'required error'},
]}
showClear field="merchant_ref" label="Merchant Ref"
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
<Col span={12}>
<Form.Input
rules={[
{required: true, message: 'required error'},
]}
showClear field="application_number" label={t('bill.bill_number')}
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<Form.Input
rules={[
{required: true, message: 'required error'},
{type: 'email', message: t('base.validate.email')}
]}
type={'email'}
showClear field="student_email" label="Email"
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
<Col span={12}>
<Form.Input
rules={[
{required: true, message: 'required error'},
]}
showClear field="paid_area" label={t('bill.create.pay_area')}
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<Form.Select
rules={[
{required: true, message: 'required error'},
]}
optionList={paymentChannelList}
allowCreate filter showClear field="payment_channel" label={t('bill.title_pay_channel')}
placeholder={t('base.please_select')} style={{width: '100%'}}/>
</Col>
<Col span={12}>
<Form.Select
rules={[
{required: true, message: 'required error'},
]}
optionList={paymentMethodList}
allowCreate filter showClear field="payment_method" label={t('bill.title_pay_method')}
placeholder={t('base.please_select')} style={{width: '100%'}}/>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<Form.DatePicker
showClear
rules={[
{required: true, message: 'required error'},
]}
type={'date'}
field="initiated_paid_date"
label={t('bill.title_initiated_paid_at')}
style={{width: '100%'}}
/>
</Col>
<Col span={12}>
<Form.DatePicker
showClear
rules={[
{required: true, message: 'required error'},
]}
type={'date'}
field="paid_date"
label={t('bill.title_paid_at')}
style={{width: '100%'}}
/>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<Form.DatePicker
showClear
rules={[
{required: true, message: 'required error'},
]}
type={'date'}
field="delivered_date"
label={t('bill.title_delivered_at')}
style={{width: '100%'}}
/>
</Col>
</Row>
<div>
<BillTypeList onChange={(details) => setState({details})}/>
{state.errorMessage && <div className="semi-form-field-error-message" style={{marginBottom: 10}}>
<IconAlertCircle/>
<span style={{marginLeft: 5}}>{state.errorMessage}</span>
</div>}
<Divider/>
</div>
<div className={'text-right'} style={{margin: '20px 0'}}>
<Space spacing={12}>
<Button onClick={handleClose} type={'tertiary'}>{t('base.cancel')}</Button>
<Button
loading={state.loading} htmlType={'submit'} theme={'solid'}
type={'primary'}>{t('bill.create.confirm')}</Button>
</Space>
</div>
</Form>
</Modal>
</>)
}