@@ -21,6 +21,7 @@ import {
2121 ANNUAL_BILLING_MONTHS ,
2222 type BillingOption ,
2323 DEFAULT_MONTHLY_PRICE_FACTOR ,
24+ getAnnualSavings ,
2425 getBillingOptions ,
2526 resolveMonthlyPriceFactor ,
2627} from '@/utils/billing' ;
@@ -72,14 +73,39 @@ function formatMoney(value: number) {
7273 return Number . isInteger ( value ) ? `¥${ value } ` : `¥${ value . toFixed ( 2 ) } ` ;
7374}
7475
75- function formatBillingOptionLabel ( option : BillingOption , showAmount = true ) {
76+ function formatDiscount ( value : number ) {
77+ return Number . isInteger ( value ) ? String ( value ) : value . toFixed ( 1 ) ;
78+ }
79+
80+ function formatBillingOptionText ( option : BillingOption , showAmount = true ) {
7681 const amount = showAmount ? `,${ formatMoney ( option . amount ) } ` : '' ;
7782 if ( option . billingCycle === 'year' ) {
7883 return `年付(${ option . billingMonths } 个月${ amount } )` ;
7984 }
8085 return `${ option . billingMonths } 个月${ showAmount ? `(${ formatMoney ( option . amount ) } )` : '' } ` ;
8186}
8287
88+ function BillingOptionLabel ( {
89+ option,
90+ showAmount,
91+ } : {
92+ option : BillingOption ;
93+ showAmount : boolean ;
94+ } ) {
95+ const savings = getAnnualSavings ( option ) ;
96+
97+ return (
98+ < span className = "inline-flex min-w-0 flex-wrap items-center gap-1.5" >
99+ < span > { formatBillingOptionText ( option , showAmount ) } </ span >
100+ { savings . discount > 0 && (
101+ < Tag color = "gold" className = "m-0" >
102+ 约{ formatDiscount ( savings . discount ) } 折优惠
103+ </ Tag >
104+ ) }
105+ </ span >
106+ ) ;
107+ }
108+
83109function BillingMonthsSelect ( {
84110 disabled,
85111 onChange,
@@ -109,6 +135,12 @@ function BillingMonthsSelect({
109135 const selectedValue = options . some ( ( option ) => option . value === value )
110136 ? value
111137 : fallbackValue ;
138+ const selectedOption = options . find (
139+ ( option ) => option . value === selectedValue ,
140+ ) ;
141+ const selectedSavings = selectedOption
142+ ? getAnnualSavings ( selectedOption )
143+ : { amount : 0 , percent : 0 , discount : 0 } ;
112144
113145 useEffect ( ( ) => {
114146 if ( selectedValue !== value ) {
@@ -121,17 +153,24 @@ function BillingMonthsSelect({
121153 }
122154
123155 return (
124- < Select
125- aria-label = "选择付费周期"
126- className = "w-full sm:w-60"
127- disabled = { disabled }
128- onChange = { onChange }
129- options = { options . map ( ( option ) => ( {
130- label : formatBillingOptionLabel ( option , showAmount ) ,
131- value : option . value ,
132- } ) ) }
133- value = { selectedValue }
134- />
156+ < div className = "flex w-full flex-col gap-1 sm:w-72" >
157+ < Select
158+ aria-label = "选择付费周期"
159+ className = "w-full"
160+ disabled = { disabled }
161+ onChange = { onChange }
162+ options = { options . map ( ( option ) => ( {
163+ label : < BillingOptionLabel option = { option } showAmount = { showAmount } /> ,
164+ value : option . value ,
165+ } ) ) }
166+ value = { selectedValue }
167+ />
168+ { selectedSavings . discount > 0 && (
169+ < div className = "rounded border border-red-200 bg-red-50 px-2 py-1 text-xs font-medium text-red-600" >
170+ { `年付比按月购买节省 ${ formatMoney ( selectedSavings . amount ) } ,约${ formatDiscount ( selectedSavings . discount ) } 折优惠` }
171+ </ div >
172+ ) }
173+ </ div >
135174 ) ;
136175}
137176
0 commit comments