From d98bddc54fc8807218962604f514c0d9b088a61c Mon Sep 17 00:00:00 2001 From: callmeyan Date: Sun, 26 May 2024 11:41:45 +0800 Subject: [PATCH] :sparkles: update qr-code style --- Dockerfile | 9 ++- src/components/bill/bill-detail-items.tsx | 25 +++++++ src/components/bill/bill.less | 48 +++++++++++- src/components/bill/detail.tsx | 52 ++++++------- src/components/bill/index.ts | 3 +- src/components/bill/qr-code.tsx | 2 +- src/components/bill/search-form.tsx | 2 +- src/components/icons/index.tsx | 89 +++++++++++++++++++++++ src/components/money-format.tsx | 5 +- src/i18n/translations/en.json | 2 + src/i18n/translations/sc.json | 2 + src/i18n/translations/tc.json | 2 + src/pages/bill/query.tsx | 10 +-- src/pages/manual/index.tsx | 17 +---- src/pages/manual/manual.module.less | 14 ---- src/routes/index.tsx | 1 + src/service/api/bill.ts | 10 ++- src/service/request.ts | 8 +- src/types/api.d.ts | 4 + src/vite-env.d.ts | 3 +- vite.config.ts | 1 + 21 files changed, 233 insertions(+), 76 deletions(-) create mode 100644 src/components/bill/bill-detail-items.tsx diff --git a/Dockerfile b/Dockerfile index 715ff42..4ba666c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,12 @@ FROM node:18.19.1-alpine AS builder MAINTAINER yaclty2@gmail.com WORKDIR /app +# envs 配置 +# 应用部署后的URL +ENV APP_SITE_URL "" +# 应用接口前缀 +ENV APP_API_URL "" + # Copy source code to the builder COPY package.json yarn.lock* ./ COPY public ./public @@ -24,8 +30,7 @@ FROM nginx:1.26-alpine3.19 AS runner WORKDIR /app # envs 配置 -ENV APP_API_URL https://baidu.com -ENV APP_SITE_URL https://pay.wm-app.xyz +ENV APP_API_URL http://43.136.175.109:50000 # nginx配置文件 COPY nginx.conf /etc/nginx/conf.d/default.conf.template diff --git a/src/components/bill/bill-detail-items.tsx b/src/components/bill/bill-detail-items.tsx new file mode 100644 index 0000000..1d76876 --- /dev/null +++ b/src/components/bill/bill-detail-items.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import {useTranslation} from "react-i18next"; + +import {IconBillType, IconMoney, IconStudentId} 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 }) => { + return
+
{item.icon} {item.title} :
+
{item.value}
+
+} +const BillDetailItems = ({bill}: { bill: BillModel }) => { + const {t} = useTranslation(); + return (<> + } title={t('manual.bill_type')} value={'TUITION FEE'}/> + } title={t('manual.student_number')} value={bill.student_number}/> + } title={t('bill.title_student_name')} + value={`${bill.student_english_name}/${bill.student_chinese_name}`}/> + } title={t('manual.amount')} value={}/> + ) +} + +export default BillDetailItems; \ No newline at end of file diff --git a/src/components/bill/bill.less b/src/components/bill/bill.less index 6704504..d20df50 100644 --- a/src/components/bill/bill.less +++ b/src/components/bill/bill.less @@ -1,4 +1,4 @@ -.bill-search-form{ +.bill-search-form { } @@ -15,6 +15,7 @@ .bill-info-title { display: flex; align-items: center; + margin-right: 5px; &:before { content: ' '; @@ -27,4 +28,49 @@ } } } +} +.modal-bill-detail{ + .modal-bill-info{ + display: flex; + justify-content: center; + } + .bill-info-detail{ + margin-left: 30px; + width: 340px; + } + .bill-exp-time{ + font-size: 18px; + margin-bottom: 20px; + border-bottom: dashed 2px #eeeeee; + padding-bottom: 20px; + } +} + + +.bill-detail-item { + display: flex; + margin-bottom: 10px; + align-items: center; + + .detail-item-title { + font-weight: bold; + width: 120px; + white-space: nowrap; + display: flex; + align-items: center; + } + .item-title{ + margin-left: 5px; + } + + .detail-item-value { + color: #999999; + } +} +body[data-lang=en-US]{ + .bill-detail-item { + .detail-item-title{ + width: 170px; + } + } } \ No newline at end of file diff --git a/src/components/bill/detail.tsx b/src/components/bill/detail.tsx index 524fefc..e69a03e 100644 --- a/src/components/bill/detail.tsx +++ b/src/components/bill/detail.tsx @@ -1,43 +1,37 @@ import styles from "@/pages/manual/manual.module.less"; import {Button, Space} from "@douyinfe/semi-ui"; -import QRCode from "qrcode.react"; import {useTranslation} from "react-i18next"; -import {useRef} from "react"; -import {saveAs} from "file-saver"; + +import {BillDetailItems, useBillQRCode} from "@/components/bill/index.ts"; + +import './bill.less' -const BillDetailItem = (item:{title:string;value:string})=>{ - return
-
{item.title} :
-
{item.value}
-
+type BillDetailProps = { + onCancel: ()=>void; + bill:BillModel; } - -const BillDetail:BasicComponent<{bill:BillModel}> = ()=>{ +const BillDetail:BasicComponent = ({bill,onCancel})=>{ const {t} = useTranslation(); - const qrCodeRef = useRef(null) - const downloadQRCode = ()=>{ - const canvas = qrCodeRef.current?.querySelector('canvas'); - if(!canvas) return - saveAs(canvas.toDataURL(), 'qrcode.png') - } - return
- -
+ const { exportQRCode,QRCode } = useBillQRCode() + return
+
+
-
- -
+
-
{t('manual.exp_time')} {'12:00'}
-
- - - - +
+
{t('manual.exp_time')} {'12:00'}
+
- +
+
+ + + + +
} export default BillDetail \ No newline at end of file diff --git a/src/components/bill/index.ts b/src/components/bill/index.ts index 3711f02..7af9a51 100644 --- a/src/components/bill/index.ts +++ b/src/components/bill/index.ts @@ -3,5 +3,6 @@ import { BillList } from './list.tsx' import useBillQRCode from './qr-code.tsx' +import BillDetailItems from './bill-detail-items.tsx' -export {BillDetail, BillList, useBillQRCode} \ No newline at end of file +export {BillDetail, BillList, useBillQRCode,BillDetailItems} \ No newline at end of file diff --git a/src/components/bill/qr-code.tsx b/src/components/bill/qr-code.tsx index a119d9e..a94936c 100644 --- a/src/components/bill/qr-code.tsx +++ b/src/components/bill/qr-code.tsx @@ -15,7 +15,7 @@ export type BillQrcodeProps = { function getPayUrl(billId?: string | number) { const {host, protocol} = location //AppConfig.SITE_URL - const rootUrl = (typeof (APP_SITE_URL) == "string" ? APP_SITE_URL : undefined) || `${protocol}//${host}` + const rootUrl = (typeof (APP_SITE_URL) == "string" ? APP_SITE_URL : undefined) || AppConfig.SITE_URL || `${protocol}//${host}` return `${rootUrl}/pay?bill=${billId || 0}&from=qrcode` } diff --git a/src/components/bill/search-form.tsx b/src/components/bill/search-form.tsx index 49c0046..579427a 100644 --- a/src/components/bill/search-form.tsx +++ b/src/components/bill/search-form.tsx @@ -23,7 +23,7 @@ type SearchFormFields = { const SearchForm: React.FC = (props) => { const formSubmit = (value: SearchFormFields) => { const params: BillQueryParams = {} - if (value.dateRange) { + if (value.dateRange && value.dateRange.length == 2) { params.start_date = dayjs(value.dateRange[0]).format('YYYY-MM-DD'); params.end_date = dayjs(value.dateRange[1]).format('YYYY-MM-DD'); } diff --git a/src/components/icons/index.tsx b/src/components/icons/index.tsx index 6155e41..d25da87 100644 --- a/src/components/icons/index.tsx +++ b/src/components/icons/index.tsx @@ -32,4 +32,93 @@ export const IconChecked = ({style}: IconProps) => { fill="currentColor"> ) +} + +export const IconMoney = ({style}: IconProps) => { + return ( + + + + + + + + + + + + + + + + ) +} + +export const IconStudentId = ({style}: IconProps) => { + return ( + + + + + + + + + + + + + + + + + + + + + ) +} + +export const IconBillType = ({style}: IconProps) => { + return ( + + + + + + + + + + + + + + + + + + + + + + ) } \ No newline at end of file diff --git a/src/components/money-format.tsx b/src/components/money-format.tsx index 40761d2..93dcf58 100644 --- a/src/components/money-format.tsx +++ b/src/components/money-format.tsx @@ -6,6 +6,9 @@ type MoneyFormatProps = { } function formatCurrency(amount: string | number) { + if(amount === '0' || amount === '0.00') { + return '0.00'; + } // 将金额转换为字符串,并限制小数点后两位 let amountStr = Number(amount).toFixed(2); @@ -24,6 +27,6 @@ function formatCurrency(amount: string | number) { const MoneyFormat: React.FC = ({money, currency = 'HK$'}) => { // 将货币数字转换为千分位格式且带2位小数 - return money?{currency} {formatCurrency(money)}:null + return (money||money==0||money=='0') ?{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 b92b6c5..8d535c4 100644 --- a/src/i18n/translations/en.json +++ b/src/i18n/translations/en.json @@ -2,6 +2,7 @@ "base": { "bill_number": "Bill Number", "btn_search_submit": "Search", + "close": "Close", "please_enter": "Please Enter", "please_select": "Please Select", "student_number": "Student Number" @@ -13,6 +14,7 @@ "confirm_batch": "Batch Confirm", "confirm_select_empty": "Require confirm bill data", "confirmed": "Confirmed", + "download-qr-code": "Download QR Code", "download_receipt": "Download receipt", "pay_status": "Bill Status", "pay_status_canceled": "Canceled", diff --git a/src/i18n/translations/sc.json b/src/i18n/translations/sc.json index f29e712..b4c5e37 100644 --- a/src/i18n/translations/sc.json +++ b/src/i18n/translations/sc.json @@ -2,6 +2,7 @@ "base": { "bill_number": "账单编号", "btn_search_submit": "搜索", + "close": "关闭", "please_enter": "请输入", "please_select": "请选择", "student_number": "学号" @@ -13,6 +14,7 @@ "confirm_batch": "批量对账", "confirm_select_empty": "对账账单为空", "confirmed": "已对账", + "download-qr-code": "下载二维码", "download_receipt": "下载收据", "pay_status": "账单状态", "pay_status_canceled": "已作废", diff --git a/src/i18n/translations/tc.json b/src/i18n/translations/tc.json index 270174f..a7f7e7c 100644 --- a/src/i18n/translations/tc.json +++ b/src/i18n/translations/tc.json @@ -2,6 +2,7 @@ "base": { "bill_number": "账单编号", "btn_search_submit": "搜索", + "close": "关闭", "please_enter": "请输入", "please_select": "请选择", "student_number": "学号" @@ -13,6 +14,7 @@ "confirm_batch": "批量对账", "confirm_select_empty": "对账账单为空", "confirmed": "已对账", + "download-qr-code": "下载二维码", "download_receipt": "下载收据", "pay_status": "账单状态", "pay_status_canceled": "已作废", diff --git a/src/pages/bill/query.tsx b/src/pages/bill/query.tsx index 9506efb..008dfae 100644 --- a/src/pages/bill/query.tsx +++ b/src/pages/bill/query.tsx @@ -32,15 +32,15 @@ const BillQuery = () => { }} /> { - }} + width={620} onCancel={() => setShowBill(undefined)} //>=1.16.0 closeOnEsc={true} - footerFill={true} + footer={null} + closeIcon={} > - {showBill && } + {showBill && setShowBill(undefined)}/>}
) } diff --git a/src/pages/manual/index.tsx b/src/pages/manual/index.tsx index 5bc559a..e359451 100644 --- a/src/pages/manual/index.tsx +++ b/src/pages/manual/index.tsx @@ -1,11 +1,10 @@ import {Button, Form, Space, Toast} from "@douyinfe/semi-ui"; import {useTranslation} from "react-i18next"; -import {ReactNode, useRef, useState} from "react"; +import { useRef, useState} from "react"; 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 {BillDetailItems, useBillQRCode} from "@/components/bill"; import styles from './manual.module.less' import {createManualBill, getBillDetail} from "@/service/api/bill.ts"; @@ -14,12 +13,6 @@ import {BizError} from "@/service/types.ts"; import {IconAlertTriangle} from "@douyinfe/semi-icons"; import dayjs from "dayjs"; -const BillDetailItem = (item: { title: string; value: ReactNode }) => { - return
-
{item.title} :
-
{item.value}
-
-} export default function Index() { @@ -50,9 +43,7 @@ export default function Index() { const BillInfo = ({bill}: { bill?: BillModel }) => { if (!bill) return null; return (<> - - - }/> + ) @@ -100,7 +91,7 @@ export default function Index() {
- +
{billInfo &&
{t('manual.exp_time')} {dayjs(billInfo.expiration_time).format('HH:mm')}
} diff --git a/src/pages/manual/manual.module.less b/src/pages/manual/manual.module.less index 1e380f5..dc9f659 100644 --- a/src/pages/manual/manual.module.less +++ b/src/pages/manual/manual.module.less @@ -11,23 +11,9 @@ } .billDetail { - padding: 30px 0 50px; } -.billDetailItem { - display: flex; - margin-bottom: 10px; - align-items: center; -} -.billDetailItemTitle { - font-weight: bold; - width: 150px; -} - -.billDetailItemValue { - color: #999999; -} .billQrCode{ text-align: center; margin-left: 150px; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 2a53507..3ea4ed2 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -80,6 +80,7 @@ const AppRouter = () => { // change ui locale const {i18n} = useTranslation() const locale = useMemo(()=>{ + document.body.setAttribute('data-lang', i18n.language) if(i18n.language === 'zh-CN') return zh_CN; else if(i18n.language === 'zh-TW') return zh_TW; return en_US; diff --git a/src/service/api/bill.ts b/src/service/api/bill.ts index 2b89c4d..c762163 100644 --- a/src/service/api/bill.ts +++ b/src/service/api/bill.ts @@ -1,4 +1,4 @@ -import {get, post} from "@/service/request.ts"; +import {get, post, put} from "@/service/request.ts"; export type BillQueryResult = { result: BillModel[]; @@ -23,7 +23,7 @@ function formatBillQueryResult(params: BillQueryParams, result: BillQueryResult) } export async function billList(params: BillQueryParams) { - const result = await get('/bills', {params}) + const result = await get('/bills', params) return formatBillQueryResult(params, result); } @@ -32,6 +32,12 @@ export function createManualBill(params: ManualCreateBillParam) { return post<{ id: number }>('/manual_payment', params) } +// 获取账单详情 export function getBillDetail(id: number) { return get('/bills/' + id) +} + +// 作废订单 +export function cancelBill(id: number){ + return put('/bills/' + id) } \ No newline at end of file diff --git a/src/service/request.ts b/src/service/request.ts index 3345adc..e62146e 100644 --- a/src/service/request.ts +++ b/src/service/request.ts @@ -1,15 +1,13 @@ import axios from 'axios'; import {stringify} from 'qs' -import { BizError } from './types'; - +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, + baseURL: AppConfig.API_PREFIX || '/api', + timeout: REQUEST_TIMEOUT, headers: {'Content-Type': JSON_FORMAT} }) diff --git a/src/types/api.d.ts b/src/types/api.d.ts index 3199998..84880c5 100644 --- a/src/types/api.d.ts +++ b/src/types/api.d.ts @@ -1,4 +1,7 @@ +// 请求方式 +declare type RequestMethod = 'get' | 'post' | 'put' | 'delete' +// 接口返回数据类型 declare interface APIResponse { /** * 错误码,0:成功,其他失败 @@ -9,4 +12,5 @@ declare interface APIResponse { * 非0情况下,提示信息 */ message: string; + request_id: string; } \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 4b34e00..82d5750 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -7,7 +7,8 @@ declare type AllType = string | number | object | undefined | null; declare const APP_SITE_URL:string; declare const AppConfig:{ // 系统部署运行地址(主要用于支付二维码生成) - SITE_URL:string + SITE_URL:string; + API_PREFIX: string; }; declare type BasicComponentProps = { diff --git a/vite.config.ts b/vite.config.ts index 1520047..723b0cc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,6 +11,7 @@ export default defineConfig(({mode}) => { define: { AppConfig: JSON.stringify({ SITE_URL: process.env.APP_SITE_URL || null, + API_PREFIX: process.env.APP_API_PREFIX || '/api', }), }, resolve: {