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