feat: 订阅支付弹窗添加会员协议确认功能
- SubscriptionContentNew.tsx: 添加协议确认 Checkbox 和链接 - SubscriptionContent.js: 同步添加协议确认功能(旧版组件) - 根据套餐类型动态显示 PRO/MAX 会员服务协议链接 - 未勾选协议时点击支付按钮显示 Toast 提示 - 切换套餐时自动重置协议勾选状态 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,8 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
InputRightElement,
|
InputRightElement,
|
||||||
|
Checkbox,
|
||||||
|
Link as ChakraLink,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
@@ -55,6 +57,12 @@ import {
|
|||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import { getApiBase } from '../../utils/apiConfig';
|
import { getApiBase } from '../../utils/apiConfig';
|
||||||
|
|
||||||
|
// 会员协议 URL 配置
|
||||||
|
const AGREEMENT_URLS = {
|
||||||
|
pro: 'https://valuefrontier.cn/htmls/pro-member-agreement.html',
|
||||||
|
max: 'https://valuefrontier.cn/htmls/max-member-agreement.html',
|
||||||
|
};
|
||||||
|
|
||||||
export default function SubscriptionContent() {
|
export default function SubscriptionContent() {
|
||||||
// Auth context
|
// Auth context
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@@ -97,6 +105,9 @@ export default function SubscriptionContent() {
|
|||||||
const [validatingPromo, setValidatingPromo] = useState(false);
|
const [validatingPromo, setValidatingPromo] = useState(false);
|
||||||
const [priceInfo, setPriceInfo] = useState(null); // 价格信息(包含升级计算)
|
const [priceInfo, setPriceInfo] = useState(null); // 价格信息(包含升级计算)
|
||||||
|
|
||||||
|
// 会员协议确认状态
|
||||||
|
const [agreementChecked, setAgreementChecked] = useState(false);
|
||||||
|
|
||||||
// 加载订阅套餐数据
|
// 加载订阅套餐数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSubscriptionPlans();
|
fetchSubscriptionPlans();
|
||||||
@@ -286,6 +297,9 @@ export default function SubscriptionContent() {
|
|||||||
|
|
||||||
setSelectedPlan(plan);
|
setSelectedPlan(plan);
|
||||||
|
|
||||||
|
// 切换套餐时重置协议勾选状态
|
||||||
|
setAgreementChecked(false);
|
||||||
|
|
||||||
// 计算价格(包含升级判断)
|
// 计算价格(包含升级判断)
|
||||||
await calculatePrice(plan, selectedCycle, promoCodeApplied ? promoCode : null);
|
await calculatePrice(plan, selectedCycle, promoCodeApplied ? promoCode : null);
|
||||||
|
|
||||||
@@ -1603,11 +1617,45 @@ export default function SubscriptionContent() {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 会员协议确认 */}
|
||||||
|
<Checkbox
|
||||||
|
isChecked={agreementChecked}
|
||||||
|
onChange={(e) => setAgreementChecked(e.target.checked)}
|
||||||
|
colorScheme="green"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<Text fontSize="sm" color={secondaryText}>
|
||||||
|
我已阅读并同意
|
||||||
|
<ChakraLink
|
||||||
|
href={AGREEMENT_URLS[selectedPlan?.name?.toLowerCase()] || AGREEMENT_URLS.pro}
|
||||||
|
isExternal
|
||||||
|
color="blue.500"
|
||||||
|
textDecoration="underline"
|
||||||
|
mx={1}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
《{selectedPlan?.name?.toLowerCase() === 'max' ? 'MAX' : 'PRO'}会员服务协议》
|
||||||
|
</ChakraLink>
|
||||||
|
</Text>
|
||||||
|
</Checkbox>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorScheme="green"
|
colorScheme="green"
|
||||||
size="lg"
|
size="lg"
|
||||||
leftIcon={<Icon as={FaWeixin} />}
|
leftIcon={<Icon as={FaWeixin} />}
|
||||||
onClick={handleCreateOrder}
|
onClick={() => {
|
||||||
|
if (!agreementChecked) {
|
||||||
|
toast({
|
||||||
|
title: '请先阅读并同意会员服务协议',
|
||||||
|
status: 'warning',
|
||||||
|
duration: 3000,
|
||||||
|
isClosable: true,
|
||||||
|
position: 'top',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleCreateOrder();
|
||||||
|
}}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
loadingText="创建订单中..."
|
loadingText="创建订单中..."
|
||||||
isDisabled={!selectedPlan}
|
isDisabled={!selectedPlan}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
Container,
|
Container,
|
||||||
useBreakpointValue,
|
useBreakpointValue,
|
||||||
|
Checkbox,
|
||||||
|
Link as ChakraLink,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaWeixin,
|
FaWeixin,
|
||||||
@@ -46,6 +48,12 @@ import { useSubscriptionEvents } from '../../hooks/useSubscriptionEvents';
|
|||||||
import { subscriptionConfig, themeColors } from '../../views/Pages/Account/subscription-content';
|
import { subscriptionConfig, themeColors } from '../../views/Pages/Account/subscription-content';
|
||||||
import { getApiBase } from '../../utils/apiConfig';
|
import { getApiBase } from '../../utils/apiConfig';
|
||||||
|
|
||||||
|
// 会员协议 URL 配置
|
||||||
|
const AGREEMENT_URLS = {
|
||||||
|
pro: 'https://valuefrontier.cn/htmls/pro-member-agreement.html',
|
||||||
|
max: 'https://valuefrontier.cn/htmls/max-member-agreement.html',
|
||||||
|
};
|
||||||
|
|
||||||
// 计费周期选择器组件 - 移动端垂直布局(年付在上),桌面端水平布局
|
// 计费周期选择器组件 - 移动端垂直布局(年付在上),桌面端水平布局
|
||||||
interface CycleSelectorProps {
|
interface CycleSelectorProps {
|
||||||
options: any[];
|
options: any[];
|
||||||
@@ -154,6 +162,9 @@ export default function SubscriptionContentNew() {
|
|||||||
|
|
||||||
const [openFaqIndex, setOpenFaqIndex] = useState(null);
|
const [openFaqIndex, setOpenFaqIndex] = useState(null);
|
||||||
|
|
||||||
|
// 会员协议确认状态
|
||||||
|
const [agreementChecked, setAgreementChecked] = useState(false);
|
||||||
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
@@ -418,6 +429,10 @@ export default function SubscriptionContentNew() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
setSelectedPlan(plan);
|
setSelectedPlan(plan);
|
||||||
|
|
||||||
|
// 切换套餐时重置协议勾选状态
|
||||||
|
setAgreementChecked(false);
|
||||||
|
|
||||||
await calculatePrice(plan, selectedCycle, promoCodeApplied ? promoCode : null);
|
await calculatePrice(plan, selectedCycle, promoCodeApplied ? promoCode : null);
|
||||||
onOpen();
|
onOpen();
|
||||||
};
|
};
|
||||||
@@ -1607,6 +1622,28 @@ export default function SubscriptionContentNew() {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 会员协议确认 */}
|
||||||
|
<Checkbox
|
||||||
|
isChecked={agreementChecked}
|
||||||
|
onChange={(e) => setAgreementChecked(e.target.checked)}
|
||||||
|
colorScheme="green"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<Text fontSize="sm" color="rgba(255, 255, 255, 0.7)">
|
||||||
|
我已阅读并同意
|
||||||
|
<ChakraLink
|
||||||
|
href={AGREEMENT_URLS[(selectedPlan as any)?.name?.toLowerCase()] || AGREEMENT_URLS.pro}
|
||||||
|
isExternal
|
||||||
|
color="#3182CE"
|
||||||
|
textDecoration="underline"
|
||||||
|
mx={1}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
《{(selectedPlan as any)?.name?.toLowerCase() === 'max' ? 'MAX' : 'PRO'}会员服务协议》
|
||||||
|
</ChakraLink>
|
||||||
|
</Text>
|
||||||
|
</Checkbox>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
w="full"
|
w="full"
|
||||||
size="lg"
|
size="lg"
|
||||||
@@ -1615,7 +1652,19 @@ export default function SubscriptionContentNew() {
|
|||||||
: 'linear-gradient(135deg, #07C160, #059048)'}
|
: 'linear-gradient(135deg, #07C160, #059048)'}
|
||||||
color="white"
|
color="white"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
onClick={handleCreatePaymentOrder}
|
onClick={() => {
|
||||||
|
if (!agreementChecked) {
|
||||||
|
toast({
|
||||||
|
title: '请先阅读并同意会员服务协议',
|
||||||
|
status: 'warning',
|
||||||
|
duration: 3000,
|
||||||
|
isClosable: true,
|
||||||
|
position: 'top',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleCreatePaymentOrder();
|
||||||
|
}}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
isDisabled={!selectedPlan}
|
isDisabled={!selectedPlan}
|
||||||
leftIcon={paymentMethod === 'alipay'
|
leftIcon={paymentMethod === 'alipay'
|
||||||
|
|||||||
Reference in New Issue
Block a user