update
This commit is contained in:
parent
9a50d1c47d
commit
71b6f776c9
@ -51,5 +51,6 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ const BillDetailItems = (prop: { bill: BillModel }) => {
|
||||
<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')}
|
||||
value={`${prop.bill.student_english_name}/${prop.bill.student_chinese_name}`}/>
|
||||
<BillDetailItem icon={<IconStudentEmail/>} title={'Email'} value={prop.bill.student_email}/>
|
||||
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}/>}/>
|
||||
</>)
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ const BillDetail:BasicComponent<BillDetailProps> = ({bill,onCancel})=>{
|
||||
</div>
|
||||
</div>
|
||||
<div className={'bill-info-detail'}>
|
||||
<div className={'bill-exp-time text-center'}> {t('manual.exp_time')} {dayjs(bill.expiration_time).format('HH:mm')} </div>
|
||||
<div className={'bill-exp-time text-center'}> {t('manual.exp_time')} {dayjs(bill.expiration_time).format('YYYY-MM-DD HH:mm')} </div>
|
||||
<BillDetailItems bill={bill} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,12 +1,13 @@
|
||||
import {Table, Typography} from "@douyinfe/semi-ui";
|
||||
import {ColumnProps} from "@douyinfe/semi-ui/lib/es/table";
|
||||
import React, {useMemo} from "react";
|
||||
import React, {useMemo, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import MoneyFormat from "@/components/money-format.tsx";
|
||||
import {Card} from "@/components/card";
|
||||
import './bill.less'
|
||||
import {BillStatus} from "@/service/types.ts";
|
||||
|
||||
type BillListProps = {
|
||||
type: 'query' | 'reconciliation';
|
||||
@ -20,6 +21,31 @@ type BillListProps = {
|
||||
|
||||
export const BillList: React.FC<BillListProps> = (props) => {
|
||||
const {t, i18n} = useTranslation()
|
||||
const [currentTotalAmount,setCurrentTotalAmount] = useState(0)
|
||||
|
||||
|
||||
const billStatusText = (billStatus: string) => {
|
||||
switch (billStatus) {
|
||||
case 'PENDING':
|
||||
return t('bill.pay_status_pending')
|
||||
case 'PAID':
|
||||
return t('bill.pay_status_paid')
|
||||
case 'CANCELLED':
|
||||
return t('bill.pay_status_canceled')
|
||||
default:
|
||||
return billStatus
|
||||
}
|
||||
}
|
||||
const applyStatusText = (status:string) => {
|
||||
switch (status) {
|
||||
case 'UNCHECKED':
|
||||
return t('bill.reconciliation_status_pending')
|
||||
case 'CHECKED':
|
||||
return t('bill.reconciliation_status_submitted')
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
const columns = useMemo<ColumnProps<BillModel>[]>(() => {
|
||||
const cols: ColumnProps<BillModel>[] = [
|
||||
@ -138,7 +164,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
title: t('bill.title_bill_status'),
|
||||
dataIndex: 'status',
|
||||
width: 150,
|
||||
// render: value => dayjs(value).format('YYYY-MM-DD'),
|
||||
render: value => billStatusText(value),
|
||||
},
|
||||
]
|
||||
if (props.type != 'reconciliation') {
|
||||
@ -146,7 +172,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
title: t('bill.title_reconciliation_status'),
|
||||
dataIndex: 'apply_status',
|
||||
width: 150,
|
||||
// render: value => dayjs(value).format('YYYY-MM-DD'),
|
||||
render: value => applyStatusText(value),
|
||||
})
|
||||
}
|
||||
if (props.operationRender) {
|
||||
@ -161,9 +187,19 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
return cols;
|
||||
}, [props.operationRender, props.type, i18n.language]);
|
||||
|
||||
const currentTotalAmount = useMemo(() => {
|
||||
// 计算当前列表总金额
|
||||
return props.source?.list.map(s => Number(s.amount)).reduce((s, c) => (s + c), 0)
|
||||
const isExpired = (bill: BillModel) => {
|
||||
return bill.status == BillStatus.PENDING && dayjs(bill.expiration_time).isBefore(Date.now())
|
||||
}
|
||||
const currentList = useMemo(()=>{
|
||||
const originList = props.source?.list || [];
|
||||
originList.forEach(s => {
|
||||
if(isExpired(s)){
|
||||
s.status = BillStatus.EXPIRED;
|
||||
}
|
||||
})
|
||||
const _total = originList.map(s=>Number(s.amount)).reduce((s, c) => (s + c), 0)
|
||||
setCurrentTotalAmount(_total)
|
||||
return originList;
|
||||
},[props.source])
|
||||
|
||||
return <Card
|
||||
@ -183,7 +219,7 @@ export const BillList: React.FC<BillListProps> = (props) => {
|
||||
<Table<BillModel>
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={props.source?.list}
|
||||
dataSource={currentList}
|
||||
rowKey={'id'}
|
||||
pagination={{
|
||||
currentPage: props.source?.pagination.current,
|
||||
|
@ -74,16 +74,16 @@ const SearchForm: React.FC<SearchFormProps> = (props) => {
|
||||
<Form<SearchFormFields> onSubmit={formSubmit}>
|
||||
<Row type={'flex'} gutter={20}>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.DatePicker type={'dateRange'} field="dateRange" label={t('bill.bill_date')}
|
||||
<Form.DatePicker showClear type={'dateRange'} field="dateRange" label={t('bill.bill_date')}
|
||||
style={{width: '100%'}}>
|
||||
</Form.DatePicker>
|
||||
</Col>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Input field='student_number' label={t('base.student_number')} trigger='blur'
|
||||
<Form.Input showClear field='student_number' label={t('base.student_number')} trigger='blur'
|
||||
placeholder={t('base.please_enter')}/>
|
||||
</Col>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
<Form.Input field='application_number' label={t('base.bill_number')} trigger='blur'
|
||||
<Form.Input showClear field='application_number' label={t('base.bill_number')} trigger='blur'
|
||||
placeholder={t('base.please_enter')}/>
|
||||
</Col>
|
||||
<Col xxl={4} xl={6} md={8}>
|
||||
|
@ -53,8 +53,6 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
|
||||
isLoggedIn: !!user,
|
||||
user:{
|
||||
...user,
|
||||
// TODO 等待接口返回
|
||||
department:'root'
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -71,7 +69,7 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
|
||||
const login = async (code: string, state: string) => {
|
||||
const user = await auth(code, state)
|
||||
// 保存token
|
||||
setAuthToken(user.token, user.exp)
|
||||
setAuthToken(user.token, user.expiration_time?(new Date(user.expiration_time)).getTime():-1);
|
||||
//
|
||||
dispatch({
|
||||
action: 'login',
|
||||
@ -79,8 +77,6 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
|
||||
isLoggedIn: true,
|
||||
user:{
|
||||
...user,
|
||||
// TODO 等待接口返回
|
||||
department:'root'
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -105,6 +101,7 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
|
||||
user: {
|
||||
id: 1,
|
||||
token: 'test-123123',
|
||||
expiration_time: '',
|
||||
email: 'test@qq.com',
|
||||
department: 'root',
|
||||
exp: 1,
|
||||
@ -125,7 +122,7 @@ export const AuthProvider = ({children}: { children: React.ReactNode }) => {
|
||||
user:{
|
||||
...state.user,
|
||||
...user
|
||||
} as any,
|
||||
} as never,
|
||||
|
||||
}
|
||||
})
|
||||
|
@ -4,11 +4,14 @@ import {useSetState} from "ahooks";
|
||||
import {useEffect} from "react";
|
||||
import {createExternalBill} from "@/service/api/bill.ts";
|
||||
import {IconLoading} from "@/components/icons";
|
||||
import {FailIcon} from "@/assets/images/pay/fail.tsx";
|
||||
import {PayLogo} from "@/pages/pay/component";
|
||||
|
||||
// 获取必填参数
|
||||
const RequiredParams = [
|
||||
// 'application_number','student_number', 可以不设置
|
||||
'source', 'amount',
|
||||
'source',
|
||||
'amount',
|
||||
'program_code',
|
||||
'intake_year',
|
||||
'intake_semester'
|
||||
@ -36,39 +39,41 @@ const ExternalCreate = () => {
|
||||
setState({loading: false})
|
||||
navigate(`/pay?bill=${ret.id}`, {replace: true})
|
||||
}).catch(() => {
|
||||
setState({loading: false, 'error': 'create pay order error'})
|
||||
setState({loading: false, 'error': 'create pay order failed'})
|
||||
})
|
||||
}
|
||||
useEffect(() => {
|
||||
if (searchParams) {
|
||||
const paramsContent = searchParams.get('params');
|
||||
if (!paramsContent) {
|
||||
return setState({error: 'params error'})
|
||||
return setState({error: 'params error',loading: false})
|
||||
}
|
||||
|
||||
const params: ExternalCreateParamsType = JSON.parse(paramsContent);
|
||||
for (let i = 0; i < RequiredParams.length; i++) {
|
||||
const key = RequiredParams[i];
|
||||
if (!params[key]) {
|
||||
return setState({error: 'params error: require ' + key})
|
||||
return setState({error: 'params error: require ' + key,loading: false})
|
||||
}
|
||||
params[key] = searchParams.get(key)
|
||||
}
|
||||
if (!params.application_number && !params.student_number) {
|
||||
return setState({error: 'params error: require application_number or student_number'})
|
||||
return setState({error: 'params error: require application_number or student_number',loading: false})
|
||||
}
|
||||
if (!params.details || params.details.length == 0) {
|
||||
return setState({error: 'params error: require detail'})
|
||||
return setState({error: 'params error: require detail',loading: false})
|
||||
}
|
||||
createBill(params)
|
||||
return;
|
||||
}
|
||||
}, [searchParams])
|
||||
return (<div className={`${styles.container} text-center`}>
|
||||
<PayLogo/>
|
||||
{state.loading && <div>
|
||||
<div><IconLoading size={70} /></div>
|
||||
<div></div>
|
||||
</div>}
|
||||
{state.error && <div>
|
||||
<div style={{fontSize:100,color:'rgb(240, 86, 114)',margin:'30px 0 10px'}}><FailIcon /></div>
|
||||
<h3>{state.error}</h3>
|
||||
</div>}
|
||||
</div>)
|
||||
|
@ -91,7 +91,7 @@ const PayIndex = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.payConfirm}>
|
||||
<div className="student-email">Your Email: {bill.student_email}</div>
|
||||
{bill.student_email && <div className="student-email">Your Email: {bill.student_email}</div> }
|
||||
<div className="pay-submit">
|
||||
{ payChannel == 'asia_pay' && <StartAsiaPay bill={bill} />}
|
||||
<StartFlyWire bill={bill} open={payChannel == 'flywire'} />
|
||||
|
@ -16,6 +16,8 @@ export class BizError extends Error {
|
||||
|
||||
export enum BillStatus {
|
||||
PENDING= 'PENDING',
|
||||
// 已过期
|
||||
EXPIRED = 'EXPIRED',
|
||||
PAID = 'PAID',
|
||||
CANCELED = 'CANCELED',
|
||||
}
|
||||
|
1
src/types/auth.d.ts
vendored
1
src/types/auth.d.ts
vendored
@ -5,6 +5,7 @@ declare type UserProfile = {
|
||||
username: string;
|
||||
department: string;
|
||||
exp: number;
|
||||
expiration_time: string;
|
||||
iat: number;
|
||||
iss: string;
|
||||
nbf: number;
|
||||
|
@ -14,7 +14,7 @@ export default defineConfig(({mode}) => {
|
||||
API_PREFIX: process.env.APP_API_PREFIX || '/api',
|
||||
FIY_WIRE_GATEWAY: process.env.FIY_WIRE_GATEWAY || 'https://gateway.flywire.com/v1/transfers',
|
||||
SSO_AUTH_URL: process.env.SSO_AUTH_URL || 'https://portal.chuhai.edu.hk',
|
||||
SSO_AUTH_CLIENT_KEY: process.env.AUTH_CLIENT_KEY || 'test_client_id',
|
||||
SSO_AUTH_CLIENT_KEY: process.env.AUTH_CLIENT_KEY || 'payment',
|
||||
AUTH_TOKEN_KEY: process.env.AUTH_TOKEN_KEY || 'payment-auth-token',
|
||||
}),
|
||||
AppMode: JSON.stringify(mode)
|
||||
@ -28,7 +28,7 @@ export default defineConfig(({mode}) => {
|
||||
port:10086,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://43.136.175.109', //
|
||||
target: 'https://test-payment-be.hkchc.team', //
|
||||
changeOrigin: true,
|
||||
//rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user