优惠码Bug修复
This commit is contained in:
340
CLAUDE.md
340
CLAUDE.md
@@ -4,24 +4,24 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
This is a hybrid React dashboard application with a Flask/Python backend for financial/trading analysis. Built on the Argon Dashboard Chakra PRO template with extensive customization.
|
Hybrid React dashboard for financial/trading analysis with Flask backend. Built on Argon Dashboard Chakra PRO template.
|
||||||
|
|
||||||
### Frontend (React + Chakra UI)
|
### Tech Stack
|
||||||
- **Framework**: React 18.3.1 with Chakra UI 2.8.2
|
|
||||||
- **State Management**: Redux Toolkit (@reduxjs/toolkit)
|
|
||||||
- **Routing**: React Router DOM v6 with lazy loading for code splitting
|
|
||||||
- **Styling**: Tailwind CSS + custom Chakra theme
|
|
||||||
- **Build Tool**: CRACO (Create React App Configuration Override) with custom webpack optimizations
|
|
||||||
- **Charts**: ApexCharts, ECharts, Recharts, D3
|
|
||||||
- **UI Components**: Ant Design (antd) alongside Chakra UI
|
|
||||||
- **Other Libraries**: Three.js (@react-three), FullCalendar, Leaflet maps
|
|
||||||
|
|
||||||
### Backend (Flask/Python)
|
**Frontend**
|
||||||
- **Framework**: Flask with SQLAlchemy ORM
|
- React 18.3.1 + Chakra UI 2.8.2 + Ant Design
|
||||||
- **Database**: ClickHouse for analytics queries + MySQL/PostgreSQL
|
- Redux Toolkit for state management
|
||||||
- **Real-time**: Flask-SocketIO for WebSocket connections
|
- React Router v6 with React.lazy() code splitting
|
||||||
- **Task Queue**: Celery with Redis for background processing
|
- CRACO build system with aggressive webpack optimization
|
||||||
- **External APIs**: Tencent Cloud SMS, WeChat Pay integration
|
- Charts: ApexCharts, ECharts, Recharts, D3
|
||||||
|
- Additional: Three.js, FullCalendar, Leaflet maps
|
||||||
|
|
||||||
|
**Backend**
|
||||||
|
- Flask + SQLAlchemy ORM
|
||||||
|
- ClickHouse (analytics) + MySQL/PostgreSQL (transactional)
|
||||||
|
- Flask-SocketIO for WebSocket real-time updates
|
||||||
|
- Celery + Redis for background jobs
|
||||||
|
- Tencent Cloud SMS + WeChat Pay integration
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
@@ -46,89 +46,265 @@ npm run reinstall # Clean install (runs clean + install)
|
|||||||
|
|
||||||
### Backend Development
|
### Backend Development
|
||||||
```bash
|
```bash
|
||||||
python app.py # Main Flask server (newer version)
|
python app.py # Main Flask server
|
||||||
python app_2.py # Flask server (appears to be current main)
|
python simulation_background_processor.py # Background task processor for trading simulations
|
||||||
python simulation_background_processor.py # Background data processor for simulations
|
pip install -r requirements.txt # Install Python dependencies
|
||||||
```
|
```
|
||||||
|
|
||||||
### Deployment
|
### Deployment
|
||||||
```bash
|
```bash
|
||||||
npm run deploy # Executes scripts/deploy-from-local.sh
|
npm run deploy # Deploy from local (scripts/deploy-from-local.sh)
|
||||||
npm run deploy:setup # Setup deployment (scripts/setup-deployment.sh)
|
npm run rollback # Rollback to previous version
|
||||||
npm run rollback # Rollback deployment (scripts/rollback-from-local.sh)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Python Dependencies
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Frontend Structure
|
### Application Entry Flow
|
||||||
- **src/App.js** - Main application entry with route definitions (routing moved from src/routes.js)
|
```
|
||||||
- **src/layouts/** - Layout wrappers (Auth, Home, MainLayout)
|
src/index.js
|
||||||
- **src/views/** - Page components (Community, Company, TradingSimulation, etc.)
|
└── src/App.js (root component)
|
||||||
- **src/components/** - Reusable UI components
|
├── AppProviders (src/providers/AppProviders.js)
|
||||||
- **src/contexts/** - React contexts (AuthContext, NotificationContext, IndustryContext)
|
│ ├── ReduxProvider (store from src/store/)
|
||||||
- **src/store/** - Redux store with slices (posthogSlice, etc.)
|
│ ├── ChakraProvider (theme from src/theme/)
|
||||||
- **src/services/** - API service layer
|
│ ├── NotificationProvider (src/contexts/NotificationContext.js)
|
||||||
- **src/theme/** - Chakra UI theme customization
|
│ └── AuthProvider (src/contexts/AuthContext.js)
|
||||||
- **src/mocks/** - MSW (Mock Service Worker) handlers for development
|
├── AppRoutes (src/routes/index.js)
|
||||||
- src/mocks/handlers/ - Request handlers by domain
|
│ ├── MainLayout routes (with navbar/footer)
|
||||||
- src/mocks/data/ - Mock data files
|
│ └── Standalone routes (auth pages, fullscreen views)
|
||||||
- src/mocks/browser.js - MSW browser setup
|
└── GlobalComponents (modal overlays, global UI)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Routing Architecture (Modular Design)
|
||||||
|
Routing is **declarative** and split across multiple files in `src/routes/`:
|
||||||
|
|
||||||
|
- **index.js** - Main router (combines config + renders routes)
|
||||||
|
- **routeConfig.js** - Route definitions (path, component, protection, layout, children)
|
||||||
|
- **lazy-components.js** - React.lazy() imports for code splitting
|
||||||
|
- **homeRoutes.js** - Nested home page routes
|
||||||
|
- **constants/** - Protection modes, layout mappings
|
||||||
|
- **utils/** - Route rendering logic (wrapWithProtection, renderRoute)
|
||||||
|
|
||||||
|
Route protection modes (PROTECTION_MODES):
|
||||||
|
- `PUBLIC` - No authentication required
|
||||||
|
- `MODAL` - Shows auth modal if not logged in
|
||||||
|
- `REDIRECT` - Redirects to /auth/sign-in if not logged in
|
||||||
|
|
||||||
|
### Frontend Directory Structure
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── App.js - Root component (providers + routing)
|
||||||
|
├── providers/ - Provider composition (AppProviders.js)
|
||||||
|
├── routes/ - Modular routing system (see above)
|
||||||
|
├── layouts/ - Page layouts (MainLayout, Auth)
|
||||||
|
├── views/ - Page components (Community, TradingSimulation, etc.)
|
||||||
|
├── components/ - Reusable UI components
|
||||||
|
├── contexts/ - React contexts (Auth, Notification, Sidebar)
|
||||||
|
├── store/ - Redux store + slices (auth, posthog, stock, industry, etc.)
|
||||||
|
├── services/ - API layer (axios wrappers)
|
||||||
|
├── utils/ - Utility functions (apiConfig, priceFormatters, logger)
|
||||||
|
├── constants/ - App constants (animations, etc.)
|
||||||
|
├── hooks/ - Custom React hooks
|
||||||
|
├── theme/ - Chakra UI theme customization
|
||||||
|
└── mocks/ - MSW handlers for development
|
||||||
|
├── handlers/ - Request handlers by domain (auth, stock, company, etc.)
|
||||||
|
├── data/ - Mock data files
|
||||||
|
└── browser.js - MSW setup (starts when REACT_APP_ENABLE_MOCK=true)
|
||||||
|
```
|
||||||
|
|
||||||
### Backend Structure
|
### Backend Structure
|
||||||
- **app.py / app_2.py** - Main Flask application with routes, authentication, and business logic
|
```
|
||||||
- **simulation_background_processor.py** - Background processor for trading simulations
|
app.py - Flask server (routes, auth, business logic)
|
||||||
- **wechat_pay.py / wechat_pay_config.py** - WeChat payment integration
|
simulation_background_processor.py - Celery worker for trading simulations
|
||||||
- **concept_api.py** - API for concept/industry analysis
|
concept_api.py - Concept/industry analysis API
|
||||||
- **tdays.csv** - Trading days calendar data (loaded into memory at startup)
|
wechat_pay.py / wechat_pay_config.py - WeChat payment integration
|
||||||
|
tdays.csv - Trading days calendar (loaded at startup)
|
||||||
|
requirements.txt - Python dependencies
|
||||||
|
```
|
||||||
|
|
||||||
### Key Integrations
|
### State Management Strategy
|
||||||
- **ClickHouse** - High-performance analytics queries
|
- **Redux Toolkit**: Global state (auth modal, posthog, stock data, industry data, subscriptions, community data)
|
||||||
- **Celery + Redis** - Background task processing
|
- **React Context**: Cross-cutting concerns (AuthContext, NotificationContext, SidebarContext)
|
||||||
- **Flask-SocketIO** - Real-time data updates via WebSocket
|
- **Component State**: Local UI state (forms, toggles, etc.)
|
||||||
- **Tencent Cloud** - SMS services
|
|
||||||
- **WeChat Pay** - Payment processing
|
|
||||||
- **PostHog** - Analytics (initialized in Redux)
|
|
||||||
- **MSW** - API mocking for development/testing
|
|
||||||
|
|
||||||
### Routing & Code Splitting
|
### Real-time Updates
|
||||||
- Routing is defined in **src/App.js** (not src/routes.js - that file is deprecated)
|
- Flask-SocketIO for WebSocket connections
|
||||||
- Heavy components use React.lazy() for code splitting (Community, TradingSimulation, etc.)
|
- Example: Community event notifications push via WebSocket
|
||||||
- Protected routes use ProtectedRoute and ProtectedRouteRedirect components
|
- Client: `socket.io-client` library
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Environment Files
|
### Environment Files
|
||||||
Multiple environment configurations available:
|
```
|
||||||
- **.env.mock** - Mock data mode (default for `npm start`)
|
.env.mock - Mock mode (default): MSW intercepts all API calls, no backend needed
|
||||||
- **.env.local** - Real backend connection
|
.env.development - Dev mode: Connects to dev backend
|
||||||
- **.env.development** - Development environment
|
.env.test - Test mode: Used by 'npm run start:test' (backend + frontend together)
|
||||||
- **.env.test** - Test environment
|
.env.production - Production build config
|
||||||
|
```
|
||||||
|
|
||||||
### Build Configuration (craco.config.js)
|
**Key environment variables:**
|
||||||
- **Webpack caching**: Filesystem cache for faster rebuilds (50-80% improvement)
|
- `REACT_APP_ENABLE_MOCK=true` - Enable MSW mocking
|
||||||
- **Code splitting**: Aggressive chunk splitting by library (react-vendor, charts-lib, chakra-ui, antd-lib, three-lib, etc.)
|
- `REACT_APP_API_URL` - Backend URL (empty string = use relative paths or MSW)
|
||||||
- **Path aliases**: `@` → src/, `@components` → src/components/, `@views` → src/views/, `@assets` → src/assets/, `@contexts` → src/contexts/
|
|
||||||
- **Optimizations**: ESLint plugin removed from build for speed, Babel caching enabled, moment locale stripping
|
|
||||||
- **Source maps**: Disabled in production, eval-cheap-module-source-map in development
|
|
||||||
- **Dev server proxy**: `/api` requests proxy to http://49.232.185.254:5001
|
|
||||||
|
|
||||||
### Important Build Notes
|
### MSW (Mock Service Worker) Setup
|
||||||
- Uses NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' for Node compatibility
|
MSW is used for API mocking during development:
|
||||||
- Gulp task adds Creative Tim license headers post-build
|
|
||||||
- Bundle analyzer available via `ANALYZE=true npm run build:analyze`
|
|
||||||
- Pre-build: kills any process on port 3000
|
|
||||||
|
|
||||||
## Testing
|
1. **Activation**: Set `REACT_APP_ENABLE_MOCK=true` in env file
|
||||||
- **React Testing Library** for component tests
|
2. **Worker file**: `public/mockServiceWorker.js` (auto-generated)
|
||||||
- **MSW** (Mock Service Worker) for API mocking during tests
|
3. **Handlers**: `src/mocks/handlers/` (organized by domain: auth, stock, company, etc.)
|
||||||
- Run tests: `npm test`
|
4. **Mode**: `onUnhandledRequest: 'warn'` - unhandled requests pass through to backend
|
||||||
|
|
||||||
## Deployment
|
When MSW is active, the dev server proxy is disabled (MSW intercepts first).
|
||||||
- Deployment scripts in **scripts/** directory
|
|
||||||
- Build output processed by Gulp for licensing
|
### Path Aliases (craco.config.js)
|
||||||
- Supports rollback via scripts/rollback-from-local.sh
|
All aliases resolve to `src/` subdirectories:
|
||||||
|
```
|
||||||
|
@/ → src/
|
||||||
|
@components/ → src/components/
|
||||||
|
@views/ → src/views/
|
||||||
|
@assets/ → src/assets/
|
||||||
|
@contexts/ → src/contexts/
|
||||||
|
@layouts/ → src/layouts/
|
||||||
|
@services/ → src/services/
|
||||||
|
@store/ → src/store/
|
||||||
|
@utils/ → src/utils/
|
||||||
|
@hooks/ → src/hooks/
|
||||||
|
@theme/ → src/theme/
|
||||||
|
@mocks/ → src/mocks/
|
||||||
|
@constants/ → src/constants/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Webpack Optimizations (craco.config.js)
|
||||||
|
**Performance features:**
|
||||||
|
- Filesystem cache (50-80% rebuild speedup)
|
||||||
|
- Aggressive code splitting by library:
|
||||||
|
- `react-vendor` - React core (priority 30)
|
||||||
|
- `charts-lib` - echarts, d3, apexcharts, recharts (priority 25)
|
||||||
|
- `chakra-ui` - Chakra UI + Emotion (priority 23)
|
||||||
|
- `antd-lib` - Ant Design (priority 22)
|
||||||
|
- `three-lib` - Three.js (priority 20)
|
||||||
|
- `calendar-lib` - moment, date-fns, FullCalendar (priority 18)
|
||||||
|
- ESLint plugin removed from build (20-30% speedup)
|
||||||
|
- Babel caching enabled
|
||||||
|
- moment locale stripping (IgnorePlugin)
|
||||||
|
- Source maps: disabled in production, `eval-cheap-module-source-map` in dev
|
||||||
|
|
||||||
|
**Dev server:**
|
||||||
|
- Port 3000 (kills existing process on prestart)
|
||||||
|
- Proxy (when MSW disabled): `/api` → `http://49.232.185.254:5001`
|
||||||
|
- Bundle analyzer: `ANALYZE=true npm run build:analyze`
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
1. `npm run build` compiles with CRACO + webpack optimizations
|
||||||
|
2. Gulp task (`gulp licenses`) adds Creative Tim license headers to JS/HTML
|
||||||
|
3. Output: `build/` directory
|
||||||
|
|
||||||
|
**Node compatibility:**
|
||||||
|
```bash
|
||||||
|
NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Working with Routes
|
||||||
|
To add a new route:
|
||||||
|
|
||||||
|
1. Add lazy import in `src/routes/lazy-components.js`
|
||||||
|
2. Add route config in `src/routes/routeConfig.js` with:
|
||||||
|
- `path` - URL path
|
||||||
|
- `component` - From lazyComponents
|
||||||
|
- `protection` - MODAL/REDIRECT/PUBLIC
|
||||||
|
- `layout` - 'main' (with nav) or 'none' (fullscreen)
|
||||||
|
3. Routes automatically render with Suspense + ErrorBoundary (handled by PageTransitionWrapper)
|
||||||
|
|
||||||
|
### Component Organization Patterns
|
||||||
|
Based on recent refactoring (see README.md for details):
|
||||||
|
|
||||||
|
**Atomic Design Pattern:**
|
||||||
|
- **Atoms** - Basic UI elements (buttons, badges, inputs)
|
||||||
|
- **Molecules** - Combinations of atoms (cards, forms)
|
||||||
|
- **Organisms** - Complex components (lists, panels)
|
||||||
|
|
||||||
|
**Example structure for large components (1000+ lines):**
|
||||||
|
```
|
||||||
|
src/views/Community/components/
|
||||||
|
├── EventCard/
|
||||||
|
│ ├── index.js - Smart wrapper (routes compact vs detailed)
|
||||||
|
│ ├── CompactEventCard.js - Compact view
|
||||||
|
│ ├── DetailedEventCard.js - Detailed view
|
||||||
|
│ ├── EventTimeline.js - Atomic component
|
||||||
|
│ ├── EventImportanceBadge.js - Atomic component
|
||||||
|
│ └── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Utility extraction:**
|
||||||
|
- Extract reusable logic to `src/utils/` (e.g., `priceFormatters.js`)
|
||||||
|
- Extract shared constants to `src/constants/` (e.g., `animations.js`)
|
||||||
|
|
||||||
|
### API Integration
|
||||||
|
**Service layer** (`src/services/`):
|
||||||
|
- Use `getApiBase()` from `src/utils/apiConfig.js` for base URL
|
||||||
|
- Example: `${getApiBase()}/api/endpoint`
|
||||||
|
- In mock mode, MSW intercepts; in dev/prod, hits backend
|
||||||
|
|
||||||
|
**Adding new API endpoints:**
|
||||||
|
1. Add service function in `src/services/` (or inline in component)
|
||||||
|
2. If using MSW: Add handler in `src/mocks/handlers/{domain}.js`
|
||||||
|
3. Import handler in `src/mocks/handlers/index.js`
|
||||||
|
|
||||||
|
### Redux State Management
|
||||||
|
**Existing slices** (`src/store/slices/`):
|
||||||
|
- `authModalSlice` - Auth modal state
|
||||||
|
- `posthogSlice` - PostHog analytics
|
||||||
|
- `stockSlice` - Stock data
|
||||||
|
- `industrySlice` - Industry/concept data
|
||||||
|
- `subscriptionSlice` - User subscriptions
|
||||||
|
- `communityDataSlice` - Community content
|
||||||
|
|
||||||
|
**Adding new slice:**
|
||||||
|
1. Create `src/store/slices/yourSlice.js`
|
||||||
|
2. Import and add to `src/store/index.js`
|
||||||
|
3. Access via `useSelector`, dispatch via `useDispatch`
|
||||||
|
|
||||||
|
## Backend Architecture
|
||||||
|
|
||||||
|
### Flask Application (app.py)
|
||||||
|
- **Authentication**: Flask-Login + session management
|
||||||
|
- **Database**: SQLAlchemy models + ClickHouse client
|
||||||
|
- **Background jobs**: Celery tasks for async processing
|
||||||
|
- **Real-time**: Flask-SocketIO for WebSocket events
|
||||||
|
- **Trading days**: `tdays.csv` loaded into memory at startup (global `trading_days` variable)
|
||||||
|
|
||||||
|
### Key Backend Patterns
|
||||||
|
- **ClickHouse**: Used for high-volume analytics queries (stock data, time series)
|
||||||
|
- **MySQL/PostgreSQL**: Used for transactional data (users, orders, subscriptions)
|
||||||
|
- **Celery**: Background processor runs in separate process (`simulation_background_processor.py`)
|
||||||
|
- **CORS**: Enabled for frontend communication
|
||||||
|
|
||||||
|
### API Proxy Configuration
|
||||||
|
When not in mock mode, frontend proxies to backend:
|
||||||
|
- `/api` → `http://49.232.185.254:5001` (main API)
|
||||||
|
- `/concept-api` → `http://49.232.185.254:6801` (concept analysis API)
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
### Code Splitting Strategy
|
||||||
|
Heavy pages are lazy-loaded to reduce initial bundle size:
|
||||||
|
- Community, TradingSimulation, Company pages use `React.lazy()`
|
||||||
|
- Webpack splits large libraries into separate chunks
|
||||||
|
- Check bundle size with `npm run build:analyze`
|
||||||
|
|
||||||
|
### Error Boundaries
|
||||||
|
- **Layout-level**: Each layout (MainLayout, Auth) has its own ErrorBoundary
|
||||||
|
- **Page-level**: PageTransitionWrapper wraps each route with ErrorBoundary
|
||||||
|
- **Strategy**: Errors are isolated to prevent entire app crashes
|
||||||
|
|
||||||
|
### PostHog Analytics
|
||||||
|
- Initialized in Redux (`posthogSlice`)
|
||||||
|
- Configured during app startup in `App.js`
|
||||||
|
- Used for user behavior tracking
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
- **Large components**: If component exceeds ~500 lines, consider refactoring (see README.md)
|
||||||
|
- **Re-renders**: Use `React.memo`, `useMemo`, `useCallback` for expensive operations
|
||||||
|
- **Bundle size**: Monitor with webpack-bundle-analyzer
|
||||||
|
- **Caching**: Webpack filesystem cache speeds up rebuilds significantly
|
||||||
@@ -162,6 +162,18 @@ export default function SubscriptionContent() {
|
|||||||
// 计算价格(包含升级和优惠码)
|
// 计算价格(包含升级和优惠码)
|
||||||
const calculatePrice = async (plan, cycle, promoCodeValue = null) => {
|
const calculatePrice = async (plan, cycle, promoCodeValue = null) => {
|
||||||
try {
|
try {
|
||||||
|
// 确保优惠码值正确:只接受非空字符串,其他情况传null
|
||||||
|
const validPromoCode = promoCodeValue && typeof promoCodeValue === 'string' && promoCodeValue.trim()
|
||||||
|
? promoCodeValue.trim()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
logger.debug('SubscriptionContent', '计算价格', {
|
||||||
|
plan: plan.name,
|
||||||
|
cycle,
|
||||||
|
promoCodeValue,
|
||||||
|
validPromoCode
|
||||||
|
});
|
||||||
|
|
||||||
const response = await fetch('/api/subscription/calculate-price', {
|
const response = await fetch('/api/subscription/calculate-price', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -171,7 +183,7 @@ export default function SubscriptionContent() {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
to_plan: plan.name,
|
to_plan: plan.name,
|
||||||
to_cycle: cycle,
|
to_cycle: cycle,
|
||||||
promo_code: promoCodeValue || null
|
promo_code: validPromoCode
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -191,7 +203,9 @@ export default function SubscriptionContent() {
|
|||||||
|
|
||||||
// 验证优惠码
|
// 验证优惠码
|
||||||
const handleValidatePromoCode = async () => {
|
const handleValidatePromoCode = async () => {
|
||||||
if (!promoCode.trim()) {
|
const trimmedCode = promoCode.trim();
|
||||||
|
|
||||||
|
if (!trimmedCode) {
|
||||||
setPromoCodeError('请输入优惠码');
|
setPromoCodeError('请输入优惠码');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -205,8 +219,8 @@ export default function SubscriptionContent() {
|
|||||||
setPromoCodeError('');
|
setPromoCodeError('');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 重新计算价格,包含优惠码
|
// 重新计算价格,包含优惠码(使用去除空格后的值)
|
||||||
const result = await calculatePrice(selectedPlan, selectedCycle, promoCode);
|
const result = await calculatePrice(selectedPlan, selectedCycle, trimmedCode);
|
||||||
|
|
||||||
if (result && !result.promo_error) {
|
if (result && !result.promo_error) {
|
||||||
setPromoCodeApplied(true);
|
setPromoCodeApplied(true);
|
||||||
@@ -1171,6 +1185,11 @@ export default function SubscriptionContent() {
|
|||||||
stopAutoPaymentCheck();
|
stopAutoPaymentCheck();
|
||||||
setPaymentOrder(null);
|
setPaymentOrder(null);
|
||||||
setPaymentCountdown(0);
|
setPaymentCountdown(0);
|
||||||
|
// 清空优惠码状态
|
||||||
|
setPromoCode('');
|
||||||
|
setPromoCodeApplied(false);
|
||||||
|
setPromoCodeError('');
|
||||||
|
setPriceInfo(null);
|
||||||
onPaymentModalClose();
|
onPaymentModalClose();
|
||||||
}}
|
}}
|
||||||
size="lg"
|
size="lg"
|
||||||
|
|||||||
Reference in New Issue
Block a user