feat:export query bill to excel; payment channel only flywire

This commit is contained in:
LittleBoy 2024-07-29 13:07:02 +08:00
parent 9c7bf8c1cb
commit 6741e61e13
11 changed files with 76 additions and 48 deletions

View File

@ -1,4 +1,4 @@
import {Table, Typography} from "@douyinfe/semi-ui";
import {Space, Table, Typography} from "@douyinfe/semi-ui";
import {ColumnProps} from "@douyinfe/semi-ui/lib/es/table";
import React, {useMemo, useState} from "react";
import {useTranslation} from "react-i18next";
@ -18,6 +18,7 @@ type BillListProps = {
onPageChange: (pageIndex:number) => void;
tableFooter?: React.ReactNode;
loading?: boolean;
beforeTotalAmount?: React.ReactNode;
}
export const BillList: React.FC<BillListProps> = (props) => {
@ -213,16 +214,19 @@ export const BillList: React.FC<BillListProps> = (props) => {
return <Card
title={t('bill.title_bill_list')}
headerRight={<div className="bill-info">
<div className="bill-info-item">
<span className="bill-info-title">{t('bill.query_amount_total')} :</span>
<MoneyFormat money={props.source?.pagination.recordTotal || 0}/>
headerRight={<Space spacing={20}>
{props.beforeTotalAmount}
<div className="bill-info">
<div className="bill-info-item">
<span className="bill-info-title">{t('bill.query_amount_total')} :</span>
<MoneyFormat money={props.source?.pagination.recordTotal || 0}/>
</div>
<div className="bill-info-item">
<span className="bill-info-title current-amount">{t('bill.query_amount_current_page')} :</span>
<MoneyFormat money={currentTotalAmount || 0}/>
</div>
</div>
<div className="bill-info-item">
<span className="bill-info-title current-amount">{t('bill.query_amount_current_page')} :</span>
<MoneyFormat money={currentTotalAmount || 0}/>
</div>
</div>}
</Space>}
>
<div className="bill-list-table">
<Table<BillModel>

View File

@ -104,7 +104,7 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
placeholder={t('base.please_enter')}/>
</Col>
<Col xxl={6} xl={6} md={8}>
<Form.Select showClear field="payment_channel" label={t('bill.title_pay_method')}
<Form.Select showClear field="payment_channel" label={t('bill.title_pay_channel')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
<Form.Select.Option value="ASIAPAY">ASIAPAY</Form.Select.Option>
<Form.Select.Option value="FLYWIRE">FLYWIRE</Form.Select.Option>

View File

@ -30,6 +30,7 @@
"confirmed": "Confirmed",
"download-qr-code": "Download QR Code",
"download_receipt": "Download receipt",
"export_excel": "Export Excel",
"paid": "Paid",
"paid_confirm": "Please confirm the order status is set to paid",
"pay_status": "Bill Status",

View File

@ -30,6 +30,7 @@
"confirmed": "已对账",
"download-qr-code": "下载二维码",
"download_receipt": "下载收据",
"export_excel": "导出账单列表",
"paid": "已支付",
"paid_confirm": "是否将此订单状态设为已支付",
"pay_status": "账单状态",

View File

@ -30,6 +30,7 @@
"confirmed": "已對帳",
"download-qr-code": "下載二維碼",
"download_receipt": "下載收據",
"export_excel": "Export Excel",
"paid": "已支付",
"paid_confirm": "是否將此訂單狀態設為已支付",
"pay_status": "帳單狀態",

View File

@ -37,7 +37,7 @@ export const BillPaidModal: React.FC<BillPaidModalProps> = (props) => {
}
const onSubmit = (values: BillUpdateParams) => {
if (!props.bill) return;
finishBill({bill:props.bill,param:values}).then(props.onConfirm)
finishBill({bill: props.bill, param: values}).then(props.onConfirm)
// setState({
// loading: true
// })
@ -69,7 +69,7 @@ export const BillPaidModal: React.FC<BillPaidModalProps> = (props) => {
<Form<BillUpdateParams> onSubmit={onSubmit} initValues={{
payment_channel: 'FLYWIRE',
payment_method: 'card',
payment_method: '',
merchant_ref: props.bill?.merchant_ref,
payment_amount: props.bill?.amount,
actual_payment_amount: props.bill?.amount
@ -88,19 +88,34 @@ export const BillPaidModal: React.FC<BillPaidModalProps> = (props) => {
</Form.Select>
</Col>
<Col span={12}>
{/*<Form.AutoComplete*/}
{/* rules={[*/}
{/* {required: true, message: 'required error'},*/}
{/* ]}*/}
{/* placeholder={t('base.please_select')} style={{width: '100%'}}*/}
{/* data={[*/}
{/* {value:'card',label:'Card(VISA,MasterCard,UnionPay,JCB...)'},*/}
{/* {value:'wechat',label:'Wechat'},*/}
{/* {value:'alipay',label:'Alipay'},*/}
{/* {value:'other',label:'Other'},*/}
{/* ]}*/}
{/* // renderItem={()=>{*/}
{/* //*/}
{/* // }}*/}
{/* showClear field="payment_method" label={t('bill.title_pay_method')}*/}
{/*/>*/}
<Form.Select
rules={[
{required: true, message: 'required error'},
]}
showClear field="payment_method" label={t('bill.title_pay_method')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
<Form.Select.Option value="card">Card(VISA,MasterCard,UnionPay,JCB...)</Form.Select.Option>
<Form.Select.Option value="wechat">Wechat</Form.Select.Option>
<Form.Select.Option value="alipay">Alipay</Form.Select.Option>
<Form.Select.Option value="other">Other</Form.Select.Option>
{/*<Form.Select.Option value="ASIAPAY">ASIAPAY</Form.Select.Option>*/}
{/*<Form.Select.Option value="PPS">PPS</Form.Select.Option>*/}
</Form.Select>
optionList={[
{value: 'card', label: 'Card(VISA,MasterCard,UnionPay,JCB...)'},
{value: 'wechat', label: 'Wechat'},
{value: 'alipay', label: 'Alipay'},
{value: 'other', label: 'Other'},
]}
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}>
@ -139,7 +154,9 @@ export const BillPaidModal: React.FC<BillPaidModalProps> = (props) => {
<div className={'text-right'} style={{margin: '10px 0 20px'}}>
<Space spacing={12}>
<Button onClick={props.onCancel} type={'tertiary'}>{t('base.cancel')}</Button>
<Button loading={state.loading} htmlType={'submit'} theme={'solid'} type={'primary'}>{t('base.confirm_paid')}</Button>
<Button
loading={state.loading} htmlType={'submit'} theme={'solid'}
type={'primary'}>{t('base.confirm_paid')}</Button>
</Space>
</div>
</Form>

View File

@ -2,6 +2,7 @@ import {Button, Divider, Modal, Notification, Popconfirm, Toast} from "@douyinfe
import {useState} from "react";
import {useRequest, useSetState} from "ahooks";
import {useTranslation} from "react-i18next";
import {stringify} from "qs"
import {BillList} from "@/components/bill/list.tsx";
import SearchForm from "@/components/bill/search-form.tsx";
@ -12,6 +13,7 @@ import {useDownloadReceiptPDF} from "@/service/generate-pdf.ts";
import {BillDetailItems} from "@/components/bill";
import {BillPaidModal} from "@/pages/bill/components/bill_paid_modal.tsx";
import {BillTypeConfirm} from "@/pages/bill/components/bill_type_confirm.tsx";
import {saveAs} from "file-saver";
const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => {
@ -84,11 +86,17 @@ const BillQuery = () => {
}
</div>)
}
const onExportExcel = ()=>{
const downloadUrl = `${AppConfig.API_PREFIX || '/api'}/bills/export?${stringify(queryParams)}`
saveAs(downloadUrl, 'bill-result-excel.xlsx')
}
return (<div>
<SearchForm showApply loading={loading} onSearch={setBillQueryParams}/>
<BillList
type={'query'} loading={loading} source={data}
operationRender={operation} operationRenderWidth={180}
beforeTotalAmount={<Button onClick={onExportExcel} theme={'solid'} type={'secondary'}>{t('bill.export_excel')}</Button>}
onPageChange={(page_number) => {
setBillQueryParams({
...queryParams,

View File

@ -107,7 +107,7 @@ export default function Index() {
<div className={styles.QRCodeContainer}>
<QRCode size={250} className={styles.qrCode} bill={billInfo}/>
</div>
{billInfo && <div
{billInfo && billInfo.expiration_time && <div
className={styles.billExpTime}> {t('manual.exp_time')} {dayjs(billInfo.expiration_time).format('YYYY-MM-DD HH:mm')} </div>}
</div>
</Space>

View File

@ -1,16 +1,12 @@
import {useTranslation} from "react-i18next";
import {useEffect, useState} from "react";
import {useNavigate, useSearchParams} from "react-router-dom";
import {Radio, RadioGroup} from '@douyinfe/semi-ui';
import styles from './pay.module.less'
import {PayLogo} from "@/pages/pay/component";
import MoneyFormat from "@/components/money-format.tsx";
import FlywireLogo from "@/assets/images/pay/flywire.tsx";
import AsiaPayLogo from "@/assets/images/pay/asia_pay.tsx";
import {getBillDetail} from "@/service/api/bill.ts";
import {BillStatus} from "@/service/types.ts";
import {StartAsiaPay} from "@/pages/pay/component/start-asia-pay.tsx";
import {StartFlyWire} from "@/pages/pay/component/start-fly-wire.tsx";
@ -28,7 +24,7 @@ const PayIndex = () => {
return;
}
const payChannel = search.get('pay_channel') || 'asia_pay'
const payChannel = search.get('pay_channel') || 'flywire'
setPayChannel(payChannel)
getBillDetail(Number(billId)).then((bill) => {
// 判断bill状态
@ -59,19 +55,19 @@ const PayIndex = () => {
</div>}
</div>
</div>
<div className={styles.payChanel}>
<RadioGroup style={{width: '100%'}} type={'card'} onChange={(e) => setPayChannel(e.target.value)}
value={payChannel}>
<Radio value={'asia_pay'} style={{flex: 1}}>
<AsiaPayLogo/>
<span style={{marginLeft: 5}}>AsiaPay</span>
</Radio>
<Radio value={'flywire'} style={{flex: 1}}>
<FlywireLogo/>
<span style={{marginLeft: 5}}>Flywire</span>
</Radio>
</RadioGroup>
</div>
{/*<div className={styles.payChanel}>*/}
{/* <RadioGroup style={{width: '100%'}} type={'card'} onChange={(e) => setPayChannel(e.target.value)}*/}
{/* value={payChannel}>*/}
{/* <Radio value={'asia_pay'} style={{flex: 1}}>*/}
{/* <AsiaPayLogo/>*/}
{/* <span style={{marginLeft: 5}}>AsiaPay</span>*/}
{/* </Radio>*/}
{/* <Radio value={'flywire'} style={{flex: 1}}>*/}
{/* <FlywireLogo/>*/}
{/* <span style={{marginLeft: 5}}>Flywire</span>*/}
{/* </Radio>*/}
{/* </RadioGroup>*/}
{/*</div>*/}
<div className={styles.payDetail}>
{bill.details.length == 1 ? <div className="pay-item">
<div className="title">Payment Type:</div>
@ -96,7 +92,7 @@ const PayIndex = () => {
<div className={styles.payConfirm}>
{bill.student_email && <div className="student-email">Your Email: {bill.student_email}</div> }
<div className="pay-submit">
{ payChannel == 'asia_pay' && <StartAsiaPay bill={bill} />}
{/*{ payChannel == 'asia_pay' && <StartAsiaPay bill={bill} />}*/}
<StartFlyWire bill={bill} open={payChannel == 'flywire'} />
</div>
</div>

View File

@ -89,16 +89,16 @@ export async function finishFlywire({bill,param}: BillUpdateFormParams){
"data": {
"remark": param.remark,
"payment_id": param.merchant_ref,
"amount_from": param.actual_payment_amount,
"amount_from": Number(param.actual_payment_amount) * 100,
"currency_from": "HKD",
"amount_to": param.payment_amount,
"amount_to": Number(param.payment_amount) * 100,
"currency_to": "HKD",
"status": "guaranteed",
"expiration_date": dayjs().format('YYYY-MM-DDTHH:mm:ss[Z]'),
"external_reference": bill.id,
"country": "CN",
"payment_method": {
"type": param.payment_channel
"type": param.payment_method
},
"fields": {
"student_email": bill.student_email,

View File

@ -35,7 +35,7 @@ export function GeneratePdf(bill: BillModel) {
title: 'Programme:',
content: bill.programme_english_name
}, 56)
drawItem(doc, {title: 'Mode of Study:', content: bill.attendance_mode}, bill.programme_english_name.length > 70?70:64)
drawItem(doc, {title: 'Mode of Study:', content: bill.attendance_mode == 'FT' ? 'FULL-TIME': bill.attendance_mode}, bill.programme_english_name.length > 70?70:64)
// draw table
autoTable(doc, {
startY: 80,
@ -61,7 +61,7 @@ export function GeneratePdf(bill: BillModel) {
`#${it.id}`,
dayjs(bill.paid_at).format('YYYY-MM-DD'),
it.bill_type,
`${bill.payment_channel}` + bill.payment_channel != bill.payment_method ? `(${bill.payment_method})` : '',
`${bill.payment_channel}` + (bill.payment_channel != bill.payment_method ? `(${bill.payment_method})` : ''),
`${it.amount}`
];
})),