@windrun-huaiin/third-ui 7.3.2 → 7.3.3
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/main/faq.js +12 -0
- package/dist/main/faq.mjs +12 -0
- package/dist/main/gallery.js +12 -0
- package/dist/main/gallery.mjs +12 -0
- package/dist/main/index.d.ts +2 -0
- package/dist/main/index.js +4 -0
- package/dist/main/index.mjs +2 -0
- package/dist/main/money-price/money-price-button.d.ts +2 -0
- package/dist/main/money-price/money-price-button.js +96 -0
- package/dist/main/money-price/money-price-button.mjs +94 -0
- package/dist/main/money-price/money-price-config.d.ts +8 -0
- package/dist/main/money-price/money-price-config.js +223 -0
- package/dist/main/money-price/money-price-config.mjs +219 -0
- package/dist/main/money-price/money-price-interactive.d.ts +2 -0
- package/dist/main/money-price/money-price-interactive.js +293 -0
- package/dist/main/money-price/money-price-interactive.mjs +291 -0
- package/dist/main/money-price/money-price-types.d.ts +116 -0
- package/dist/main/money-price/money-price-types.js +14 -0
- package/dist/main/money-price/money-price-types.mjs +14 -0
- package/dist/main/money-price/money-price.d.ts +2 -0
- package/dist/main/money-price/money-price.js +93 -0
- package/dist/main/money-price/money-price.mjs +91 -0
- package/dist/main/price-plan.js +13 -0
- package/dist/main/price-plan.mjs +13 -0
- package/dist/main/server.d.ts +4 -0
- package/dist/main/server.js +11 -0
- package/dist/main/server.mjs +3 -0
- package/dist/node_modules/.pnpm/cose-base@1.0.3/node_modules/cose-base/cose-base.js +1 -1
- package/dist/node_modules/.pnpm/cose-base@2.2.0/node_modules/cose-base/cose-base.js +1 -1
- package/dist/node_modules/.pnpm/layout-base@1.0.2/node_modules/layout-base/layout-base.js +1 -1
- package/dist/node_modules/.pnpm/layout-base@2.0.1/node_modules/layout-base/layout-base.js +1 -1
- package/package.json +1 -1
- package/src/main/index.ts +5 -1
- package/src/main/money-price/money-price-button.tsx +105 -0
- package/src/main/money-price/money-price-config.ts +229 -0
- package/src/main/money-price/money-price-interactive.tsx +356 -0
- package/src/main/money-price/money-price-types.ts +138 -0
- package/src/main/money-price/money-price.tsx +307 -0
- package/src/main/server.ts +25 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money Price Configuration
|
|
3
|
+
* 价格组件配置文件
|
|
4
|
+
*/
|
|
5
|
+
// 示例配置
|
|
6
|
+
const moneyPriceConfig = {
|
|
7
|
+
paymentProviders: {
|
|
8
|
+
stripe: {
|
|
9
|
+
provider: 'stripe',
|
|
10
|
+
enabled: true,
|
|
11
|
+
products: {
|
|
12
|
+
free: {
|
|
13
|
+
key: 'free',
|
|
14
|
+
name: 'free', // Just a key, actual display name comes from translation
|
|
15
|
+
plans: {
|
|
16
|
+
monthly: {
|
|
17
|
+
priceId: 'free',
|
|
18
|
+
amount: 0,
|
|
19
|
+
currency: 'usd',
|
|
20
|
+
credits: 0
|
|
21
|
+
},
|
|
22
|
+
yearly: {
|
|
23
|
+
priceId: 'free',
|
|
24
|
+
amount: 0,
|
|
25
|
+
currency: 'usd',
|
|
26
|
+
credits: 0
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
pro: {
|
|
31
|
+
key: 'pro',
|
|
32
|
+
name: 'pro', // Just a key, actual display name comes from translation
|
|
33
|
+
plans: {
|
|
34
|
+
monthly: {
|
|
35
|
+
priceId: process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID || 'price_pro_monthly',
|
|
36
|
+
amount: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_AMOUNT) || 10,
|
|
37
|
+
currency: process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_CURRENCY || 'usd',
|
|
38
|
+
credits: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_MONTHLY_CREDITS) || 100
|
|
39
|
+
},
|
|
40
|
+
yearly: {
|
|
41
|
+
priceId: process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_PRICE_ID || 'price_pro_yearly',
|
|
42
|
+
amount: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_AMOUNT) || 96,
|
|
43
|
+
originalAmount: 120,
|
|
44
|
+
discountPercent: 20,
|
|
45
|
+
currency: process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_CURRENCY || 'usd',
|
|
46
|
+
credits: Number(process.env.NEXT_PUBLIC_STRIPE_PRO_YEARLY_CREDITS) || 1200
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
ultra: {
|
|
51
|
+
key: 'ultra',
|
|
52
|
+
name: 'ultra', // Just a key, actual display name comes from translation
|
|
53
|
+
plans: {
|
|
54
|
+
monthly: {
|
|
55
|
+
priceId: process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_PRICE_ID || 'price_ultra_monthly',
|
|
56
|
+
amount: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_AMOUNT) || 20,
|
|
57
|
+
currency: process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_CURRENCY || 'usd',
|
|
58
|
+
credits: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_MONTHLY_CREDITS) || 250
|
|
59
|
+
},
|
|
60
|
+
yearly: {
|
|
61
|
+
priceId: process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_PRICE_ID || 'price_ultra_yearly',
|
|
62
|
+
amount: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_AMOUNT) || 192,
|
|
63
|
+
originalAmount: 240,
|
|
64
|
+
discountPercent: 20,
|
|
65
|
+
currency: process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_CURRENCY || 'usd',
|
|
66
|
+
credits: Number(process.env.NEXT_PUBLIC_STRIPE_ULTRA_YEARLY_CREDITS) || 3000
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
alipay: {
|
|
73
|
+
provider: 'alipay',
|
|
74
|
+
enabled: false,
|
|
75
|
+
products: {
|
|
76
|
+
free: {
|
|
77
|
+
key: 'free',
|
|
78
|
+
name: 'free',
|
|
79
|
+
plans: {
|
|
80
|
+
monthly: {
|
|
81
|
+
priceId: 'free',
|
|
82
|
+
amount: 0,
|
|
83
|
+
currency: 'cny',
|
|
84
|
+
credits: 0
|
|
85
|
+
},
|
|
86
|
+
yearly: {
|
|
87
|
+
priceId: 'free',
|
|
88
|
+
amount: 0,
|
|
89
|
+
currency: 'cny',
|
|
90
|
+
credits: 0
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
pro: {
|
|
95
|
+
key: 'pro',
|
|
96
|
+
name: 'pro',
|
|
97
|
+
plans: {
|
|
98
|
+
monthly: {
|
|
99
|
+
priceId: 'alipay_pro_monthly',
|
|
100
|
+
amount: 70,
|
|
101
|
+
currency: 'cny',
|
|
102
|
+
credits: 100
|
|
103
|
+
},
|
|
104
|
+
yearly: {
|
|
105
|
+
priceId: 'alipay_pro_yearly',
|
|
106
|
+
amount: 672,
|
|
107
|
+
originalAmount: 840,
|
|
108
|
+
discountPercent: 20,
|
|
109
|
+
currency: 'cny',
|
|
110
|
+
credits: 1200
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
ultra: {
|
|
115
|
+
key: 'ultra',
|
|
116
|
+
name: 'ultra',
|
|
117
|
+
plans: {
|
|
118
|
+
monthly: {
|
|
119
|
+
priceId: 'alipay_ultra_monthly',
|
|
120
|
+
amount: 140,
|
|
121
|
+
currency: 'cny',
|
|
122
|
+
credits: 250
|
|
123
|
+
},
|
|
124
|
+
yearly: {
|
|
125
|
+
priceId: 'alipay_ultra_yearly',
|
|
126
|
+
amount: 1344,
|
|
127
|
+
originalAmount: 1680,
|
|
128
|
+
discountPercent: 20,
|
|
129
|
+
currency: 'cny',
|
|
130
|
+
credits: 3000
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
wechat: {
|
|
137
|
+
provider: 'wechat',
|
|
138
|
+
enabled: false,
|
|
139
|
+
products: {
|
|
140
|
+
free: {
|
|
141
|
+
key: 'free',
|
|
142
|
+
name: 'free',
|
|
143
|
+
plans: {
|
|
144
|
+
monthly: {
|
|
145
|
+
priceId: 'free',
|
|
146
|
+
amount: 0,
|
|
147
|
+
currency: 'cny',
|
|
148
|
+
credits: 0
|
|
149
|
+
},
|
|
150
|
+
yearly: {
|
|
151
|
+
priceId: 'free',
|
|
152
|
+
amount: 0,
|
|
153
|
+
currency: 'cny',
|
|
154
|
+
credits: 0
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
pro: {
|
|
159
|
+
key: 'pro',
|
|
160
|
+
name: 'pro',
|
|
161
|
+
plans: {
|
|
162
|
+
monthly: {
|
|
163
|
+
priceId: 'wechat_pro_monthly',
|
|
164
|
+
amount: 70,
|
|
165
|
+
currency: 'cny',
|
|
166
|
+
credits: 100
|
|
167
|
+
},
|
|
168
|
+
yearly: {
|
|
169
|
+
priceId: 'wechat_pro_yearly',
|
|
170
|
+
amount: 672,
|
|
171
|
+
originalAmount: 840,
|
|
172
|
+
discountPercent: 20,
|
|
173
|
+
currency: 'cny',
|
|
174
|
+
credits: 1200
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
ultra: {
|
|
179
|
+
key: 'ultra',
|
|
180
|
+
name: 'ultra',
|
|
181
|
+
plans: {
|
|
182
|
+
monthly: {
|
|
183
|
+
priceId: 'wechat_ultra_monthly',
|
|
184
|
+
amount: 140,
|
|
185
|
+
currency: 'cny',
|
|
186
|
+
credits: 250
|
|
187
|
+
},
|
|
188
|
+
yearly: {
|
|
189
|
+
priceId: 'wechat_ultra_yearly',
|
|
190
|
+
amount: 1344,
|
|
191
|
+
originalAmount: 1680,
|
|
192
|
+
discountPercent: 20,
|
|
193
|
+
currency: 'cny',
|
|
194
|
+
credits: 3000
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
activeProvider: process.env.NEXT_PUBLIC_ACTIVE_PAYMENT_PROVIDER || 'stripe',
|
|
202
|
+
display: {
|
|
203
|
+
currency: '$',
|
|
204
|
+
locale: 'en',
|
|
205
|
+
minFeaturesCount: 4
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
// 辅助函数:获取当前激活的支付供应商配置
|
|
209
|
+
function getActiveProviderConfig(config) {
|
|
210
|
+
const provider = config.activeProvider;
|
|
211
|
+
return config.paymentProviders[provider];
|
|
212
|
+
}
|
|
213
|
+
// 辅助函数:获取特定产品的价格信息
|
|
214
|
+
function getProductPricing(productKey, billingType, provider, config) {
|
|
215
|
+
const providerConfig = config.paymentProviders[provider];
|
|
216
|
+
return providerConfig.products[productKey].plans[billingType];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export { getActiveProviderConfig, getProductPricing, moneyPriceConfig };
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var tslib_es6 = require('../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.js');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var nextjs = require('@clerk/nextjs');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var fingerprintProvider = require('../../clerk/fingerprint/fingerprint-provider.js');
|
|
9
|
+
var utils = require('@windrun-huaiin/lib/utils');
|
|
10
|
+
var navigation = require('next/navigation');
|
|
11
|
+
var client = require('react-dom/client');
|
|
12
|
+
var moneyPriceButton = require('./money-price-button.js');
|
|
13
|
+
var moneyPriceConfig = require('./money-price-config.js');
|
|
14
|
+
var moneyPriceTypes = require('./money-price-types.js');
|
|
15
|
+
|
|
16
|
+
function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath }) {
|
|
17
|
+
var _a, _b, _c, _d;
|
|
18
|
+
const fingerprintContext = fingerprintProvider.useFingerprintContextSafe();
|
|
19
|
+
const { redirectToSignIn } = nextjs.useClerk();
|
|
20
|
+
const router = navigation.useRouter();
|
|
21
|
+
const [billingType, setBillingType] = React.useState(data.billingSwitch.defaultKey);
|
|
22
|
+
const [isProcessing, setIsProcessing] = React.useState(false);
|
|
23
|
+
const [tooltip, setTooltip] = React.useState({ show: false, content: '', x: 0, y: 0 });
|
|
24
|
+
// 确定用户状态
|
|
25
|
+
const getUserState = () => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
if (!fingerprintContext)
|
|
28
|
+
return moneyPriceTypes.UserState.Anonymous;
|
|
29
|
+
const { xUser, xSubscription } = fingerprintContext;
|
|
30
|
+
if (!(xUser === null || xUser === void 0 ? void 0 : xUser.clerkUserId))
|
|
31
|
+
return moneyPriceTypes.UserState.Anonymous;
|
|
32
|
+
if (!(xSubscription === null || xSubscription === void 0 ? void 0 : xSubscription.status) || xSubscription.status === 'free')
|
|
33
|
+
return moneyPriceTypes.UserState.FreeUser;
|
|
34
|
+
if ((_a = xSubscription.priceName) === null || _a === void 0 ? void 0 : _a.includes('Pro'))
|
|
35
|
+
return moneyPriceTypes.UserState.ProUser;
|
|
36
|
+
if ((_b = xSubscription.priceName) === null || _b === void 0 ? void 0 : _b.includes('Ultra'))
|
|
37
|
+
return moneyPriceTypes.UserState.UltraUser;
|
|
38
|
+
return moneyPriceTypes.UserState.FreeUser;
|
|
39
|
+
};
|
|
40
|
+
const userContext = {
|
|
41
|
+
isAuthenticated: !!((_a = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xUser) === null || _a === void 0 ? void 0 : _a.clerkUserId),
|
|
42
|
+
subscriptionStatus: getUserState(),
|
|
43
|
+
subscriptionType: ((_c = (_b = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _b === void 0 ? void 0 : _b.priceId) === null || _c === void 0 ? void 0 : _c.includes('yearly')) ? 'yearly' : 'monthly',
|
|
44
|
+
subscriptionEndDate: (_d = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _d === void 0 ? void 0 : _d.subPeriodEnd
|
|
45
|
+
};
|
|
46
|
+
// 处理登录
|
|
47
|
+
const handleLogin = () => {
|
|
48
|
+
if (signInPath) {
|
|
49
|
+
router.push(signInPath);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
redirectToSignIn();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
// 处理升级
|
|
56
|
+
const handleUpgrade = (plan, billingType) => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
// 如果没有配置 API 端点,跳转到首页
|
|
58
|
+
if (!upgradeApiEndpoint) {
|
|
59
|
+
router.push('/');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
setIsProcessing(true);
|
|
63
|
+
try {
|
|
64
|
+
// 获取价格配置
|
|
65
|
+
const pricing = moneyPriceConfig.getProductPricing(plan, billingType, config.activeProvider, config);
|
|
66
|
+
// 调用 API 创建支付会话
|
|
67
|
+
const response = yield fetch(upgradeApiEndpoint, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
priceId: pricing.priceId,
|
|
74
|
+
plan: plan,
|
|
75
|
+
billingType: billingType,
|
|
76
|
+
provider: config.activeProvider
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
const result = yield response.json();
|
|
80
|
+
if (result.success && result.checkoutUrl) {
|
|
81
|
+
// 跳转到支付页面
|
|
82
|
+
window.location.href = result.checkoutUrl;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.error('Failed to create checkout session:', result.error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('Error during upgrade:', error);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
setIsProcessing(false);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
// 更新价格显示
|
|
96
|
+
const updatePriceDisplay = (newBillingType) => {
|
|
97
|
+
const providerConfig = moneyPriceConfig.getActiveProviderConfig(config);
|
|
98
|
+
data.plans.forEach((plan) => {
|
|
99
|
+
const productConfig = providerConfig.products[plan.key];
|
|
100
|
+
const pricing = productConfig.plans[newBillingType];
|
|
101
|
+
// 更新价格值
|
|
102
|
+
const priceValueElement = document.querySelector(`[data-price-value="${plan.key}"]`);
|
|
103
|
+
if (priceValueElement) {
|
|
104
|
+
if (pricing.amount === 0) {
|
|
105
|
+
priceValueElement.textContent = 'Free';
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
priceValueElement.textContent = `${data.currency}${pricing.amount}`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 更新单位
|
|
112
|
+
const priceUnitElement = document.querySelector(`[data-price-unit="${plan.key}"]`);
|
|
113
|
+
if (priceUnitElement) {
|
|
114
|
+
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
115
|
+
priceUnitElement.textContent = (billingOption === null || billingOption === void 0 ? void 0 : billingOption.unit) || '/month';
|
|
116
|
+
}
|
|
117
|
+
// 更新原价和折扣
|
|
118
|
+
const priceOriginalElement = document.querySelector(`[data-price-original="${plan.key}"]`);
|
|
119
|
+
const priceDiscountElement = document.querySelector(`[data-price-discount="${plan.key}"]`);
|
|
120
|
+
if (pricing.originalAmount && pricing.discountPercent) {
|
|
121
|
+
if (priceOriginalElement) {
|
|
122
|
+
priceOriginalElement.style.display = 'inline';
|
|
123
|
+
priceOriginalElement.textContent = `${data.currency}${pricing.originalAmount}`;
|
|
124
|
+
}
|
|
125
|
+
if (priceDiscountElement) {
|
|
126
|
+
priceDiscountElement.style.display = 'inline';
|
|
127
|
+
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
128
|
+
if (billingOption === null || billingOption === void 0 ? void 0 : billingOption.discountText) {
|
|
129
|
+
priceDiscountElement.textContent = billingOption.discountText.replace('{percent}', String(pricing.discountPercent));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
if (priceOriginalElement)
|
|
135
|
+
priceOriginalElement.style.display = 'none';
|
|
136
|
+
if (priceDiscountElement)
|
|
137
|
+
priceDiscountElement.style.display = 'none';
|
|
138
|
+
}
|
|
139
|
+
// 更新副标题
|
|
140
|
+
const priceSubtitleElement = document.querySelector(`[data-price-subtitle="${plan.key}"]`);
|
|
141
|
+
if (priceSubtitleElement && plan.showBillingSubTitle !== false) {
|
|
142
|
+
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
143
|
+
priceSubtitleElement.textContent = (billingOption === null || billingOption === void 0 ? void 0 : billingOption.subTitle) || '';
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
// 更新按钮样式
|
|
148
|
+
const updateButtonStyles = (newBillingType) => {
|
|
149
|
+
const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
|
|
150
|
+
const yearlyButton = document.querySelector('[data-billing-button="yearly"]');
|
|
151
|
+
const activeClasses = 'text-white bg-gradient-to-r from-purple-400 to-pink-500 hover:from-purple-500 hover:to-pink-600 dark:from-purple-500 dark:to-pink-600 dark:hover:from-purple-600 rounded-full shadow-sm';
|
|
152
|
+
const inactiveClasses = 'text-gray-800 dark:text-gray-200 hover:text-gray-900 dark:hover:text-gray-100 rounded-full';
|
|
153
|
+
if (monthlyButton && yearlyButton) {
|
|
154
|
+
if (newBillingType === 'monthly') {
|
|
155
|
+
monthlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', activeClasses);
|
|
156
|
+
yearlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', inactiveClasses);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
monthlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', inactiveClasses);
|
|
160
|
+
yearlyButton.className = utils.cn('min-w-[120px] px-6 py-2 font-medium transition text-lg relative', activeClasses);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
// 更新折扣信息
|
|
165
|
+
const updateDiscountInfo = (newBillingType) => {
|
|
166
|
+
const discountInfoElement = document.querySelector('[data-discount-info]');
|
|
167
|
+
if (!discountInfoElement)
|
|
168
|
+
return;
|
|
169
|
+
const billingOption = data.billingSwitch.options.find(opt => opt.key === newBillingType);
|
|
170
|
+
const providerConfig = moneyPriceConfig.getActiveProviderConfig(config);
|
|
171
|
+
// 检查是否有折扣
|
|
172
|
+
let hasDiscount = false;
|
|
173
|
+
let discountPercent = 0;
|
|
174
|
+
['pro', 'ultra'].forEach(planKey => {
|
|
175
|
+
const product = providerConfig.products[planKey];
|
|
176
|
+
const pricing = product.plans[newBillingType];
|
|
177
|
+
if (pricing.discountPercent) {
|
|
178
|
+
hasDiscount = true;
|
|
179
|
+
discountPercent = pricing.discountPercent;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
// 清空内容
|
|
183
|
+
discountInfoElement.innerHTML = '';
|
|
184
|
+
if (hasDiscount && (billingOption === null || billingOption === void 0 ? void 0 : billingOption.discountText)) {
|
|
185
|
+
const discountBadge = document.createElement('span');
|
|
186
|
+
discountBadge.className = 'px-2 py-1 text-xs rounded bg-yellow-100 text-yellow-800 font-semibold align-middle text-center inline-flex items-center justify-center whitespace-nowrap';
|
|
187
|
+
discountBadge.textContent = billingOption.discountText.replace('{percent}', String(discountPercent));
|
|
188
|
+
discountInfoElement.appendChild(discountBadge);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
// 动态替换按钮
|
|
192
|
+
React.useEffect(() => {
|
|
193
|
+
data.plans.forEach((plan) => {
|
|
194
|
+
const placeholder = document.querySelector(`[data-button-placeholder="${plan.key}"]`);
|
|
195
|
+
if (placeholder) {
|
|
196
|
+
const root = client.createRoot(placeholder);
|
|
197
|
+
root.render(jsxRuntime.jsx(moneyPriceButton.MoneyPriceButton, { planKey: plan.key, userContext: userContext, billingType: billingType, onLogin: handleLogin, onUpgrade: handleUpgrade, texts: data.buttonTexts, isProcessing: isProcessing }));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}, [userContext, billingType, isProcessing]);
|
|
201
|
+
// 处理月付/年付切换和 tooltip 功能
|
|
202
|
+
React.useEffect(() => {
|
|
203
|
+
const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
|
|
204
|
+
const yearlyButton = document.querySelector('[data-billing-button="yearly"]');
|
|
205
|
+
const handleMonthlyClick = () => {
|
|
206
|
+
setBillingType('monthly');
|
|
207
|
+
updatePriceDisplay('monthly');
|
|
208
|
+
updateButtonStyles('monthly');
|
|
209
|
+
updateDiscountInfo('monthly');
|
|
210
|
+
};
|
|
211
|
+
const handleYearlyClick = () => {
|
|
212
|
+
setBillingType('yearly');
|
|
213
|
+
updatePriceDisplay('yearly');
|
|
214
|
+
updateButtonStyles('yearly');
|
|
215
|
+
updateDiscountInfo('yearly');
|
|
216
|
+
};
|
|
217
|
+
if (monthlyButton) {
|
|
218
|
+
monthlyButton.addEventListener('click', handleMonthlyClick);
|
|
219
|
+
}
|
|
220
|
+
if (yearlyButton) {
|
|
221
|
+
yearlyButton.addEventListener('click', handleYearlyClick);
|
|
222
|
+
}
|
|
223
|
+
// 添加 tooltip 功能
|
|
224
|
+
const tooltipHandlers = [];
|
|
225
|
+
data.plans.forEach((plan) => {
|
|
226
|
+
var _a;
|
|
227
|
+
(_a = plan.features) === null || _a === void 0 ? void 0 : _a.forEach((feature, i) => {
|
|
228
|
+
if (feature === null || feature === void 0 ? void 0 : feature.tooltip) {
|
|
229
|
+
const tooltipTrigger = document.querySelector(`[data-tooltip-trigger="${plan.key}-${i}"]`);
|
|
230
|
+
if (tooltipTrigger) {
|
|
231
|
+
const handlers = {
|
|
232
|
+
mouseenter: (e) => {
|
|
233
|
+
setTooltip({
|
|
234
|
+
show: true,
|
|
235
|
+
content: feature.tooltip,
|
|
236
|
+
x: e.clientX,
|
|
237
|
+
y: e.clientY
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
mousemove: (e) => {
|
|
241
|
+
setTooltip(prev => (Object.assign(Object.assign({}, prev), { x: e.clientX, y: e.clientY })));
|
|
242
|
+
},
|
|
243
|
+
mouseleave: () => {
|
|
244
|
+
setTooltip(prev => (Object.assign(Object.assign({}, prev), { show: false })));
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
tooltipTrigger.addEventListener('mouseenter', handlers.mouseenter);
|
|
248
|
+
tooltipTrigger.addEventListener('mousemove', handlers.mousemove);
|
|
249
|
+
tooltipTrigger.addEventListener('mouseleave', handlers.mouseleave);
|
|
250
|
+
tooltipHandlers.push({ element: tooltipTrigger, handlers });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
// 初始化价格显示
|
|
256
|
+
updatePriceDisplay(billingType);
|
|
257
|
+
updateDiscountInfo(billingType);
|
|
258
|
+
// 清理
|
|
259
|
+
return () => {
|
|
260
|
+
if (monthlyButton) {
|
|
261
|
+
monthlyButton.removeEventListener('click', handleMonthlyClick);
|
|
262
|
+
}
|
|
263
|
+
if (yearlyButton) {
|
|
264
|
+
yearlyButton.removeEventListener('click', handleYearlyClick);
|
|
265
|
+
}
|
|
266
|
+
// 清理 tooltip 事件监听器
|
|
267
|
+
tooltipHandlers.forEach(({ element, handlers }) => {
|
|
268
|
+
element.removeEventListener('mouseenter', handlers.mouseenter);
|
|
269
|
+
element.removeEventListener('mousemove', handlers.mousemove);
|
|
270
|
+
element.removeEventListener('mouseleave', handlers.mouseleave);
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
}, [data]);
|
|
274
|
+
// Tooltip 组件
|
|
275
|
+
const Tooltip = ({ show, content, x, y }) => {
|
|
276
|
+
if (!show)
|
|
277
|
+
return null;
|
|
278
|
+
const style = {
|
|
279
|
+
position: 'fixed',
|
|
280
|
+
left: Math.max(8, x),
|
|
281
|
+
top: Math.max(8, y),
|
|
282
|
+
zIndex: 9999,
|
|
283
|
+
maxWidth: 200,
|
|
284
|
+
transform: 'translateY(-50%)',
|
|
285
|
+
pointerEvents: 'none',
|
|
286
|
+
whiteSpace: 'pre-line',
|
|
287
|
+
};
|
|
288
|
+
return (jsxRuntime.jsx("div", { style: style, className: "bg-gray-700 dark:bg-gray-200 text-gray-100 dark:text-gray-800 text-xs leading-relaxed px-3 py-2 rounded-lg shadow-lg border border-gray-300 dark:border-gray-600 backdrop-blur-sm", children: content }));
|
|
289
|
+
};
|
|
290
|
+
return jsxRuntime.jsx(Tooltip, Object.assign({}, tooltip));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
exports.MoneyPriceInteractive = MoneyPriceInteractive;
|