feat add bill/student number confirm
This commit is contained in:
parent
1f16e05c01
commit
c11c7ee922
@ -130,6 +130,9 @@ body #root{
|
||||
}
|
||||
}
|
||||
}
|
||||
.text-nowrap{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// input
|
||||
.semi-input-wrapper, .semi-select,.semi-datepicker-range-input,.semi-input-textarea-wrapper {
|
||||
|
@ -1,25 +1,29 @@
|
||||
import React, {useMemo} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
import {IconBillType, IconMoney, IconStudentEmail, IconStudentId} from "@/components/icons";
|
||||
import {IconBillType, IconMoney, IconStudentEmail, IconStudentId, IconStudentName} from "@/components/icons";
|
||||
import MoneyFormat from "@/components/money-format.tsx";
|
||||
import './bill.less'
|
||||
|
||||
const BillDetailItem = (item: { title: React.ReactNode; value: React.ReactNode, icon?: React.ReactNode }) => {
|
||||
export const BillDetailItem = (item: { title: React.ReactNode; value: React.ReactNode, icon?: React.ReactNode }) => {
|
||||
return <div className={'bill-detail-item'}>
|
||||
<div className={'detail-item-title'}>{item.icon} <span className={'item-title'}>{item.title}</span> :</div>
|
||||
<div className={'detail-item-value'}>{item.value}</div>
|
||||
</div>
|
||||
}
|
||||
const BillDetailItems = (prop: { bill: BillModel }) => {
|
||||
type BillDetailItemsProps = {
|
||||
bill: BillModel;
|
||||
studentNumberRender?: React.ReactNode;
|
||||
}
|
||||
const BillDetailItems = (prop: BillDetailItemsProps) => {
|
||||
const {t} = useTranslation();
|
||||
const billType = useMemo(()=>{
|
||||
return prop.bill.details[0].bill_type
|
||||
},[prop.bill])
|
||||
return (<>
|
||||
<BillDetailItem icon={<IconBillType/>} title={t('manual.bill_type')} value={billType}/>
|
||||
<BillDetailItem icon={<IconStudentId/>} title={t('manual.student_number')} value={prop.bill.student_number || prop.bill.application_number}/>
|
||||
<BillDetailItem icon={<IconStudentId/>} title={t('bill.title_student_name')}
|
||||
{prop.studentNumberRender?prop.studentNumberRender:<BillDetailItem icon={<IconStudentId/>} title={t('manual.student_number')} value={prop.bill.student_number || prop.bill.application_number || '-'}/>}
|
||||
<BillDetailItem icon={<IconStudentName/>} title={t('bill.title_student_name')}
|
||||
value={`${prop.bill.student_english_name||'-'}${prop.bill.student_chinese_name?' / '+ prop.bill.student_chinese_name : ''}`}/>
|
||||
<BillDetailItem icon={<IconStudentEmail/>} title={'Email'} value={prop.bill.student_email||'-'}/>
|
||||
<BillDetailItem icon={<IconMoney/>} title={t('manual.amount')} value={<MoneyFormat money={prop.bill.amount}/>}/>
|
||||
|
@ -3,6 +3,7 @@ import {ColumnProps} from "@douyinfe/semi-ui/lib/es/table";
|
||||
import React, {useMemo, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import dayjs from "dayjs";
|
||||
import {IconCheckCircleStroked} from "@douyinfe/semi-icons";
|
||||
|
||||
import MoneyFormat from "@/components/money-format.tsx";
|
||||
import {Card} from "@/components/card";
|
||||
@ -20,6 +21,15 @@ type BillListProps = {
|
||||
loading?: boolean;
|
||||
beforeTotalAmount?: React.ReactNode;
|
||||
}
|
||||
const CheckNumberCorrect = ({origin,confirmed}:{origin: string,confirmed?:string}) => {
|
||||
if(origin == confirmed && origin){
|
||||
return (<Space style={{marginTop:2,color:'green'}}><span>{origin}</span><IconCheckCircleStroked /></Space>)
|
||||
}
|
||||
return <div style={{lineHeight:1}}>
|
||||
<div style={confirmed?{color:'red'}:{}}>{origin?.length ?origin: 'N/A'}</div>
|
||||
{confirmed&&<Space style={{marginTop:2,color:'green'}}><span>{confirmed}</span><IconCheckCircleStroked /></Space>}
|
||||
</div>
|
||||
}
|
||||
|
||||
export const BillList: React.FC<BillListProps> = (props) => {
|
||||
const {t, i18n} = useTranslation()
|
||||
@ -32,7 +42,10 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
return t('bill.pay_status_pending')
|
||||
case 'PAID':
|
||||
return t('bill.pay_status_paid')
|
||||
case 'EXPIRED':
|
||||
return t('bill.pay_status_expired')
|
||||
case 'CANCELED':
|
||||
case 'CANCELLED':
|
||||
return t('bill.pay_status_canceled')
|
||||
default:
|
||||
return billStatus
|
||||
@ -56,45 +69,52 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
dataIndex: 'id',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: 'Merchant Ref',
|
||||
dataIndex: 'merchant_ref',
|
||||
width: 200,
|
||||
// render: (_) => (<MoneyFormat money={_}/>),
|
||||
},
|
||||
{
|
||||
title: t('base.student_number'),
|
||||
dataIndex: 'student_number',
|
||||
width: 150,
|
||||
render: (value) => value?.length ?value: 'N/A'
|
||||
render: (value,record) => (<CheckNumberCorrect origin={value} confirmed={record.student_number_confirm || 'test-confirm'} />)
|
||||
},
|
||||
{
|
||||
title: t('base.bill_number'),
|
||||
dataIndex: 'application_number',
|
||||
width: 150,
|
||||
render: (value,record) => (<CheckNumberCorrect origin={value} confirmed={value||record.application_number_confirm} />)
|
||||
},
|
||||
{
|
||||
title: '开始支付时间',
|
||||
title: t('bill.title_initiated_paid_at'),
|
||||
dataIndex: 'initiated_paid_at',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (value) => value?.length ?value: 'N/A'
|
||||
},
|
||||
{
|
||||
title: '到账时间',
|
||||
title: t('bill.title_delivered_at'),
|
||||
dataIndex: 'delivered_at',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (value) => value?.length ?value: 'N/A'
|
||||
},
|
||||
{
|
||||
title: t('bill.title_paid_at'),
|
||||
dataIndex: 'paid_at',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (value) => value?.length ?value: 'N/A'
|
||||
},
|
||||
{
|
||||
title: t('bill.title_create_at'),
|
||||
dataIndex: 'create_at',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (value) => value?.length ?value: 'N/A'
|
||||
},
|
||||
{
|
||||
title: t('bill.title_student_name'),
|
||||
dataIndex: 'student_english_name',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (_, record) => (<div>{record.student_english_name}<br/>{record.student_chinese_name}</div>)
|
||||
},
|
||||
{
|
||||
@ -118,7 +138,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
title: t('bill.title_year'),
|
||||
dataIndex: 'intake_year',
|
||||
width: 120,
|
||||
render: (_, record) => (<div>{record.intake_year}/{String(record.intake_semester).length == 1 ? '0':''}{record.intake_semester}</div>)
|
||||
render: (_, record) => record.intake_year?(<div>{record.intake_year}/{String(record.intake_semester).length == 1 ? '0':''}{record.intake_semester}</div>):"N/A"
|
||||
},
|
||||
// {
|
||||
// title: t('bill.title_semester'),
|
||||
@ -130,7 +150,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
dataIndex: 'detail',
|
||||
ellipsis: {showTitle: true},
|
||||
width: 220,
|
||||
render: (_, record) => (<div style={{fontSize: 13, lineHeight: 1.2}}>
|
||||
render: (_, record) => (<div style={{fontSize: 13, lineHeight: 1.2,wordBreak:'break-all',maxWidth:'100%',whiteSpace:'normal'}}>
|
||||
{record.details.map((it, idx) => (<div key={idx}>{it.bill_type}: <MoneyFormat money={it.amount}/></div>))}
|
||||
</div>),
|
||||
},
|
||||
@ -139,7 +159,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
dataIndex: '_detail',
|
||||
ellipsis: {showTitle: true},
|
||||
width: 220,
|
||||
render: (_, record) => (<div style={{fontSize: 13, lineHeight: 1.2}}>
|
||||
render: (_, record) => (<div style={{fontSize: 13, lineHeight: 1.2,wordBreak:'break-all',maxWidth:'100%',whiteSpace:'normal'}}>
|
||||
{record.details.filter(s=>s.confirm_status == 'CONFIRMED').map((it) => (<div key={it.id}>{it.confirm_type}: <MoneyFormat money={it.amount}/></div>))}
|
||||
</div>),
|
||||
},
|
||||
@ -187,12 +207,6 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
</div>
|
||||
):'N/A'),
|
||||
},
|
||||
{
|
||||
title: 'Merchant Ref',
|
||||
dataIndex: 'merchant_ref',
|
||||
width: 250,
|
||||
// render: (_) => (<MoneyFormat money={_}/>),
|
||||
},
|
||||
{
|
||||
title: t('bill.title_bill_status'),
|
||||
dataIndex: 'status',
|
||||
|
@ -4,6 +4,7 @@ import dayjs from "dayjs";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {Card} from "@/components/card";
|
||||
import {BillQueryParams} from "@/service/api/bill.ts";
|
||||
import {BillTypes} from "@/service/bill-types.ts";
|
||||
|
||||
type SearchFormProps = {
|
||||
onSearch?: (params: BillQueryParams) => void;
|
||||
@ -20,6 +21,7 @@ type SearchFormFields = {
|
||||
application_number?: string;
|
||||
bill_number?: string;
|
||||
payment_channel?: string;
|
||||
confirm_status?: ConfirmStatus;
|
||||
bill_status?: string;
|
||||
apply_status?: string;
|
||||
sort_by?: string;
|
||||
@ -35,9 +37,6 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
if (value.id) {
|
||||
params.id = value.id;
|
||||
}
|
||||
if (value.merchant_ref) {
|
||||
params.merchant_ref = value.merchant_ref;
|
||||
}
|
||||
if (value.student_number) {
|
||||
params.student_number = value.student_number;
|
||||
}
|
||||
@ -48,6 +47,10 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
if (value.apply_status) {
|
||||
params.apply_status = value.apply_status;
|
||||
}
|
||||
// 确认状态
|
||||
if (value.confirm_status) {
|
||||
params.confirm_status = value.confirm_status;
|
||||
}
|
||||
// 账单状态
|
||||
if (value.bill_status) {
|
||||
params.status = value.bill_status;
|
||||
@ -91,17 +94,19 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
<div className="bill-search-form">
|
||||
<Form<SearchFormFields> onSubmit={formSubmit}>
|
||||
<Row type={'flex'} gutter={16}>
|
||||
<Col xxl={3} xl={6} md={8}>
|
||||
<Form.Input showClear field='id' label="ID" trigger='blur' placeholder={t('base.please_enter')}/>
|
||||
</Col>
|
||||
<Col xxl={3} xl={6} md={8}>
|
||||
<Form.Input showClear field='merchant_ref' label="Merchant Ref" trigger='blur' placeholder={t('base.please_enter')}/>
|
||||
</Col>
|
||||
<Col xxl={6} xl={6} md={8}>
|
||||
<Form.DatePicker showClear type={'dateRange'} field="dateRange" label={t('bill.bill_date')}
|
||||
<Form.DatePicker showClear type={'dateRange'} field="dateRange" label={t('bill.title_initiated_paid_at')}
|
||||
style={{width: '100%'}}>
|
||||
</Form.DatePicker>
|
||||
</Col>
|
||||
<Col xxl={6} xl={6} md={8}>
|
||||
<Form.DatePicker showClear type={'dateRange'} field="delivered_at" label={t('bill.title_delivered_at')}
|
||||
style={{width: '100%'}}>
|
||||
</Form.DatePicker>
|
||||
</Col>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Input showClear field='id' label="ID / Merchant Ref" trigger='blur' placeholder={t('base.please_enter')}/>
|
||||
</Col>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Input showClear field='student_number' label={t('base.student_number')} trigger='blur'
|
||||
placeholder={t('base.please_enter')}/>
|
||||
@ -126,7 +131,7 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
<Form.Select.Option value="create_at asc">{t('bill.title_create_at')} {t('bill.sort_asc')}</Form.Select.Option>
|
||||
</Form.Select>
|
||||
</Col>
|
||||
<Col xxl={6} xl={6} md={8}>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Select showClear field="payment_channel" label={t('bill.title_pay_channel')}
|
||||
placeholder={t('base.please_select')} style={{width: '100%'}}>
|
||||
<Form.Select.Option value="FLYWIRE">FLYWIRE</Form.Select.Option>
|
||||
@ -134,23 +139,34 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
<Form.Select.Option value="PPS">PPS</Form.Select.Option>
|
||||
</Form.Select>
|
||||
</Col>
|
||||
<Col xxl={6} xl={6} md={8}>
|
||||
<Form.Select showClear field="bill_type" label={t('bill.title_bill_type')}
|
||||
placeholder={t('base.please_select')} style={{width: '100%'}}>
|
||||
<Form.Select.Option value="FLYWIRE">FLYWIRE</Form.Select.Option>
|
||||
<Form.Select.Option value="CBP">CBP</Form.Select.Option>
|
||||
<Form.Select.Option value="PPS">PPS</Form.Select.Option>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Select
|
||||
field="bill_type" style={{width: '100%'}}
|
||||
label={t('manual.bill_type')}
|
||||
placeholder={t('manual.bill_type')}
|
||||
>
|
||||
{
|
||||
BillTypes.map((it, idx) => (
|
||||
<Form.Select.Option key={idx} value={it.label}>{it.label}</Form.Select.Option>))
|
||||
}
|
||||
</Form.Select>
|
||||
</Col>
|
||||
{props.showApply && <>
|
||||
<Col xxl={6} xl={6} md={8}>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Select showClear field="confirm_status" label={t('bill.title_confirm_status')}
|
||||
placeholder={t('base.please_select')} style={{width: '100%'}}>
|
||||
<Form.Select.Option value="CONFIRMED">{t('bill.status_confirmed')}</Form.Select.Option>
|
||||
<Form.Select.Option value="UNCONFIRMED">{t('bill.status_unconfirmed')}</Form.Select.Option>
|
||||
</Form.Select>
|
||||
</Col>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Select showClear field="bill_status" label={t('bill.pay_status')}
|
||||
placeholder={t('base.please_select')} style={{width: '100%'}}>
|
||||
{billStatusOptions.map((item, index) => (
|
||||
<Form.Select.Option key={index} value={item.value}>{item.label}</Form.Select.Option>))}
|
||||
</Form.Select>
|
||||
</Col>
|
||||
<Col xxl={6} xl={6} md={8}>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Select showClear field="apply_status" label={t('bill.title_reconciliation_status')}
|
||||
placeholder={t('base.please_select')} style={{width: '100%'}}>
|
||||
{applyStatusOptions.map((item, index) => (
|
||||
@ -158,7 +174,7 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
</Form.Select>
|
||||
</Col>
|
||||
</>}
|
||||
<Col xxl={6} xl={6} md={8} style={{display: 'flex', alignItems: 'flex-end', paddingBottom: 12}}>
|
||||
<Col xxl={4} xl={6} md={8} style={{display: 'flex', alignItems: 'flex-end', paddingBottom: 12}}>
|
||||
<Button loading={props.loading} style={{width: 100}} htmlType={'submit'} theme={'solid'}
|
||||
type={'primary'}>{t('base.btn_search_submit')}</Button>
|
||||
</Col>
|
||||
|
@ -60,6 +60,22 @@ export const IconMoney = ({style}: IconProps) => {
|
||||
}
|
||||
|
||||
export const IconStudentId = ({style}: IconProps) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em" height="1em" style={style}>
|
||||
<path
|
||||
d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32z m-40 632H136V232h752v560z"
|
||||
fill="#00C479"
|
||||
></path>
|
||||
<path
|
||||
d="M610.3 476h123.4c1.3 0 2.3-3.6 2.3-8v-48c0-4.4-1-8-2.3-8H610.3c-1.3 0-2.3 3.6-2.3 8v48c0 4.4 1 8 2.3 8zM615.1 620h185.7c3.9 0 7.1-3.6 7.1-8v-48c0-4.4-3.2-8-7.1-8H615.1c-3.9 0-7.1 3.6-7.1 8v48c0 4.4 3.2 8 7.1 8zM224 673h43.9c4.2 0 7.6-3.3 7.9-7.5 3.8-50.5 46-90.5 97.2-90.5s93.4 40 97.2 90.5c0.3 4.2 3.7 7.5 7.9 7.5H522c4.6 0 8.2-3.8 8-8.4-2.8-53.3-32-99.7-74.6-126.1 18.1-19.9 29.1-46.4 29.1-75.5 0-61.9-49.9-112-111.4-112s-111.4 50.1-111.4 112c0 29.1 11 55.5 29.1 75.5-42.7 26.5-71.8 72.8-74.6 126.1-0.4 4.6 3.2 8.4 7.8 8.4z m149-262c28.5 0 51.7 23.3 51.7 52s-23.2 52-51.7 52-51.7-23.3-51.7-52 23.2-52 51.7-52z"
|
||||
></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const IconStudentName = ({style}: IconProps) => {
|
||||
return (
|
||||
<svg className="icon" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em" height="1em" style={style}>
|
||||
@ -88,6 +104,7 @@ export const IconStudentId = ({style}: IconProps) => {
|
||||
</g>
|
||||
</svg>)
|
||||
}
|
||||
|
||||
export const IconStudentEmail = ({style}: IconProps) => {
|
||||
return (
|
||||
<svg className="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -15,7 +15,7 @@
|
||||
"student_number": "Student Number"
|
||||
},
|
||||
"bill": {
|
||||
"bill_date": "Date",
|
||||
"bill_date": "In",
|
||||
"bill_number": "Bill Number",
|
||||
"cancel": "Cancel",
|
||||
"cancel_confirm": "Please make sure to cancel the bill",
|
||||
@ -23,9 +23,13 @@
|
||||
"cancel_success": "Successful cancel bill",
|
||||
"confirm": "Check",
|
||||
"confirm_batch": "Batch Confirm",
|
||||
"confirm_bill": "Confirm Bill Information",
|
||||
"confirm_bill_number": "Confirm Bill Number",
|
||||
"confirm_bill_type": "Confirm Bill",
|
||||
"confirm_bill_type_batch": "Batch confirm Bill Type",
|
||||
"confirm_confirm_title": "Confirm check the Bill?",
|
||||
"confirm_select_empty": "Require confirm bill data",
|
||||
"confirm_student_number": "Confirm Student Number",
|
||||
"confirm_success": "Confirm success!",
|
||||
"confirmed": "Confirmed",
|
||||
"download-qr-code": "Download QR Code",
|
||||
@ -36,6 +40,7 @@
|
||||
"paid_confirm": "Please confirm the order status is set to paid",
|
||||
"pay_status": "Bill Status",
|
||||
"pay_status_canceled": "CANCELED",
|
||||
"pay_status_expired": "EXPIRED",
|
||||
"pay_status_paid": "PAID",
|
||||
"pay_status_pending": "PENDING",
|
||||
"query_amount_current_page": "The total amount of current page",
|
||||
@ -46,6 +51,8 @@
|
||||
"set_bill_paid": "Set Bill Paid",
|
||||
"sort_asc": "ASC",
|
||||
"sort_desc": "DESC",
|
||||
"status_confirmed": "CONFIRMED",
|
||||
"status_unconfirmed": "UNCONFIRMED",
|
||||
"title_actual_payment_amount": "Actually Paid",
|
||||
"title_amount": "Amount",
|
||||
"title_bill_detail": "Bill Detail",
|
||||
@ -53,17 +60,20 @@
|
||||
"title_bill_status": "Bill Status",
|
||||
"title_bill_type": "Bill Type",
|
||||
"title_bill_type_confirm": "Confirm Bill Type",
|
||||
"title_create_at": "Input Date",
|
||||
"title_confirm_status": "Confirm Status",
|
||||
"title_create_at": "Input Time",
|
||||
"title_delivered_at": "Delivered Time",
|
||||
"title_department": "Department",
|
||||
"title_initiated_paid_at": "Initiated Time",
|
||||
"title_operate": "Operation",
|
||||
"title_paid_at": "Transaction Date",
|
||||
"title_paid_at": "Transaction Time",
|
||||
"title_pay_amount": "Pay Amount",
|
||||
"title_pay_channel": "Payment Channel",
|
||||
"title_pay_method": "Pay Method",
|
||||
"title_pay_sort": "Sort By",
|
||||
"title_program_id": "Program ID",
|
||||
"title_program_name": "Program",
|
||||
"title_reconciliation_status": "Reconciliation",
|
||||
"title_reconciliation_status": "Reconciliation Status",
|
||||
"title_remark": "Remark",
|
||||
"title_semester": "Semester",
|
||||
"title_service_charge": "Service Charge",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"student_number": "学号"
|
||||
},
|
||||
"bill": {
|
||||
"bill_date": "支付日期",
|
||||
"bill_date": "开始支付时间",
|
||||
"bill_number": "账单编号",
|
||||
"cancel": "作废",
|
||||
"cancel_confirm": "确定作废此账单",
|
||||
@ -23,9 +23,13 @@
|
||||
"cancel_success": "作废账单成功",
|
||||
"confirm": "对账",
|
||||
"confirm_batch": "批量对账",
|
||||
"confirm_bill": "确认账单信息",
|
||||
"confirm_bill_number": "确认账单编号",
|
||||
"confirm_bill_type": "确认账单",
|
||||
"confirm_bill_type_batch": "批量确认账单",
|
||||
"confirm_confirm_title": "请确定对账此账单?",
|
||||
"confirm_select_empty": "对账账单为空",
|
||||
"confirm_student_number": "确认学号",
|
||||
"confirm_success": "对账成功!",
|
||||
"confirmed": "已对账",
|
||||
"download-qr-code": "下载二维码",
|
||||
@ -36,6 +40,7 @@
|
||||
"paid_confirm": "是否将此订单状态设为已支付",
|
||||
"pay_status": "账单状态",
|
||||
"pay_status_canceled": "已作废",
|
||||
"pay_status_expired": "已过期",
|
||||
"pay_status_paid": "已支付",
|
||||
"pay_status_pending": "未支付",
|
||||
"query_amount_current_page": "当前页总金额",
|
||||
@ -46,6 +51,8 @@
|
||||
"set_bill_paid": "设置账单支付完成",
|
||||
"sort_asc": "升序",
|
||||
"sort_desc": "降序",
|
||||
"status_confirmed": "已确认",
|
||||
"status_unconfirmed": "未确认",
|
||||
"title_actual_payment_amount": "实付金额",
|
||||
"title_amount": "账单金额",
|
||||
"title_bill_detail": "账单详情",
|
||||
@ -53,8 +60,11 @@
|
||||
"title_bill_status": "账单状态",
|
||||
"title_bill_type": "账单类型",
|
||||
"title_bill_type_confirm": "确认账单",
|
||||
"title_confirm_status": "确认状态",
|
||||
"title_create_at": "创建时间",
|
||||
"title_delivered_at": "到账时间",
|
||||
"title_department": "学系",
|
||||
"title_initiated_paid_at": "开始支付时间",
|
||||
"title_operate": "操作",
|
||||
"title_paid_at": "支付时间",
|
||||
"title_pay_amount": "应付金额",
|
||||
|
@ -15,7 +15,7 @@
|
||||
"student_number": "學號"
|
||||
},
|
||||
"bill": {
|
||||
"bill_date": "支付日期",
|
||||
"bill_date": "開始支付時間",
|
||||
"bill_number": "帳單編號",
|
||||
"cancel": "作廢",
|
||||
"cancel_confirm": "確定作廢此帳單",
|
||||
@ -23,9 +23,13 @@
|
||||
"cancel_success": "作廢帳單成功",
|
||||
"confirm": "對帳",
|
||||
"confirm_batch": "批次對帳",
|
||||
"confirm_bill": "確認帳單資訊",
|
||||
"confirm_bill_number": "確認帳單編號",
|
||||
"confirm_bill_type": "確認賬單",
|
||||
"confirm_bill_type_batch": "批次確認帳單",
|
||||
"confirm_confirm_title": "請確定對帳此帳單?",
|
||||
"confirm_select_empty": "對帳帳單為空",
|
||||
"confirm_student_number": "確認學號",
|
||||
"confirm_success": "對帳成功!",
|
||||
"confirmed": "已對帳",
|
||||
"download-qr-code": "下載二維碼",
|
||||
@ -36,6 +40,7 @@
|
||||
"paid_confirm": "是否將此訂單狀態設為已支付",
|
||||
"pay_status": "帳單狀態",
|
||||
"pay_status_canceled": "已作廢",
|
||||
"pay_status_expired": "已過期",
|
||||
"pay_status_paid": "已付款",
|
||||
"pay_status_pending": "未付款",
|
||||
"query_amount_current_page": "目前頁總金額",
|
||||
@ -46,6 +51,8 @@
|
||||
"set_bill_paid": "設定帳單支付完成",
|
||||
"sort_asc": "升序",
|
||||
"sort_desc": "降序",
|
||||
"status_confirmed": "已確認",
|
||||
"status_unconfirmed": "未確認",
|
||||
"title_actual_payment_amount": "實付金額",
|
||||
"title_amount": "帳單金額",
|
||||
"title_bill_detail": "帳單詳情",
|
||||
@ -53,8 +60,11 @@
|
||||
"title_bill_status": "帳單狀態",
|
||||
"title_bill_type": "帳單類型",
|
||||
"title_bill_type_confirm": "確認帳單",
|
||||
"title_confirm_status": "確認狀態",
|
||||
"title_create_at": "創建時間",
|
||||
"title_delivered_at": "到帳時間",
|
||||
"title_department": "學系",
|
||||
"title_initiated_paid_at": "開始支付時間",
|
||||
"title_operate": "操作",
|
||||
"title_paid_at": "付款時間",
|
||||
"title_pay_amount": "應付金額",
|
||||
|
@ -27,15 +27,15 @@ export const BillTypeConfirm: React.FC<BillTypeConfirmProps> = (props) => {
|
||||
})
|
||||
}
|
||||
return <div className="confirm-item align-center space-between"
|
||||
style={{marginBottom: 10}}>
|
||||
<div>
|
||||
style={{marginBottom: 20}}>
|
||||
<div style={{lineHeight:1.1}}>
|
||||
<div>{it.bill_type}</div>
|
||||
<div>
|
||||
<div style={{fontSize:12}}>
|
||||
<MoneyFormat money={it.amount}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="confirm-item-btn">
|
||||
<Space spacing={20}>
|
||||
<Space spacing={15}>
|
||||
{it.confirm_status != 'CONFIRMED' && <Select onChange={v=>setState({bill_type:String(v)})} defaultValue={it.bill_type} style={{width:180}} placeholder={t('manual.bill_type')}>
|
||||
{
|
||||
BillTypes.map((it, idx) => (
|
||||
@ -43,12 +43,12 @@ export const BillTypeConfirm: React.FC<BillTypeConfirmProps> = (props) => {
|
||||
}
|
||||
</Select>}
|
||||
{
|
||||
it.confirm_status == 'CONFIRMED' ? <Tag color='light-blue'>{state.bill_type}</Tag> : <>
|
||||
it.confirm_status == 'CONFIRMED' ? <Tag size={'large'} color='light-blue'>{state.bill_type}</Tag> : <>
|
||||
<Popconfirm
|
||||
title={'Notice'} onConfirm={() => onConfirmBill()}
|
||||
position={'topRight'}
|
||||
content={`${t('bill.confirm_bill_type')}?`}
|
||||
><Button loading={state.loading} theme={'solid'}>{t('base.confirm')}</Button></Popconfirm>
|
||||
><Button style={{width:80}} loading={state.loading} theme={'solid'}>{t('base.confirm')}</Button></Popconfirm>
|
||||
</>
|
||||
}
|
||||
</Space>
|
||||
|
56
src/pages/bill/components/number_confirm.tsx
Normal file
56
src/pages/bill/components/number_confirm.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import {Button, Space, Tag, Input} from "@douyinfe/semi-ui";
|
||||
import React from "react";
|
||||
import {useSetState} from "ahooks";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
type NumberConfirmProps = {
|
||||
bill: BillModel;
|
||||
type: 'student_number' | 'application_number'
|
||||
}
|
||||
|
||||
export const NumberConfirm: React.FC<NumberConfirmProps> = (props) => {
|
||||
const {t} = useTranslation()
|
||||
const [state, setState] = useSetState({
|
||||
loading: false,
|
||||
confirmed: false,
|
||||
confirmNumber: (props.type == 'application_number' ? props.bill.application_number : props.bill.student_number) || '',
|
||||
})
|
||||
const onConfirm = () => {
|
||||
if (!state.confirmNumber.length) return
|
||||
|
||||
setState({loading: true})
|
||||
setTimeout(() => {
|
||||
setState({loading: false, confirmed: true})
|
||||
}, 500)
|
||||
|
||||
// confirmBillType({id:it.id,type:state.bill_type}).then(() => {
|
||||
// setState({loading:false})
|
||||
// setItem({...it,confirm_status:'CONFIRMED'})
|
||||
// }).catch(() => {
|
||||
// setState({loading:false})
|
||||
// })
|
||||
}
|
||||
return <div
|
||||
className="confirm-item align-center space-between"
|
||||
style={{marginBottom: 15}}>
|
||||
<div>
|
||||
<div>{t(props.type == 'student_number' ? 'bill.confirm_student_number' : 'bill.confirm_bill_number')}</div>
|
||||
</div>
|
||||
<div className="confirm-item-btn">
|
||||
<Space spacing={15}>
|
||||
{!state.confirmed && <Input
|
||||
onChange={v => setState({confirmNumber: String(v)})}
|
||||
defaultValue={state.confirmNumber}
|
||||
style={{width: 180}} placeholder={t('base.please_enter')}/>}
|
||||
{
|
||||
state.confirmed ? <Space>
|
||||
<div>{state.confirmNumber}</div>
|
||||
<Tag size={'large'} color='light-blue'>CONFIRMED</Tag>
|
||||
</Space> : <Button
|
||||
style={{width: 80}} disabled={!state.confirmNumber} onClick={onConfirm}
|
||||
loading={state.loading} theme={'solid'}>{t('base.confirm')}</Button>
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
}
|
@ -13,6 +13,9 @@ 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";
|
||||
import {IconStudentId} from "@/components/icons";
|
||||
import {BillDetailItem} from "@/components/bill/bill-detail-items.tsx";
|
||||
import {NumberConfirm} from "@/pages/bill/components/number_confirm.tsx";
|
||||
|
||||
|
||||
const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => {
|
||||
@ -86,27 +89,31 @@ const BillQuery = () => {
|
||||
}
|
||||
</div>)
|
||||
}
|
||||
const onExportExcel = ()=>{
|
||||
const onExportExcel = () => {
|
||||
// const downloadUrl = `${AppConfig.API_PREFIX || '/api'}/bills/export?${stringify(queryParams)}`
|
||||
//
|
||||
//
|
||||
// saveAs(downloadUrl, 'bill-result-excel.xlsx')
|
||||
setState({
|
||||
exporting:true
|
||||
exporting: true
|
||||
})
|
||||
exportBillList(queryParams).then(ret=>{
|
||||
exportBillList(queryParams).then(ret => {
|
||||
console.log(ret)
|
||||
const blob = new Blob([ret], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
|
||||
const blob = new Blob([ret], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
|
||||
saveAs(blob, 'bill-result-excel.xlsx')
|
||||
}).finally(()=>{
|
||||
}).finally(() => {
|
||||
setState({
|
||||
exporting:false
|
||||
exporting: false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const onImportExcel = ()=>{
|
||||
Toast.warning({content:'Not implemented'})
|
||||
const onImportExcel = () => {
|
||||
Toast.warning({content: 'Not implemented'})
|
||||
}
|
||||
const [selectKeys, setSelectedKeys] = useState<(string | number)[]>([])
|
||||
const confirmBillTypeBatch = () => {
|
||||
console.log(selectKeys)
|
||||
}
|
||||
|
||||
return (<div>
|
||||
@ -114,10 +121,22 @@ const BillQuery = () => {
|
||||
<BillList
|
||||
type={'query'} loading={loading} source={data}
|
||||
operationRender={operation} operationRenderWidth={180}
|
||||
beforeTotalAmount={<ButtonGroup style={{marginRight:20}} theme={'solid'}>
|
||||
<Button onClick={onImportExcel}>{t('bill.import_excel')}</Button>
|
||||
<Button loading={state.exporting} onClick={onExportExcel}>{t('bill.export_excel')}</Button>
|
||||
</ButtonGroup>}
|
||||
beforeTotalAmount={<ButtonGroup style={{marginRight: 20}} theme={'solid'}>
|
||||
{
|
||||
(selectKeys.length == 0) ? <Button disabled>{t('bill.confirm_bill_type_batch')}</Button> :
|
||||
<Popconfirm
|
||||
title={'Notice'}
|
||||
content={`${t('bill.cancel_confirm_bills')}?`}
|
||||
onConfirm={() => confirmBillTypeBatch()}
|
||||
>
|
||||
<Button theme={'solid'}>{t('bill.confirm_bill_type_batch')}</Button>
|
||||
</Popconfirm>
|
||||
}
|
||||
<Button onClick={onImportExcel}>{t('bill.import_excel')}</Button>
|
||||
<Button loading={state.exporting} onClick={onExportExcel}>{t('bill.export_excel')}</Button>
|
||||
</ButtonGroup>}
|
||||
|
||||
onRowSelection={setSelectedKeys}
|
||||
onPageChange={(page_number) => {
|
||||
setBillQueryParams({
|
||||
...queryParams,
|
||||
@ -146,19 +165,34 @@ const BillQuery = () => {
|
||||
}}
|
||||
/>
|
||||
<Modal
|
||||
title="Confirm Bill Type"
|
||||
title={t('bill.confirm_bill')}
|
||||
visible={!!state.confirmBill}
|
||||
closeOnEsc={true}
|
||||
onCancel={() => {
|
||||
refresh()
|
||||
setState({confirmBill: undefined})
|
||||
}}
|
||||
width={500}
|
||||
width={550}
|
||||
footer={null}
|
||||
>
|
||||
{state.confirmBill && <>
|
||||
<div><BillDetailItems bill={state.confirmBill}/></div>
|
||||
<div className="confirm-container" style={{padding: '15px 0'}}>
|
||||
<div><BillDetailItems bill={state.confirmBill} studentNumberRender={<>
|
||||
<BillDetailItem
|
||||
icon={<IconStudentId/>} title={t('manual.student_number')}
|
||||
value={state.confirmBill.student_number || '-'}/>
|
||||
<BillDetailItem
|
||||
icon={<IconStudentId/>} title={t('base.bill_number')}
|
||||
value={state.confirmBill.application_number || '-'}/>
|
||||
</>}/></div>
|
||||
<div className="confirm-container" style={{padding: '15px 0',marginTop:20}}>
|
||||
{
|
||||
!state.confirmBill.student_number_confirm &&
|
||||
<NumberConfirm bill={state.confirmBill} type={'student_number'}/>
|
||||
}
|
||||
{
|
||||
!state.confirmBill.application_number_confirm &&
|
||||
<NumberConfirm bill={state.confirmBill} type={'application_number'}/>
|
||||
}
|
||||
{
|
||||
state.confirmBill.details.map((it, idx) => (<div key={idx}>
|
||||
<Divider margin='12px'/>
|
||||
|
@ -86,6 +86,20 @@ const BillReconciliation = () => {
|
||||
<BillList
|
||||
source={data} type={'reconciliation'}
|
||||
operationRender={queryParams.apply_status == 'CHECKED' ? undefined : operation}
|
||||
beforeTotalAmount={<div>{queryParams.apply_status != 'CHECKED' && (
|
||||
(selectKeys.length == 0 )? <Button theme={'solid'} disabled style={{marginRight: 10}}>
|
||||
{t('bill.confirm_batch')}
|
||||
</Button> :
|
||||
<Popconfirm
|
||||
title={'Notice'}
|
||||
content={`${t('bill.cancel_confirm_bills')}?`}
|
||||
onConfirm={() => confirmBill(selectKeys as number[])}
|
||||
>
|
||||
<Button theme={'solid'} style={{marginRight: 10}}>
|
||||
{t('bill.confirm_batch')}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)}</div>}
|
||||
onRowSelection={queryParams.apply_status == 'CHECKED' ? undefined : (keys: (number | string)[]) => {
|
||||
setSelectedKeys(keys);
|
||||
}}
|
||||
@ -94,17 +108,6 @@ const BillReconciliation = () => {
|
||||
...queryParams,
|
||||
page_number
|
||||
})}
|
||||
tableFooter={queryParams.apply_status != 'CHECKED' && selectKeys?.length > 0 && (
|
||||
<Popconfirm
|
||||
title={'Notice'}
|
||||
content={`${t('bill.cancel_confirm_bills')}?`}
|
||||
onConfirm={() => confirmBill(selectKeys as number[])}
|
||||
>
|
||||
<Button style={{marginRight: 10}}>
|
||||
{t('bill.confirm_batch')}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)}
|
||||
/>
|
||||
</div>)
|
||||
}
|
||||
|
4
src/types/bill.d.ts
vendored
4
src/types/bill.d.ts
vendored
@ -44,7 +44,9 @@ declare type BillQueryParam = {
|
||||
declare type BillModel = {
|
||||
id: number;
|
||||
student_number: string;
|
||||
application_number?: null | string | number;
|
||||
student_number_confirm?: string;
|
||||
application_number: string;
|
||||
application_number_confirm?: null | string;
|
||||
student_email: string;
|
||||
student_tc_name?: string;
|
||||
student_sc_name?: string;
|
||||
|
@ -28,8 +28,8 @@ export default defineConfig(({mode}) => {
|
||||
port:10086,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://test-payment-be.hkchc.team', //
|
||||
// target: 'http://127.0.0.1:50000', //
|
||||
// target: 'https://test-payment-be.hkchc.team', //
|
||||
target: 'http://127.0.0.1:50000', //
|
||||
changeOrigin: true,
|
||||
//rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user