diff --git a/package.json b/package.json index 300942d..6f2f93b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@emotion/styled": "^11.11.5", "@mui/system": "^5.15.15", "ahooks": "^3.7.11", + "axios": "^1.7.2", "clsx": "^2.1.1", "dayjs": "^1.11.11", "file-saver": "^2.0.5", @@ -27,6 +28,7 @@ "jspdf-autotable": "^3.8.2", "less": "^4.2.0", "qrcode.react": "^3.1.0", + "qs": "^6.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^14.1.1", @@ -36,6 +38,7 @@ "@types/file-saver": "^2.0.7", "@types/lodash": "^4.17.1", "@types/node": "^20.12.11", + "@types/qs": "^6.9.15", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@typescript-eslint/eslint-plugin": "^7.2.0", diff --git a/src/components/bill/index.ts b/src/components/bill/index.ts new file mode 100644 index 0000000..3711f02 --- /dev/null +++ b/src/components/bill/index.ts @@ -0,0 +1,7 @@ +import BillDetail from './detail.tsx' +import { + BillList +} from './list.tsx' +import useBillQRCode from './qr-code.tsx' + +export {BillDetail, BillList, useBillQRCode} \ No newline at end of file diff --git a/src/components/bill/list.tsx b/src/components/bill/list.tsx index 4ae227f..a3430b4 100644 --- a/src/components/bill/list.tsx +++ b/src/components/bill/list.tsx @@ -2,16 +2,17 @@ import {Table, Typography} from "@douyinfe/semi-ui"; import {ColumnProps} from "@douyinfe/semi-ui/lib/es/table"; import React, {useMemo} 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 dayjs from "dayjs"; type BillListProps = { type: 'query' | 'reconciliation'; operationRender?: (record: BillModel) => React.ReactNode; onRowSelection?: (selectedRowKeys?: (string | number)[]) => void; - source: RecordList; + source?: RecordList; onPageChange: () => void; loading?: boolean; } @@ -24,38 +25,53 @@ export const BillList: React.FC = (props) => { { title: '#', dataIndex: 'id', - width: 80, + width: 120, fixed: true, }, + { + title: t('base.student_number'), + dataIndex: 'student_number', + width: 150, + fixed: true, + render: (value) => value ?? 'N/A' + }, + { + title: t('base.bill_number'), + dataIndex: 'application_number', + fixed: true, + width: 150, + }, { title: t('bill.title_paid_at'), dataIndex: 'paid_at', - render: (value) => dayjs(value).format('YYYY-MM-DD'), + width: 150, + render: (value) => value ? dayjs(value).format('YYYY-MM-DD') : 'N/A', }, { - title: t('bill.title_program_id'), - dataIndex: 'program_id', + title: t('bill.title_student_name'), + dataIndex: 'student_english_name', + width: 150, + render: (_, record) => (
{record.student_english_name}
{record.student_chinese_name}
) + }, + { + title: t('bill.title_program_name'), + width: 250, + dataIndex: i18n.language == 'en-US' ? 'programme_english_name' : 'programme_chinese_name', }, { title: t('bill.title_department'), - dataIndex: 'department', + width: 200, + dataIndex: i18n.language == 'en-US' ? 'department_english_name' : 'department_chinese_name', }, { title: t('bill.title_year'), - dataIndex: 'student_year', + dataIndex: 'intake_year', + width: 120, }, { title: t('bill.title_semester'), - dataIndex: 'student_semester', - }, - { - title: t('bill.title_student_name_en'), - dataIndex: 'student_en_name', - }, - { - title: t('bill.title_student_name_sc'), - dataIndex: 'student_sc_name', - render: (_, record) => (`${record.student_tc_name}(${record.student_sc_name})`) + dataIndex: 'intake_semester', + width: 120, }, { title: t('bill.title_bill_detail'), @@ -63,21 +79,23 @@ export const BillList: React.FC = (props) => { ellipsis: {showTitle: true}, width: 220, render: (_, record) => (
- {record.detail.map((it,idx) => (
{it.type}:
))} + {record.detail?.map((it, idx) => (
{it.type}:
))}
), }, { title: t('bill.title_amount'), dataIndex: 'amount', + width: 150, render: (_) => (), }, { title: t('bill.title_pay_amount'), dataIndex: 'pay_amount', + width: 150, render: (_, record) => { - if (record.service_charge > 0) { + if (record.service_charge && record.service_charge > 0) { return
-
+
{t('bill.title_service_charge')}: @@ -89,21 +107,24 @@ export const BillList: React.FC = (props) => { { title: t('bill.title_actual_payment_amount'), dataIndex: 'actual_payment_amount', + width: 120, render: (_) => (), }, { title: t('bill.title_pay_method'), dataIndex: 'pay_method', - render: (_, {pay_method, payment_channel}) => (
+ width: 120, + render: (_, {payment_method, payment_channel}) => (
{payment_channel}
- ({pay_method}) + ({payment_method})
), }, { title: t('bill.title_bill_status'), dataIndex: 'bill_status', + width: 150, // render: value => dayjs(value).format('YYYY-MM-DD'), }, ] @@ -111,6 +132,7 @@ export const BillList: React.FC = (props) => { cols.push({ title: t('bill.title_reconciliation_status'), dataIndex: 'apply_status', + width: 150, // render: value => dayjs(value).format('YYYY-MM-DD'), }) } @@ -126,36 +148,34 @@ export const BillList: React.FC = (props) => { return cols; }, [props.operationRender, props.type, i18n.language]); - const currentTotalAmount = useMemo(()=>{ + const currentTotalAmount = useMemo(() => { // 计算当前列表总金额 - return props.source.list.map(s=>s.amount).reduce((s,c)=>(s+c),0) - },[props.source]) + return props.source?.list.map(s => Number(s.amount)).reduce((s, c) => (s + c), 0) + }, [props.source]) return
- 查询总金额: - + {t('bill.query_amount_total')} : +
- 当前页总金额: + {t('bill.query_amount_current_page')} :
} >
- sticky={{top: 60}} bordered - scroll={{x: 600,y:300,scrollToFirstRowOnChange:true}} columns={columns} - dataSource={props.source.list} + dataSource={props.source?.list} rowKey={'id'} pagination={{ - currentPage: props.source.pagination.current, - pageSize: props.source.pagination.pageSize, - total: props.source.pagination.total, + currentPage: props.source?.pagination.current, + pageSize: props.source?.pagination.pageSize, + total: props.source?.pagination.total, onPageChange: props.onPageChange, }} loading={props.loading} diff --git a/src/components/bill/qr-code.tsx b/src/components/bill/qr-code.tsx new file mode 100644 index 0000000..59b6ecf --- /dev/null +++ b/src/components/bill/qr-code.tsx @@ -0,0 +1,28 @@ +import React, {CSSProperties, useRef} from "react"; +import QRCodeSvg from "qrcode.react"; +import {saveAs} from "file-saver" + +export type BillQrcodeProps = { + size?: number; + bill?: BillModel; + className?: string; + style?: CSSProperties; +} +const useBillQRCode = () => { + const qrCodeRef = useRef(null) + const QRCode: React.FC = (props) => ( +
+ +
); + const exportQRCode = () => { + const canvas = qrCodeRef.current?.querySelector('canvas'); + if (!canvas) return; + const billId = qrCodeRef.current?.getAttribute('data-bill-id') || Date.now().toString(); + saveAs(canvas.toDataURL(), `qrcode-${billId}.png`) + } + return { + QRCode, + exportQRCode + } +}; +export default useBillQRCode diff --git a/src/components/bill/search-form.tsx b/src/components/bill/search-form.tsx index d9b5a93..1c189e3 100644 --- a/src/components/bill/search-form.tsx +++ b/src/components/bill/search-form.tsx @@ -1,55 +1,108 @@ import {Button, Col, Form, Row} from "@douyinfe/semi-ui"; -import React from "react"; +import React, {useMemo} from "react"; import {Card} from "@/components/card"; +import {useTranslation} from "react-i18next"; +import {BillQueryParams} from "@/service/api/bill.ts"; +import dayjs from "dayjs"; type SearchFormProps = { - onSearch?: () => void; + onSearch?: (params: BillQueryParams) => void; showApply?: boolean; + loading?: boolean; searchHeader?: React.ReactNode; searchFooter?: React.ReactNode; } - +type SearchFormFields = { + dateRange?: Date[]; + student_number?: string; + bill_number?: string; + pay_method?: string; + bill_status?: string; + apply_status?: string; +} const SearchForm: React.FC = (props) => { - + const formSubmit = (value: SearchFormFields) => { + const params: BillQueryParams = {} + if (value.dateRange) { + params.start_date = dayjs(value.dateRange[0]).format('YYYY-MM-DD'); + params.end_date = dayjs(value.dateRange[1]).format('YYYY-MM-DD'); + } + if (value.student_number) { + params.student_name = value.student_number; + } + // 对账状态 + if (value.apply_status) { + params.apply_status = value.apply_status; + } + // 账单状态 + if (value.bill_status) { + params.status = value.bill_status; + } + // 支付方式 + if (value.pay_method) { + params.payment_method = value.pay_method; + } + props.onSearch?.(params); + } + const {t, i18n} = useTranslation(); + // 根据语言变化更新订单状态options + const billStatusOptions = useMemo(() => { + return [ + {value: `${i18n.language}pending`, label: t('bill.pay_status_pending')}, + {value: 'paid', label: t('bill.pay_status_paid')}, + {value: 'canceled', label: t('bill.pay_status_canceled')} + ] + }, [i18n.language]) + // 根据语言变化更新对账状态options + const applyStatusOptions = useMemo(() => { + return [ + {value: 'pending', label: t('bill.reconciliation_status_pending')}, + {value: 'submitted', label: t('bill.reconciliation_status_submitted')} + ] + }, [i18n.language]) return ( {props.searchHeader}
-
+ onSubmit={formSubmit}> - + - + - + - + AsiaPay FlyWire PPS - - 未支付 - 已支付 - 已作废 + + {billStatusOptions.map((item,index) => ( + {item.label}))} {props.showApply && - - 未对账 - 已对账 + + {applyStatusOptions.map((item,index) => ( + {item.label}))} } - - + + diff --git a/src/components/money-format.tsx b/src/components/money-format.tsx index b7d72db..40761d2 100644 --- a/src/components/money-format.tsx +++ b/src/components/money-format.tsx @@ -1,8 +1,8 @@ import React from "react"; type MoneyFormatProps = { - money: number | string; - currency?: 'USD' | 'HKD' | 'RMB'; + money?: number | string | null; + currency?: 'US$' | 'HK$' | 'RMB'; } function formatCurrency(amount: string | number) { @@ -22,8 +22,8 @@ function formatCurrency(amount: string | number) { return (isNegative ? '-' : '') + formattedIntPart + '.' + decimalPart; } -const MoneyFormat: React.FC = ({money, currency = 'HKD'}) => { +const MoneyFormat: React.FC = ({money, currency = 'HK$'}) => { // 将货币数字转换为千分位格式且带2位小数 - return {currency} {formatCurrency(money)} + return money?{currency} {formatCurrency(money)}:null } export default MoneyFormat; \ No newline at end of file diff --git a/src/i18n/translations/en.json b/src/i18n/translations/en.json index 1844617..fa440e8 100644 --- a/src/i18n/translations/en.json +++ b/src/i18n/translations/en.json @@ -1,7 +1,23 @@ { + "base": { + "bill_number": "Bill Number", + "btn_search_submit": "Search", + "please_enter": "Please Enter", + "please_select": "Please Select", + "student_number": "Student Number" + }, "bill": { "bill_number": "Bill Number", "download_receipt": "Download receipt", + "pay_status": "Bill Status", + "pay_status_canceled": "Canceled", + "pay_status_paid": "Paid", + "pay_status_pending": "Pending", + "query_amount_current_page": "The total amount of current page", + "query_amount_total": "The total Amount", + "reconciliation_status_pending": "Pending", + "reconciliation_status_submitted": "Submitted", + "require_student_number": "Search Student Number", "title_actual_payment_amount": "Actually Paid", "title_amount": "Amount", "title_bill_detail": "Bill Detail", @@ -12,11 +28,12 @@ "title_pay_amount": "Pay Amount", "title_pay_method": "Pay Method", "title_program_id": "Program ID", + "title_program_name": "Program", "title_reconciliation_status": "Reconciliation", "title_semester": "Semester", "title_service_charge": "Service Charge", + "title_student_name": "Student Name", "title_student_name_en": "English Name", - "title_student_name_sc": "Chinese Name", "title_year": "Year" }, "error": { @@ -41,9 +58,10 @@ "bill_type": "Bill Type", "bill_type_required": "please select bill type", "btn_generate": "Generate Bill", - "exp_time": "bill will expires at", + "exp_time": "The bill will expires at", "student_number": "Student Number", - "student_number_required": "required student number" + "student_number_required": "required student number", + "success": "Create bill success" }, "pay": { "amount": "Amount", diff --git a/src/i18n/translations/sc.json b/src/i18n/translations/sc.json index a7d4797..4b42b42 100644 --- a/src/i18n/translations/sc.json +++ b/src/i18n/translations/sc.json @@ -1,7 +1,23 @@ { + "base": { + "bill_number": "账单编号", + "btn_search_submit": "搜索", + "please_enter": "请输入", + "please_select": "请选择", + "student_number": "学号" + }, "bill": { "bill_number": "账单编号", "download_receipt": "下载收据", + "pay_status": "账单状态", + "pay_status_canceled": "已作废", + "pay_status_paid": "已支付", + "pay_status_pending": "未支付", + "query_amount_current_page": "当前页总金额", + "query_amount_total": "查询总金额", + "reconciliation_status_pending": "未对账", + "reconciliation_status_submitted": "已对账", + "require_student_number": "请输入查询学号", "title_actual_payment_amount": "实付金额", "title_amount": "账单金额", "title_bill_detail": "账单详情", @@ -12,11 +28,12 @@ "title_pay_amount": "应付金额", "title_pay_method": "支付方式", "title_program_id": "专业ID", + "title_program_name": "就读专业", "title_reconciliation_status": "对账状态", "title_semester": "学期", "title_service_charge": "手续费", + "title_student_name": "学生姓名", "title_student_name_en": "英文名称", - "title_student_name_sc": "中文名字", "title_year": "学年" }, "error": { @@ -43,7 +60,8 @@ "btn_generate": "生成账单", "exp_time": "账单过期时间", "student_number": "学号", - "student_number_required": "请填写学号" + "student_number_required": "请填写学号", + "success": "创建订单成功" }, "pay": { "amount": "应付金额", diff --git a/src/i18n/translations/tc.json b/src/i18n/translations/tc.json index e75ef2d..b598191 100644 --- a/src/i18n/translations/tc.json +++ b/src/i18n/translations/tc.json @@ -1,7 +1,23 @@ { + "base": { + "bill_number": "账单编号", + "btn_search_submit": "搜索", + "please_enter": "请输入", + "please_select": "请选择", + "student_number": "学号" + }, "bill": { "bill_number": "账单编号", "download_receipt": "下载收据", + "pay_status": "账单状态", + "pay_status_canceled": "已作废", + "pay_status_paid": "已支付", + "pay_status_pending": "未支付", + "query_amount_current_page": "当前页总金额", + "query_amount_total": "查询总金额", + "reconciliation_status_pending": "未对账", + "reconciliation_status_submitted": "已对账", + "require_student_number": "请输入查询学号", "title_actual_payment_amount": "實付金額", "title_amount": "帳單金額", "title_bill_detail": "帳單詳情", @@ -12,11 +28,12 @@ "title_pay_amount": "應付金額", "title_pay_method": "付款方式", "title_program_id": "專業ID", + "title_program_name": "就读专业专业", "title_reconciliation_status": "對帳狀態", "title_semester": "學期", "title_service_charge": "手續費", + "title_student_name": "学生姓名", "title_student_name_en": "英文名稱", - "title_student_name_sc": "中文名字", "title_year": "學年" }, "error": { @@ -43,7 +60,8 @@ "btn_generate": "產生帳單", "exp_time": "帳單過期時間", "student_number": "學號", - "student_number_required": "請填入學號" + "student_number_required": "請填入學號", + "success": "创建订单成功" }, "pay": { "amount": "应付金额", diff --git a/src/pages/bill/query.tsx b/src/pages/bill/query.tsx index 6626638..840f66c 100644 --- a/src/pages/bill/query.tsx +++ b/src/pages/bill/query.tsx @@ -2,75 +2,46 @@ import {BillList} from "@/components/bill/list.tsx"; import {Button, Modal, Space} from "@douyinfe/semi-ui"; import {GeneratePdf} from "@/service/generate-pdf.ts"; import {useState} from "react"; -import {clone} from "lodash"; -import dayjs from "dayjs"; +// import dayjs from "dayjs"; import SearchForm from "@/components/bill/search-form.tsx"; import BillDetail from "@/components/bill/detail.tsx"; +import {useRequest} from "ahooks"; +import {billList, BillQueryParams} from "@/service/api/bill.ts"; - -const mockBill: BillModel = { - actual_payment_amount: 110, - amount: 110, - application_no: "123123", - apply_status: "pending", - bill_status: "pending", - created_at: new Date(), - currency: "HKD", - department: "经管学院", - expiration_time: dayjs().add(30, "minute").toDate(), - id: 1123123, - paid_area: "HongKong,China", - paid_at: new Date(), - pay_amount: 110, - pay_method: "WechatHk", - payment_channel: "AsiaPay", - program_id: 123123, - service_charge: 0, - student_en_name: "SanF Chung", - student_no: "123123", - student_sc_name: "张三丰", - student_semester: "1", - student_tc_name: "张三丰", - student_year: "2024", - updated_at: "", - detail: [ - {amount: 55, bill_id: 1123123, id: 1, type: "APPLICATION FEE"}, - {amount: 50, bill_id: 1123123, id: 1, type: "TUITION FEE"}, - {amount: 5, bill_id: 1123123, id: 1, type: "VISA FEE"}, - ] -} const BillQuery = () => { - const [data, ] = useState>({ - list: Array(10).fill(mockBill).map((it, i) => { - const s = clone(it); - s.id = i; - if (i % 2 == 0) s.service_charge = 10 - return s; - }), - pagination: {current: 1, pageSize: 10, total: 250,recordTotal:213123} - }); - const [showBill,setShowBill] = useState() - const operation = (_record:BillModel)=>{ + // const [data, ] = useState>(); + const [showBill, setShowBill] = useState() + const [queryParams, setBillQueryParams] = useState({}); + const {data, loading} = useRequest(() => billList(queryParams), { + refreshDeps: [queryParams] + }) + const operation = (_record: BillModel) => { return ( - - + + ) } return (
- - + + {}} - onCancel={()=>setShowBill(undefined)} //>=1.16.0 + onOk={() => { + }} + onCancel={() => setShowBill(undefined)} //>=1.16.0 closeOnEsc={true} footerFill={true} > - {showBill && } + {showBill && }
) } diff --git a/src/pages/manual/index.tsx b/src/pages/manual/index.tsx index c92e79e..d7386c5 100644 --- a/src/pages/manual/index.tsx +++ b/src/pages/manual/index.tsx @@ -1,63 +1,105 @@ -import {Button, Form, Space} from "@douyinfe/semi-ui"; +import {Button, Form, Space, Toast} from "@douyinfe/semi-ui"; import {useTranslation} from "react-i18next"; -import {saveAs } from "file-saver" -import QRCode from "qrcode.react"; -import {useRef} from "react"; -import styles from './manual.module.less' -import {Card} from "@/components/card"; +import {ReactNode, useRef, useState} from "react"; -const BillDetailItem = (item:{title:string;value:string})=>{ +import {Card} from "@/components/card"; +import MoneyFormat from "@/components/money-format.tsx"; +import {BillTypes} from "@/service/bill-types.ts"; +import {useBillQRCode} from "@/components/bill"; + +import styles from './manual.module.less' +import {createManualBill, getBillDetail} from "@/service/api/bill.ts"; +import {useSetState} from "ahooks"; +import {BizError} from "@/service/types.ts"; +import {IconAlertTriangle} from "@douyinfe/semi-icons"; + +const BillDetailItem = (item: { title: string; value: ReactNode }) => { return
{item.title} :
{item.value}
} + + export default function Index() { const {t} = useTranslation() - const qrCodeRef = useRef(null) - const downloadQRCode = ()=>{ - const canvas = qrCodeRef.current?.querySelector('canvas'); - if(!canvas) return - saveAs(canvas.toDataURL(), 'qrcode.png') + const frm = useRef>(null) + const [billInfo, setBillInfo] = useState() + const {exportQRCode, QRCode} = useBillQRCode(); + const [state, setState] = useSetState({ + loading: false, + errorMessage: '' + }) + + const handleSubmit = (values: ManualCreateBillParam) => { + setState({loading: true}) + createManualBill(values).then((ret) => { + frm.current?.formApi.reset(); + Toast.info({ + content: t('manual.success'), + }) + getBillDetail(ret.id).then(setBillInfo); + }).catch((e: BizError) => { + setState({errorMessage: e.message}) + }).finally(() => { + setState({loading: false}) + }) + } + + const BillInfo = ({bill}: { bill?: BillModel }) => { + if (!bill) return null; + return (<> + + + }/> + + ) } return (
-
console.log(values)}> + + layout='horizontal' onValueChange={() => setState({errorMessage: ''})} + onSubmit={handleSubmit} ref={frm}> - TUITION FEE - CAUTION FEE - APPLICATION FEE + { + BillTypes.map((it, idx) => ( + {it.label})) + }
- + + + {state.errorMessage &&
+ {state.errorMessage}
} +
+
-
- - - - +
+
-
- -
+
{t('manual.exp_time')} {'12:00'}
diff --git a/src/pages/manual/manual.module.less b/src/pages/manual/manual.module.less index 6199f8e..1e380f5 100644 --- a/src/pages/manual/manual.module.less +++ b/src/pages/manual/manual.module.less @@ -40,4 +40,7 @@ background-color: #f8f9fa; padding: 25px; border: dashed 1px #ccc; +} +.formError{ + } \ No newline at end of file diff --git a/src/service/api/bill.ts b/src/service/api/bill.ts new file mode 100644 index 0000000..2b89c4d --- /dev/null +++ b/src/service/api/bill.ts @@ -0,0 +1,37 @@ +import {get, post} from "@/service/request.ts"; + +export type BillQueryResult = { + result: BillModel[]; + total_count: number; + query_total_amount: number; +} +export type BillQueryParams = Partial; +const DEFAULT_PAGE_SIZE = 10; + +function formatBillQueryResult(params: BillQueryParams, result: BillQueryResult) { + // 将 BillQueryResult 转成成 RecordList + const formatData: RecordList = { + list: result.result, + pagination: { + total: result.total_count, + pageSize: params.page_size || DEFAULT_PAGE_SIZE, + current: params.page_number || 1, + recordTotal: Number(result.query_total_amount || 0) + } + } + return formatData +} + +export async function billList(params: BillQueryParams) { + const result = await get('/bills', {params}) + return formatBillQueryResult(params, result); +} + +//现场支付创建账单接口 +export function createManualBill(params: ManualCreateBillParam) { + return post<{ id: number }>('/manual_payment', params) +} + +export function getBillDetail(id: number) { + return get('/bills/' + id) +} \ No newline at end of file diff --git a/src/service/bill-types.ts b/src/service/bill-types.ts new file mode 100644 index 0000000..722feb7 --- /dev/null +++ b/src/service/bill-types.ts @@ -0,0 +1,37 @@ +/** + * CF CAUTION FEE + * SU STUDENT UNION FEE + * TF TUITION FEE + * CP CREDIT POINT + * CB BALANCE AS AT 30 JUN 2020 + * PF APPLICATION FEE + * OF OTHER FEE + * LT LATE PAYMENT OF TUITION FEE + * LA LATE APPLICATION FEE + * DF DOCUMENT FEE + * SC STUDENT CARD + * SV STUDENT VISA + * SE SUPPLEMENTARY EXAMINATION + * SM APPEAL AGAINST ASSESSMENT RESULTS + * TN TRANSFER OF CURRICULUM + * IN ADMINISTRATIVE FEE + */ + +export const BillTypes = [ + {value:"CF",label:'CAUTION FEE'}, + {value:"SU",label:'STUDENT UNION FEE'}, + {value:"TF",label:'TUITION FEE'}, + {value:"CP",label:'CREDIT POINT'}, + {value:"CB",label:'BALANCE AS AT 30 JUN 2020'}, + {value:"PF",label:'APPLICATION FEE'}, + {value:"OF",label:'OTHER FEE'}, + {value:"LT",label:'LATE PAYMENT OF TUITION FEE'}, + {value:"LA",label:'LATE APPLICATION FEE'}, + {value:"DF",label:'DOCUMENT FEE'}, + {value:"SC",label:'STUDENT CARD'}, + {value:"SV",label:'STUDENT VISA'}, + {value:"SE",label:'SUPPLEMENTARY EXAMINATION'}, + {value:"SM",label:'APPEAL AGAINST ASSESSMENT RESULTS'}, + {value:"TN",label:'TRANSFER OF CURRICULUM'}, + {value:"IN",label:'ADMINISTRATIVE FEE'}, +] \ No newline at end of file diff --git a/src/service/request.ts b/src/service/request.ts new file mode 100644 index 0000000..3345adc --- /dev/null +++ b/src/service/request.ts @@ -0,0 +1,81 @@ +import axios from 'axios'; +import {stringify} from 'qs' +import { BizError } from './types'; + + +const JSON_FORMAT: string = 'application/json'; +export type RequestMethod = 'get' | 'post' | 'put' | 'delete' +const REQUEST_TIMEOUT = 300000; // 超时时长5min + +const Axios = axios.create({ + baseURL: "/api", + timeout:REQUEST_TIMEOUT, + headers: {'Content-Type': JSON_FORMAT} +}) + + +// 请求前拦截 +Axios.interceptors.request.use(config => { + const token = localStorage.getItem('payment_front_token'); + if (token) { + config.headers['Authorization'] = `Bearer ${token}`; + } + if (config.data && config.data instanceof FormData) { + config.headers['Content-Type'] = 'multipart/form-data'; + } + return config +}, err => { + return Promise.reject(err) +}) + +export function request(url: string, method: RequestMethod, data: AllType = null, getOriginResult = false) { + return new Promise((resolve, reject) => { + Axios.request>({ + url, + method, + data, + }).then(res => { + if (res.status != 200) { + reject(new BizError("Service Internal Exception,Please Try Later!", res.status)) + return; + } + // const + const {code, message, data} = res.data + if (code == 0) { + if (getOriginResult) { + resolve(res.data as unknown as T) + return; + } + resolve(data as unknown as T) + } else { + reject(new BizError(message, code, data as unknown as AllType)) + } + }).catch(e => { + reject(new BizError(e.message, 500)) + }) + }) +} + + +export function post(url: string, data: AllType = {}, returnOrigin = false) { + return request(url, 'post', data, returnOrigin) +} + +export function get(url: string, data: AllType = null, returnOrigin = false) { + if (data) { + url += (url.indexOf('?') === -1 ? '?' : '&') + stringify(data) + } + return request(url, 'get', data, returnOrigin) +} + +export function put(url: string, data: AllType = {}) { + return request(url, 'put', data) +} + +export function getFileBlob(url: string) { + return new Promise((resolve, reject) => { + fetch(url).then(res => res.blob()).then(res => { + resolve(res) + }).catch(reject); + }); +} diff --git a/src/service/types.ts b/src/service/types.ts new file mode 100644 index 0000000..c913660 --- /dev/null +++ b/src/service/types.ts @@ -0,0 +1,13 @@ +export class BizError extends Error { + /** + * 错误码 + */ + code = 1; + data: string | number | object | undefined | null; + + constructor(message: string, code = 1, data?: AllType) { + super(message); + this.code = code; + this.data = data; + } +} \ No newline at end of file diff --git a/src/types/api.d.ts b/src/types/api.d.ts new file mode 100644 index 0000000..3199998 --- /dev/null +++ b/src/types/api.d.ts @@ -0,0 +1,12 @@ + +declare interface APIResponse { + /** + * 错误码,0:成功,其他失败 + */ + code: number; + data?: T; + /** + * 非0情况下,提示信息 + */ + message: string; +} \ No newline at end of file diff --git a/src/types/bill.d.ts b/src/types/bill.d.ts index b74f065..7bcd348 100644 --- a/src/types/bill.d.ts +++ b/src/types/bill.d.ts @@ -1,34 +1,64 @@ + +declare type BaseDate = number | Date | string | null; + +// 现场支付账单数据 +declare type ManualCreateBillParam = { + bill_type:string; + amount:decimal; + student_number:string; +} declare type BillDetail = { id: string | number; bill_id: string | number; type: string; - amount: number; + amount: decimal; } +/** + * 账单查询参数 + */ +declare type BillQueryParam = { + page_size:int; + page_number:int; + status:string; + apply_status:string; + student_name:string; + payment_method:string; + start_date:string; + end_date:string; +} +/** + * 账单模型 + */ declare type BillModel = { - id: string | number; - student_no: string; - application_no: string; - student_sc_name: string; - student_tc_name: string; - student_en_name: string; - program_id: string | number; - department: string; - student_year: string; - student_semester: string; - amount: number; - service_charge: number; - pay_amount: number; - actual_payment_amount: number; - pay_method: string; - currency: string; - payment_channel: string; - expiration_time: string | Date | number; - bill_status: string; + id: number; + student_number: string; + application_number?: null | string | number; + student_tc_name?: string; + student_sc_name?: string; + student_chinese_name: string; + student_english_name: string; + programme_chinese_name: string; + programme_english_name: string; + department_chinese_name: string; + department_english_name: string; + intake_year: string; + intake_semester: string; + amount: number|string; + service_charge?: null | number; + payment_amount?: null | number; + actual_payment_amount?: null | number; + payment_method?: null | string | number; + currency?: null | string | number; + payment_channel?: null | string | number; + expiration_time?: BaseDate; + status: string; apply_status: string; - paid_area: string; - paid_at: string | Date | number; - created_at: string | Date | number; - updated_at: string | Date | number; + paid_area?: null | string | number; + paid_at?:BaseDate; + merchant_ref?: null | string | number; + payment_id?: null | string | number; + create_at: BaseDate; + update_at: BaseDate; - detail: BillDetail[] + detail?: BillDetail[] } \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 5ea7047..c7905e2 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,7 +1,14 @@ /// /// -declare const APP_DEFAULT_PATH:string; +declare type decimal = number; +declare type int = number; +declare type AllType = string | number | object | undefined | null; + +interface ImportMetaEnv { + // 系统部署运行地址(主要用于支付二维码生成) + readonly VITE_APP_SITE_URL: string +} declare type BasicComponentProps = { children?: React.ReactNode; diff --git a/vite.config.ts b/vite.config.ts index d911259..52c4d10 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,12 +8,22 @@ export default defineConfig(({mode}) => { plugins: [react()], base: mode == 'test' ? './' : '/', define: { - APP_DEFAULT_PATH: JSON.stringify('/') + APP_DEFAULT_PATH: JSON.stringify('/'), + APP_SITE_URL: JSON.stringify(process.env.VITE_APP_SITE_URL) }, resolve: { alias: { '@': resolve(__dirname, './src') } + }, + server:{ + proxy:{ + '/api': { + target: 'http://43.136.175.109:50000', + changeOrigin: true, + //rewrite: (path) => path.replace(/^\/api/, '') + } + } } } }) diff --git a/yarn.lock b/yarn.lock index 8208488..dcf0ec5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -853,6 +853,11 @@ resolved "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== +"@types/qs@^6.9.15": + version "6.9.15" + resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + "@types/raf@^3.4.0": version "3.4.3" resolved "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz#85f1d1d17569b28b8db45e16e996407a56b0ab04" @@ -1039,11 +1044,25 @@ async-validator@^3.5.0: resolved "https://registry.npmmirror.com/async-validator/-/async-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500" integrity sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + atob@^2.1.2: version "2.1.2" resolved "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +axios@^1.7.2: + version "1.7.2" + resolved "https://registry.npmmirror.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" + integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.npmmirror.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -1105,6 +1124,17 @@ btoa@^1.2.1: resolved "https://registry.npmmirror.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1185,6 +1215,13 @@ color-name@~1.1.4: resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + compute-scroll-into-view@^1.0.20: version "1.0.20" resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43" @@ -1283,6 +1320,20 @@ deep-is@^0.1.3: resolved "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -1321,6 +1372,18 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + esbuild@^0.20.1: version "0.20.2" resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" @@ -1554,6 +1617,20 @@ flatted@^3.2.9: resolved "https://registry.npmmirror.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1574,6 +1651,17 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1624,6 +1712,13 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.2: version "4.2.11" resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -1644,6 +1739,23 @@ has-flag@^4.0.0: resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + hasown@^2.0.0: version "2.0.2" resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -1933,6 +2045,18 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@^1.4.1: version "1.6.0" resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -1985,6 +2109,11 @@ object-assign@^4.1.1: resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + once@^1.3.0: version "1.4.0" resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -2108,6 +2237,11 @@ prop-types@15.x, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -2123,6 +2257,13 @@ qrcode.react@^3.1.0: resolved "https://registry.npmmirror.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== +qs@^6.12.1: + version "6.12.1" + resolved "https://registry.npmmirror.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" + integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== + dependencies: + side-channel "^1.0.6" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2339,6 +2480,18 @@ semver@^7.6.0: resolved "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2351,6 +2504,16 @@ shebang-regex@^3.0.0: resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + slash@^3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"