diff --git a/src/assets/index.less b/src/assets/index.less index eb18a57..0470733 100644 --- a/src/assets/index.less +++ b/src/assets/index.less @@ -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 { diff --git a/src/components/bill/bill-detail-items.tsx b/src/components/bill/bill-detail-items.tsx index e9e2a49..d710f13 100644 --- a/src/components/bill/bill-detail-items.tsx +++ b/src/components/bill/bill-detail-items.tsx @@ -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
{item.icon} {item.title} :
{item.value}
} -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 (<> } title={t('manual.bill_type')} value={billType}/> - } title={t('manual.student_number')} value={prop.bill.student_number || prop.bill.application_number}/> - } title={t('bill.title_student_name')} + {prop.studentNumberRender?prop.studentNumberRender:} title={t('manual.student_number')} value={prop.bill.student_number || prop.bill.application_number || '-'}/>} + } title={t('bill.title_student_name')} value={`${prop.bill.student_english_name||'-'}${prop.bill.student_chinese_name?' / '+ prop.bill.student_chinese_name : ''}`}/> } title={'Email'} value={prop.bill.student_email||'-'}/> } title={t('manual.amount')} value={}/> diff --git a/src/components/bill/list.tsx b/src/components/bill/list.tsx index 700cc20..5d9bcbe 100644 --- a/src/components/bill/list.tsx +++ b/src/components/bill/list.tsx @@ -32,7 +32,10 @@ export const BillList: React.FC = (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,6 +59,12 @@ export const BillList: React.FC = (props) => { dataIndex: 'id', width: 120, }, + { + title: 'Merchant Ref', + dataIndex: 'merchant_ref', + width: 200, + // render: (_) => (), + }, { title: t('base.student_number'), dataIndex: 'student_number', @@ -68,33 +77,33 @@ export const BillList: React.FC = (props) => { width: 150, }, { - 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) => (
{record.student_english_name}
{record.student_chinese_name}
) }, { @@ -118,7 +127,7 @@ export const BillList: React.FC = (props) => { title: t('bill.title_year'), dataIndex: 'intake_year', width: 120, - render: (_, record) => (
{record.intake_year}/{String(record.intake_semester).length == 1 ? '0':''}{record.intake_semester}
) + render: (_, record) => record.intake_year?(
{record.intake_year}/{String(record.intake_semester).length == 1 ? '0':''}{record.intake_semester}
):"N/A" }, // { // title: t('bill.title_semester'), @@ -187,12 +196,6 @@ export const BillList: React.FC = (props) => { ):'N/A'), }, - { - title: 'Merchant Ref', - dataIndex: 'merchant_ref', - width: 250, - // render: (_) => (), - }, { title: t('bill.title_bill_status'), dataIndex: 'status', diff --git a/src/components/bill/search-form.tsx b/src/components/bill/search-form.tsx index e544b6d..9f7368f 100644 --- a/src/components/bill/search-form.tsx +++ b/src/components/bill/search-form.tsx @@ -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 = (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 = (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 = (props) => {
onSubmit={formSubmit}> - - - - - - - + + + + + + + @@ -126,7 +131,7 @@ const SearchForm: React.FC = (props) => { {t('bill.title_create_at')} {t('bill.sort_asc')} - + FLYWIRE @@ -134,23 +139,34 @@ const SearchForm: React.FC = (props) => { PPS - - - FLYWIRE - CBP - PPS + + + { + BillTypes.map((it, idx) => ( + {it.label})) + } {props.showApply && <> - + + + {t('bill.status_confirmed')} + {t('bill.status_unconfirmed')} + + + {billStatusOptions.map((item, index) => ( {item.label}))} - + {applyStatusOptions.map((item, index) => ( @@ -158,7 +174,7 @@ const SearchForm: React.FC = (props) => { } - + diff --git a/src/components/icons/index.tsx b/src/components/icons/index.tsx index 69a98e0..2a9f93b 100644 --- a/src/components/icons/index.tsx +++ b/src/components/icons/index.tsx @@ -60,6 +60,22 @@ export const IconMoney = ({style}: IconProps) => { } export const IconStudentId = ({style}: IconProps) => { + return ( + + + + + ) +} + +export const IconStudentName = ({style}: IconProps) => { return ( @@ -88,6 +104,7 @@ export const IconStudentId = ({style}: IconProps) => { ) } + export const IconStudentEmail = ({style}: IconProps) => { return ( = (props) => { }) } return
-
+ style={{marginBottom: 20}}> +
{it.bill_type}
-
+
- + {it.confirm_status != 'CONFIRMED' && } { - it.confirm_status == 'CONFIRMED' ? {state.bill_type} : <> + it.confirm_status == 'CONFIRMED' ? {state.bill_type} : <> onConfirmBill()} position={'topRight'} content={`${t('bill.confirm_bill_type')}?`} - > + > } diff --git a/src/pages/bill/components/number_confirm.tsx b/src/pages/bill/components/number_confirm.tsx new file mode 100644 index 0000000..bb9ddd1 --- /dev/null +++ b/src/pages/bill/components/number_confirm.tsx @@ -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 = (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
+
+
{t(props.type == 'student_number' ? 'bill.confirm_student_number' : 'bill.confirm_bill_number')}
+
+
+ + {!state.confirmed && setState({confirmNumber: String(v)})} + defaultValue={state.confirmNumber} + style={{width: 180}} placeholder={t('base.please_enter')}/>} + { + state.confirmed ? +
{state.confirmNumber}
+ CONFIRMED +
: + } +
+
+
+} \ No newline at end of file diff --git a/src/pages/bill/query.tsx b/src/pages/bill/query.tsx index 28ff509..5ed6ea3 100644 --- a/src/pages/bill/query.tsx +++ b/src/pages/bill/query.tsx @@ -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 = () => { }
) } - 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 (
@@ -114,10 +121,22 @@ const BillQuery = () => { - - - } + beforeTotalAmount={ + { + (selectKeys.length == 0) ? : + confirmBillTypeBatch()} + > + + + } + + + } + + onRowSelection={setSelectedKeys} onPageChange={(page_number) => { setBillQueryParams({ ...queryParams, @@ -146,19 +165,34 @@ const BillQuery = () => { }} /> { refresh() setState({confirmBill: undefined}) }} - width={500} + width={550} footer={null} > {state.confirmBill && <> -
-
+
+ } title={t('manual.student_number')} + value={state.confirmBill.student_number || '-'}/> + } title={t('base.bill_number')} + value={state.confirmBill.application_number || '-'}/> + }/>
+
+ { + !state.confirmBill.student_number_confirm && + + } + { + !state.confirmBill.application_number_confirm && + + } { state.confirmBill.details.map((it, idx) => (
diff --git a/src/pages/bill/reconciliation.tsx b/src/pages/bill/reconciliation.tsx index ee0d264..b785204 100644 --- a/src/pages/bill/reconciliation.tsx +++ b/src/pages/bill/reconciliation.tsx @@ -86,6 +86,20 @@ const BillReconciliation = () => { {queryParams.apply_status != 'CHECKED' && ( + (selectKeys.length == 0 )? : + confirmBill(selectKeys as number[])} + > + + + )}
} 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 && ( - confirmBill(selectKeys as number[])} - > - - - )} />
) } diff --git a/src/types/bill.d.ts b/src/types/bill.d.ts index c24537e..929cf52 100644 --- a/src/types/bill.d.ts +++ b/src/types/bill.d.ts @@ -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: null | string; + application_number_confirm?: null | string; student_email: string; student_tc_name?: string; student_sc_name?: string; diff --git a/vite.config.ts b/vite.config.ts index 3c7d82b..83e68da 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -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/, '') }