@windrun-huaiin/third-ui 7.3.3 → 7.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clerk/fingerprint/fingerprint-provider.js +26 -6
- package/dist/clerk/fingerprint/fingerprint-provider.mjs +27 -7
- package/dist/clerk/fingerprint/use-fingerprint.js +1 -1
- package/dist/clerk/fingerprint/use-fingerprint.mjs +1 -1
- package/dist/main/faq.js +0 -2
- package/dist/main/faq.mjs +0 -2
- package/dist/main/gallery.js +0 -2
- package/dist/main/gallery.mjs +0 -2
- package/dist/main/money-price/money-price-config-util.d.ts +7 -0
- package/dist/main/money-price/money-price-config-util.js +19 -0
- package/dist/main/money-price/money-price-config-util.mjs +16 -0
- package/dist/main/money-price/money-price-interactive.js +29 -50
- package/dist/main/money-price/money-price-interactive.mjs +28 -49
- package/dist/main/money-price/money-price-types.d.ts +1 -1
- package/dist/main/money-price/money-price.js +3 -23
- package/dist/main/money-price/money-price.mjs +2 -22
- package/dist/main/price-plan.js +0 -2
- package/dist/main/price-plan.mjs +0 -2
- package/dist/main/server.d.ts +1 -1
- package/dist/main/server.js +3 -4
- package/dist/main/server.mjs +1 -1
- package/package.json +1 -1
- package/src/clerk/fingerprint/fingerprint-provider.tsx +53 -20
- package/src/clerk/fingerprint/use-fingerprint.ts +1 -1
- package/src/main/money-price/money-price-config-util.ts +23 -0
- package/src/main/money-price/money-price-interactive.tsx +47 -64
- package/src/main/money-price/money-price-types.ts +1 -1
- package/src/main/money-price/money-price.tsx +5 -32
- package/src/main/server.ts +1 -2
- package/src/main/money-price/money-price-config.ts +0 -229
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money Price Configuration
|
|
3
|
+
* 价格组件配置文件
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { MoneyPriceConfig, PaymentProviderConfig, EnhancePricePlan } from './money-price-types';
|
|
7
|
+
|
|
8
|
+
// 辅助函数:获取当前激活的支付供应商配置
|
|
9
|
+
export function getActiveProviderConfig(config: MoneyPriceConfig): PaymentProviderConfig {
|
|
10
|
+
const provider = config.activeProvider;
|
|
11
|
+
return config.paymentProviders[provider];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 辅助函数:获取特定产品的价格信息
|
|
15
|
+
export function getProductPricing(
|
|
16
|
+
productKey: 'free' | 'pro' | 'ultra',
|
|
17
|
+
billingType: 'monthly' | 'yearly',
|
|
18
|
+
provider: string,
|
|
19
|
+
config: MoneyPriceConfig
|
|
20
|
+
): EnhancePricePlan {
|
|
21
|
+
const providerConfig = config.paymentProviders[provider];
|
|
22
|
+
return providerConfig.products[productKey].plans[billingType];
|
|
23
|
+
}
|
|
@@ -4,10 +4,9 @@ import { useClerk } from '@clerk/nextjs';
|
|
|
4
4
|
import { useFingerprintContextSafe } from '@third-ui/clerk/fingerprint';
|
|
5
5
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
6
6
|
import { useRouter } from 'next/navigation';
|
|
7
|
-
import React, { useEffect, useState } from 'react';
|
|
8
|
-
import { createRoot } from 'react-dom/client';
|
|
7
|
+
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
|
9
8
|
import { MoneyPriceButton } from './money-price-button';
|
|
10
|
-
import { getActiveProviderConfig, getProductPricing } from './money-price-config';
|
|
9
|
+
import { getActiveProviderConfig, getProductPricing } from './money-price-config-util';
|
|
11
10
|
import {
|
|
12
11
|
UserState,
|
|
13
12
|
type MoneyPriceInteractiveProps,
|
|
@@ -33,9 +32,9 @@ export function MoneyPriceInteractive({
|
|
|
33
32
|
x: number;
|
|
34
33
|
y: number;
|
|
35
34
|
}>({ show: false, content: '', x: 0, y: 0 });
|
|
36
|
-
|
|
35
|
+
|
|
37
36
|
// 确定用户状态
|
|
38
|
-
const getUserState = (): UserState => {
|
|
37
|
+
const getUserState = useCallback((): UserState => {
|
|
39
38
|
if (!fingerprintContext) return UserState.Anonymous;
|
|
40
39
|
const { xUser, xSubscription } = fingerprintContext;
|
|
41
40
|
|
|
@@ -44,27 +43,27 @@ export function MoneyPriceInteractive({
|
|
|
44
43
|
if (xSubscription.priceName?.includes('Pro')) return UserState.ProUser;
|
|
45
44
|
if (xSubscription.priceName?.includes('Ultra')) return UserState.UltraUser;
|
|
46
45
|
return UserState.FreeUser;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
}, [fingerprintContext]);
|
|
47
|
+
|
|
48
|
+
// 优化 userContext 使用 useMemo
|
|
49
|
+
const userContext = useMemo<UserContext>(() => ({
|
|
50
50
|
isAuthenticated: !!fingerprintContext?.xUser?.clerkUserId,
|
|
51
51
|
subscriptionStatus: getUserState(),
|
|
52
52
|
subscriptionType: fingerprintContext?.xSubscription?.priceId?.includes('yearly') ? 'yearly' : 'monthly',
|
|
53
53
|
subscriptionEndDate: fingerprintContext?.xSubscription?.subPeriodEnd
|
|
54
|
-
};
|
|
55
|
-
|
|
54
|
+
}), [fingerprintContext, getUserState]);
|
|
55
|
+
|
|
56
56
|
// 处理登录
|
|
57
|
-
const handleLogin = () => {
|
|
57
|
+
const handleLogin = useCallback(() => {
|
|
58
58
|
if (signInPath) {
|
|
59
59
|
router.push(signInPath);
|
|
60
60
|
} else {
|
|
61
61
|
redirectToSignIn();
|
|
62
62
|
}
|
|
63
|
-
};
|
|
64
|
-
|
|
63
|
+
}, [signInPath, redirectToSignIn, router]);
|
|
64
|
+
|
|
65
65
|
// 处理升级
|
|
66
|
-
const handleUpgrade = async (plan: string, billingType: string) => {
|
|
67
|
-
// 如果没有配置 API 端点,跳转到首页
|
|
66
|
+
const handleUpgrade = useCallback(async (plan: string, billingType: string) => {
|
|
68
67
|
if (!upgradeApiEndpoint) {
|
|
69
68
|
router.push('/');
|
|
70
69
|
return;
|
|
@@ -72,7 +71,6 @@ export function MoneyPriceInteractive({
|
|
|
72
71
|
|
|
73
72
|
setIsProcessing(true);
|
|
74
73
|
try {
|
|
75
|
-
// 获取价格配置
|
|
76
74
|
const pricing = getProductPricing(
|
|
77
75
|
plan as 'free' | 'pro' | 'ultra',
|
|
78
76
|
billingType as 'monthly' | 'yearly',
|
|
@@ -80,7 +78,6 @@ export function MoneyPriceInteractive({
|
|
|
80
78
|
config
|
|
81
79
|
);
|
|
82
80
|
|
|
83
|
-
// 调用 API 创建支付会话
|
|
84
81
|
const response = await fetch(upgradeApiEndpoint, {
|
|
85
82
|
method: 'POST',
|
|
86
83
|
headers: {
|
|
@@ -97,7 +94,6 @@ export function MoneyPriceInteractive({
|
|
|
97
94
|
const result = await response.json();
|
|
98
95
|
|
|
99
96
|
if (result.success && result.checkoutUrl) {
|
|
100
|
-
// 跳转到支付页面
|
|
101
97
|
window.location.href = result.checkoutUrl;
|
|
102
98
|
} else {
|
|
103
99
|
console.error('Failed to create checkout session:', result.error);
|
|
@@ -107,17 +103,16 @@ export function MoneyPriceInteractive({
|
|
|
107
103
|
} finally {
|
|
108
104
|
setIsProcessing(false);
|
|
109
105
|
}
|
|
110
|
-
};
|
|
111
|
-
|
|
106
|
+
}, [upgradeApiEndpoint, config, router]);
|
|
107
|
+
|
|
112
108
|
// 更新价格显示
|
|
113
|
-
const updatePriceDisplay = (newBillingType: string) => {
|
|
109
|
+
const updatePriceDisplay = useCallback((newBillingType: string) => {
|
|
114
110
|
const providerConfig = getActiveProviderConfig(config);
|
|
115
111
|
|
|
116
112
|
data.plans.forEach((plan: any) => {
|
|
117
113
|
const productConfig = providerConfig.products[plan.key as 'free' | 'pro' | 'ultra'];
|
|
118
114
|
const pricing = productConfig.plans[newBillingType as 'monthly' | 'yearly'];
|
|
119
115
|
|
|
120
|
-
// 更新价格值
|
|
121
116
|
const priceValueElement = document.querySelector(`[data-price-value="${plan.key}"]`) as HTMLElement;
|
|
122
117
|
if (priceValueElement) {
|
|
123
118
|
if (pricing.amount === 0) {
|
|
@@ -127,14 +122,12 @@ export function MoneyPriceInteractive({
|
|
|
127
122
|
}
|
|
128
123
|
}
|
|
129
124
|
|
|
130
|
-
// 更新单位
|
|
131
125
|
const priceUnitElement = document.querySelector(`[data-price-unit="${plan.key}"]`) as HTMLElement;
|
|
132
126
|
if (priceUnitElement) {
|
|
133
127
|
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
134
128
|
priceUnitElement.textContent = billingOption?.unit || '/month';
|
|
135
129
|
}
|
|
136
130
|
|
|
137
|
-
// 更新原价和折扣
|
|
138
131
|
const priceOriginalElement = document.querySelector(`[data-price-original="${plan.key}"]`) as HTMLElement;
|
|
139
132
|
const priceDiscountElement = document.querySelector(`[data-price-discount="${plan.key}"]`) as HTMLElement;
|
|
140
133
|
|
|
@@ -158,17 +151,16 @@ export function MoneyPriceInteractive({
|
|
|
158
151
|
if (priceDiscountElement) priceDiscountElement.style.display = 'none';
|
|
159
152
|
}
|
|
160
153
|
|
|
161
|
-
// 更新副标题
|
|
162
154
|
const priceSubtitleElement = document.querySelector(`[data-price-subtitle="${plan.key}"]`) as HTMLElement;
|
|
163
155
|
if (priceSubtitleElement && plan.showBillingSubTitle !== false) {
|
|
164
156
|
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
165
157
|
priceSubtitleElement.textContent = billingOption?.subTitle || '';
|
|
166
158
|
}
|
|
167
159
|
});
|
|
168
|
-
};
|
|
169
|
-
|
|
160
|
+
}, [config, data]);
|
|
161
|
+
|
|
170
162
|
// 更新按钮样式
|
|
171
|
-
const updateButtonStyles = (newBillingType: string) => {
|
|
163
|
+
const updateButtonStyles = useCallback((newBillingType: string) => {
|
|
172
164
|
const monthlyButton = document.querySelector('[data-billing-button="monthly"]') as HTMLButtonElement;
|
|
173
165
|
const yearlyButton = document.querySelector('[data-billing-button="yearly"]') as HTMLButtonElement;
|
|
174
166
|
|
|
@@ -184,17 +176,16 @@ export function MoneyPriceInteractive({
|
|
|
184
176
|
yearlyButton.className = cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', activeClasses);
|
|
185
177
|
}
|
|
186
178
|
}
|
|
187
|
-
};
|
|
188
|
-
|
|
179
|
+
}, []);
|
|
180
|
+
|
|
189
181
|
// 更新折扣信息
|
|
190
|
-
const updateDiscountInfo = (newBillingType: string) => {
|
|
182
|
+
const updateDiscountInfo = useCallback((newBillingType: string) => {
|
|
191
183
|
const discountInfoElement = document.querySelector('[data-discount-info]') as HTMLElement;
|
|
192
184
|
if (!discountInfoElement) return;
|
|
193
185
|
|
|
194
186
|
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
195
187
|
const providerConfig = getActiveProviderConfig(config);
|
|
196
188
|
|
|
197
|
-
// 检查是否有折扣
|
|
198
189
|
let hasDiscount = false;
|
|
199
190
|
let discountPercent = 0;
|
|
200
191
|
|
|
@@ -207,7 +198,6 @@ export function MoneyPriceInteractive({
|
|
|
207
198
|
}
|
|
208
199
|
});
|
|
209
200
|
|
|
210
|
-
// 清空内容
|
|
211
201
|
discountInfoElement.innerHTML = '';
|
|
212
202
|
|
|
213
203
|
if (hasDiscount && billingOption?.discountText) {
|
|
@@ -216,29 +206,8 @@ export function MoneyPriceInteractive({
|
|
|
216
206
|
discountBadge.textContent = billingOption.discountText.replace('{percent}', String(discountPercent));
|
|
217
207
|
discountInfoElement.appendChild(discountBadge);
|
|
218
208
|
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
// 动态替换按钮
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
data.plans.forEach((plan: any) => {
|
|
224
|
-
const placeholder = document.querySelector(`[data-button-placeholder="${plan.key}"]`);
|
|
225
|
-
if (placeholder) {
|
|
226
|
-
const root = createRoot(placeholder);
|
|
227
|
-
root.render(
|
|
228
|
-
<MoneyPriceButton
|
|
229
|
-
planKey={plan.key}
|
|
230
|
-
userContext={userContext}
|
|
231
|
-
billingType={billingType}
|
|
232
|
-
onLogin={handleLogin}
|
|
233
|
-
onUpgrade={handleUpgrade}
|
|
234
|
-
texts={data.buttonTexts}
|
|
235
|
-
isProcessing={isProcessing}
|
|
236
|
-
/>
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
}, [userContext, billingType, isProcessing]);
|
|
241
|
-
|
|
209
|
+
}, [config, data]);
|
|
210
|
+
|
|
242
211
|
// 处理月付/年付切换和 tooltip 功能
|
|
243
212
|
useEffect(() => {
|
|
244
213
|
const monthlyButton = document.querySelector('[data-billing-button="monthly"]') as HTMLButtonElement;
|
|
@@ -265,7 +234,6 @@ export function MoneyPriceInteractive({
|
|
|
265
234
|
yearlyButton.addEventListener('click', handleYearlyClick);
|
|
266
235
|
}
|
|
267
236
|
|
|
268
|
-
// 添加 tooltip 功能
|
|
269
237
|
const tooltipHandlers: Array<{
|
|
270
238
|
element: HTMLElement;
|
|
271
239
|
handlers: {
|
|
@@ -307,11 +275,9 @@ export function MoneyPriceInteractive({
|
|
|
307
275
|
});
|
|
308
276
|
});
|
|
309
277
|
|
|
310
|
-
// 初始化价格显示
|
|
311
278
|
updatePriceDisplay(billingType);
|
|
312
279
|
updateDiscountInfo(billingType);
|
|
313
280
|
|
|
314
|
-
// 清理
|
|
315
281
|
return () => {
|
|
316
282
|
if (monthlyButton) {
|
|
317
283
|
monthlyButton.removeEventListener('click', handleMonthlyClick);
|
|
@@ -320,15 +286,14 @@ export function MoneyPriceInteractive({
|
|
|
320
286
|
yearlyButton.removeEventListener('click', handleYearlyClick);
|
|
321
287
|
}
|
|
322
288
|
|
|
323
|
-
// 清理 tooltip 事件监听器
|
|
324
289
|
tooltipHandlers.forEach(({ element, handlers }) => {
|
|
325
290
|
element.removeEventListener('mouseenter', handlers.mouseenter);
|
|
326
291
|
element.removeEventListener('mousemove', handlers.mousemove);
|
|
327
292
|
element.removeEventListener('mouseleave', handlers.mouseleave);
|
|
328
293
|
});
|
|
329
294
|
};
|
|
330
|
-
}, [data]);
|
|
331
|
-
|
|
295
|
+
}, [data, billingType, updatePriceDisplay, updateButtonStyles, updateDiscountInfo]);
|
|
296
|
+
|
|
332
297
|
// Tooltip 组件
|
|
333
298
|
const Tooltip = ({ show, content, x, y }: typeof tooltip) => {
|
|
334
299
|
if (!show) return null;
|
|
@@ -351,6 +316,24 @@ export function MoneyPriceInteractive({
|
|
|
351
316
|
</div>
|
|
352
317
|
);
|
|
353
318
|
};
|
|
354
|
-
|
|
355
|
-
|
|
319
|
+
|
|
320
|
+
// 直接渲染按钮,确保宽度占满
|
|
321
|
+
return (
|
|
322
|
+
<>
|
|
323
|
+
{data.plans.map((plan: any) => (
|
|
324
|
+
<div key={plan.key} data-button-placeholder={plan.key}>
|
|
325
|
+
<MoneyPriceButton
|
|
326
|
+
planKey={plan.key}
|
|
327
|
+
userContext={userContext}
|
|
328
|
+
billingType={billingType}
|
|
329
|
+
onLogin={handleLogin}
|
|
330
|
+
onUpgrade={handleUpgrade}
|
|
331
|
+
texts={data.buttonTexts}
|
|
332
|
+
isProcessing={isProcessing}
|
|
333
|
+
/>
|
|
334
|
+
</div>
|
|
335
|
+
))}
|
|
336
|
+
<Tooltip {...tooltip} />
|
|
337
|
+
</>
|
|
338
|
+
);
|
|
356
339
|
}
|
|
@@ -20,7 +20,7 @@ export interface UserContext {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// 支付供应商类型
|
|
23
|
-
export type PaymentProvider = 'stripe' | '
|
|
23
|
+
export type PaymentProvider = 'stripe' | 'apple' | 'paypal' | 'wechat' | 'alipay' ;
|
|
24
24
|
|
|
25
25
|
// 价格计划
|
|
26
26
|
export interface EnhancePricePlan {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { GradientButton } from '@third-ui/fuma/mdx';
|
|
3
2
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
3
|
import { getTranslations } from 'next-intl/server';
|
|
5
|
-
import { getActiveProviderConfig } from './money-price-config';
|
|
4
|
+
import { getActiveProviderConfig } from './money-price-config-util';
|
|
6
5
|
import { MoneyPriceInteractive } from './money-price-interactive';
|
|
7
|
-
import type {
|
|
6
|
+
import type { MoneyPriceProps, MoneyPriceData } from './money-price-types';
|
|
8
7
|
|
|
9
8
|
export async function MoneyPrice({
|
|
10
9
|
locale,
|
|
@@ -39,23 +38,19 @@ export async function MoneyPrice({
|
|
|
39
38
|
currency: config.display.currency
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
// 获取激活的支付供应商配置
|
|
43
41
|
const providerConfig = getActiveProviderConfig(config);
|
|
44
42
|
const minPlanFeaturesCount = config.display.minFeaturesCount;
|
|
45
|
-
|
|
46
|
-
// 使用默认计费类型进行静态渲染
|
|
43
|
+
|
|
47
44
|
const defaultBilling = data.billingSwitch.defaultKey;
|
|
48
45
|
const defaultBillingDisplay = data.billingSwitch.options.find(
|
|
49
46
|
(opt: any) => opt.key === defaultBilling
|
|
50
47
|
) || data.billingSwitch.options[0];
|
|
51
48
|
|
|
52
|
-
// 计算特性数量
|
|
53
49
|
const maxFeaturesCount = Math.max(
|
|
54
50
|
...data.plans.map((plan: any) => plan.features?.length || 0),
|
|
55
51
|
minPlanFeaturesCount || 0
|
|
56
52
|
);
|
|
57
53
|
|
|
58
|
-
// 处理卡片高度对齐
|
|
59
54
|
const getFeatureRows = (plan: any) => {
|
|
60
55
|
const features = plan.features || [];
|
|
61
56
|
const filled = [...features];
|
|
@@ -63,7 +58,6 @@ export async function MoneyPrice({
|
|
|
63
58
|
return filled;
|
|
64
59
|
};
|
|
65
60
|
|
|
66
|
-
// 静态价格渲染逻辑
|
|
67
61
|
function renderPrice(plan: any, billingKey = data.billingSwitch.defaultKey) {
|
|
68
62
|
const productConfig = providerConfig.products[plan.key as 'free' | 'pro' | 'ultra'];
|
|
69
63
|
const pricing = productConfig.plans[billingKey as 'monthly' | 'yearly'];
|
|
@@ -72,7 +66,6 @@ export async function MoneyPrice({
|
|
|
72
66
|
) || defaultBillingDisplay;
|
|
73
67
|
const billingSubTitle = currentBillingDisplay?.subTitle || '';
|
|
74
68
|
|
|
75
|
-
// 免费计划
|
|
76
69
|
if (pricing.amount === 0) {
|
|
77
70
|
return (
|
|
78
71
|
<div className="flex flex-col items-start w-full" data-price-container={plan.key}>
|
|
@@ -92,7 +85,6 @@ export async function MoneyPrice({
|
|
|
92
85
|
);
|
|
93
86
|
}
|
|
94
87
|
|
|
95
|
-
// 付费计划
|
|
96
88
|
const hasDiscount = pricing.discountPercent && pricing.discountPercent > 0;
|
|
97
89
|
const unit = currentBillingDisplay.unit || '';
|
|
98
90
|
let discountText = '';
|
|
@@ -140,7 +132,6 @@ export async function MoneyPrice({
|
|
|
140
132
|
|
|
141
133
|
return (
|
|
142
134
|
<section id="money-pricing" className={cn("px-4 py-10 md:px-16 md:py-16 mx-auto max-w-7xl scroll-mt-10", sectionClassName)}>
|
|
143
|
-
{/* 标题和副标题 */}
|
|
144
135
|
<h2 className="text-3xl md:text-4xl font-bold text-center mb-3">
|
|
145
136
|
{data.title}
|
|
146
137
|
</h2>
|
|
@@ -148,7 +139,6 @@ export async function MoneyPrice({
|
|
|
148
139
|
{data.subtitle}
|
|
149
140
|
</p>
|
|
150
141
|
|
|
151
|
-
{/* 计费切换按钮 */}
|
|
152
142
|
<div className="flex flex-col items-center">
|
|
153
143
|
<div className="flex items-center relative mb-3">
|
|
154
144
|
<div className="flex bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded-full p-1" data-billing-switch>
|
|
@@ -179,12 +169,10 @@ export async function MoneyPrice({
|
|
|
179
169
|
</div>
|
|
180
170
|
</div>
|
|
181
171
|
|
|
182
|
-
{/* 折扣信息 - 默认计费的静态渲染 */}
|
|
183
172
|
<div className="h-8 flex items-center justify-center mb-3" data-discount-info>
|
|
184
173
|
{(() => {
|
|
185
174
|
const opt = data.billingSwitch.options.find((opt: any) => opt.key === data.billingSwitch.defaultKey);
|
|
186
175
|
|
|
187
|
-
// 检查默认计费类型是否有折扣
|
|
188
176
|
let hasDiscount = false;
|
|
189
177
|
let discountPercent = 0;
|
|
190
178
|
|
|
@@ -211,7 +199,6 @@ export async function MoneyPrice({
|
|
|
211
199
|
</div>
|
|
212
200
|
</div>
|
|
213
201
|
|
|
214
|
-
{/* 价格卡片区域 */}
|
|
215
202
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
216
203
|
{data.plans.map((plan: any, _idx: number) => (
|
|
217
204
|
<div
|
|
@@ -224,7 +211,6 @@ export async function MoneyPrice({
|
|
|
224
211
|
)}
|
|
225
212
|
style={{ minHeight: maxFeaturesCount*100 }}
|
|
226
213
|
>
|
|
227
|
-
{/* 标题和标签 */}
|
|
228
214
|
<div className="flex items-center gap-2 mb-2">
|
|
229
215
|
<span className="text-xl font-bold text-gray-900 dark:text-gray-100">{plan.title}</span>
|
|
230
216
|
{plan.titleTags && plan.titleTags.map((tag: string, i: number) => (
|
|
@@ -234,14 +220,11 @@ export async function MoneyPrice({
|
|
|
234
220
|
))}
|
|
235
221
|
</div>
|
|
236
222
|
|
|
237
|
-
{/* 价格和单位/折扣 */}
|
|
238
223
|
{renderPrice(plan)}
|
|
239
224
|
|
|
240
|
-
{/* 特性列表 */}
|
|
241
225
|
<ul className="flex-1 mb-6 mt-4">
|
|
242
226
|
{getFeatureRows(plan).map((feature: any, i: number) => (
|
|
243
227
|
<li key={i} className="flex items-center gap-2 mb-2 min-h-[28px]" data-feature-item={`${plan.key}-${i}`}>
|
|
244
|
-
{/* 图标 */}
|
|
245
228
|
{feature ? (
|
|
246
229
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-200 mr-1">
|
|
247
230
|
{feature.icon ? <span>{feature.icon}</span> : <span className="font-bold">✓</span>}
|
|
@@ -249,13 +232,11 @@ export async function MoneyPrice({
|
|
|
249
232
|
) : (
|
|
250
233
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full mr-1"> </span>
|
|
251
234
|
)}
|
|
252
|
-
{/* 标签 */}
|
|
253
235
|
{feature && feature.tag && (
|
|
254
236
|
<span className="px-1 py-0.5 text-[6px] rounded bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 font-semibold align-middle">
|
|
255
237
|
{feature.tag}
|
|
256
238
|
</span>
|
|
257
239
|
)}
|
|
258
|
-
{/* 描述 + 提示 */}
|
|
259
240
|
{feature ? (
|
|
260
241
|
<span className="relative group cursor-pointer text-sm text-gray-800 dark:text-gray-200">
|
|
261
242
|
{feature.description}
|
|
@@ -279,23 +260,15 @@ export async function MoneyPrice({
|
|
|
279
260
|
))}
|
|
280
261
|
</ul>
|
|
281
262
|
|
|
282
|
-
{/* 占位符,确保卡片高度一致 */}
|
|
283
263
|
<div className="flex-1" />
|
|
284
264
|
|
|
285
|
-
{
|
|
286
|
-
|
|
287
|
-
<GradientButton
|
|
288
|
-
title={data.buttonTexts.getStarted}
|
|
289
|
-
disabled={true}
|
|
290
|
-
align="center"
|
|
291
|
-
className="w-full"
|
|
292
|
-
/>
|
|
265
|
+
<div data-button-placeholder={plan.key} className="w-full">
|
|
266
|
+
{/* MoneyPriceInteractive will render the button here */}
|
|
293
267
|
</div>
|
|
294
268
|
</div>
|
|
295
269
|
))}
|
|
296
270
|
</div>
|
|
297
271
|
|
|
298
|
-
{/* 客户端增强组件 */}
|
|
299
272
|
<MoneyPriceInteractive
|
|
300
273
|
data={data}
|
|
301
274
|
config={config}
|
package/src/main/server.ts
CHANGED
|
@@ -12,10 +12,9 @@ export * from './price-plan';
|
|
|
12
12
|
// Money Price Server Component and Types
|
|
13
13
|
export { MoneyPrice } from './money-price/money-price';
|
|
14
14
|
export {
|
|
15
|
-
moneyPriceConfig,
|
|
16
15
|
getActiveProviderConfig,
|
|
17
16
|
getProductPricing
|
|
18
|
-
} from './money-price/money-price-config';
|
|
17
|
+
} from './money-price/money-price-config-util';
|
|
19
18
|
|
|
20
19
|
// Money Price Types (shared between server and client)
|
|
21
20
|
export type {
|