CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development Commands
Core Commands
-
pnpm dev:weapp- Start WeChat mini-program development server -
pnpm dev:h5- Start H5 development server -
pnpm build:weapp- Build for production (WeChat mini-program) -
pnpm lint- Run ESLint
Other Platform Builds
-
pnpm dev:alipay- Alipay mini-program development -
pnpm dev:swan- Baidu mini-program development -
pnpm dev:tt- ByteDance mini-program development
Project Overview
Manulife WeApp (臻奇智荟圈) is a wealth management WeChat mini-program built with Taro 4 + Vue 3 + NutUI.
Business Modules
The app currently includes the following main features:
-
Product Showcase - Hot products display with detail pages
- Home page shows hot products with "产品资料" (Product Materials) buttons
- Product detail page displays comprehensive product information
-
Material Library - Training materials and document management
- Knowledge base for training materials and case studies
- Material list page with document preview capabilities
- Family Office (家办) - Family office services
- Contract Signing (签单) - Contract signing workflow
- User Center - Personal profile, favorites, feedback, help center
Project Architecture
This is a Taro 4 + Vue 3 + NutUI WeChat mini-program with built-in authentication and reusable navigation components.
Technology Stack
- Framework: Taro 4.1.9 + Vue 3.3.0 + Composition API
- UI Library: NutUI 4.3.13 (JD.com's UI library for Taro)
- State Management: Pinia 3.0.3 + taro-plugin-pinia
- HTTP Client: axios-miniprogram
- Styling: Less + TailwindCSS 3.x (dual design width system)
- Build Tool: Webpack 5
- Navigation: Custom TabBar + enhanced navigation hooks
Dual Design Width System
The project uses two different design widths configured in config/index.js:16-23:
- NutUI components: 375px base width
- All other pages: 750px base width
When working with styles:
- Check if you're using NutUI components → reference 375px designs
- Custom page layouts → reference 750px designs
Key Architectural Patterns
1. Reusable Navigation Components
TabBar Component (src/components/TabBar.vue):
- Fixed bottom navigation bar
- Auto-adapts to safe areas (notch/home indicator)
- Supports icon + text layout
- Active state highlighting
- Used across: Index, Mine, Family Office, Knowledge Base, Signing pages
NavHeader Component (src/components/NavHeader.vue):
- Custom navigation header with back button
- Transparent/background variants
- Safe area padding for notch devices
- Replaces default Taro navigation bar
IconFont Component (src/components/IconFont.vue):
- Icon font wrapper for custom icons
- Supports size and color customization
2. Authentication Flow (Required)
The project has a sophisticated authentication system with automatic session management:
Startup Flow (src/app.js:26-214):
- App saves launch path for auth callback
- Checks network status and handles weak network scenarios
- Attempts silent auth if not authenticated
- On auth success, enables offline features
Core Files:
-
src/utils/authRedirect.js- All auth logic (silent refresh, navigation, state) -
src/utils/request.js- HTTP client with 401 auto-refresh interceptor -
src/pages/auth/index.vue- Auth page (must be preserved) -
src/pages/login/index.vue- Login page
How 401 Auto-Refresh Works (src/utils/request.js:241-276):
- API returns 401
- Interceptor saves current page path
- Calls
refreshSession()to get new session via WeChat login - Retries original request with new session
- If refresh fails, redirects to auth page
Important: The backend MUST provide /srv/?a=openid_wxapp endpoint for WeChat login.
3. API Layer Architecture
API Definition Pattern (src/api/index.js):
export const yourAPI = (params) => {
return buildApiUrl('your_action', params)
}
Request Wrapper (src/api/fn.js):
- All API calls should go through this wrapper
- Handles common error scenarios
- Provides consistent interface
URL Building (src/utils/tools.js):
-
buildApiUrl(action, params)- Constructs full API URL - Automatically merges default parameters from
src/utils/config.js
4. Enhanced Navigation System
useGo Hook (src/hooks/useGo.js):
import { useGo } from '@/hooks/useGo'
const go = useGo()
go('/page-name') // Auto-completes path
go('/page', { id: 123 }) // With query params
Route Storage (src/stores/router.js):
- Maintains stack of visited routes
- Used for auth callback navigation
- Automatically managed by auth flow
Page Structure
All pages follow this directory structure:
src/pages/your-page/
├── index.vue # Page component (must use <script setup>)
├── index.config.js # Page configuration (navigationBarTitleText, etc.)
└── assets/ # Page-specific assets (optional)
Current Pages
Core Pages:
-
pages/index/index- Home page (product showcase, search, grid navigation)- Hot products "产品资料" buttons navigate to
product-detailpage with product ID - Hot materials "查看更多" navigates to
material-listpage - Grid navigation icons navigate to various business pages
- Hot products "产品资料" buttons navigate to
-
pages/auth/index- Authentication page -
pages/login/index- Login page
Business Pages:
-
pages/family-office/index- Family office services -
pages/knowledge-base/index- Knowledge base (training materials, cases) -
pages/signing/index- Contract signing -
pages/plan/index- Business plan management -
pages/favorites/index- User favorites -
pages/feedback/index- User feedback -
pages/avatar/index- Avatar settings -
pages/mine/index- User profile
Product & Content Pages:
-
pages/product-detail/index- Product detail page- Receives
idparameter via Taro'suseLoadhook - Example navigation:
go('/pages/product-detail/index', { id: 1 }) - Parameters can be used to fetch product details from API
- Receives
-
pages/material-list/index- Material/Document list page -
pages/help-center/index- Help center and FAQ page -
pages/search/index- Search page for products and materials
Utility Pages:
-
pages/onboarding/index- New user onboarding -
pages/webview/index- WebView wrapper for external URLs -
pages/document-demo/index- Document preview demo page -
pages/document-preview/index- Document preview page
Component Library
Reusable Components:
-
TabBar.vue- Bottom navigation bar -
NavHeader.vue- Custom navigation header -
IconFont.vue- Icon font wrapper
Feature Components:
-
qrCode.vue- QR code display -
qrCodeSearch.vue- QR code scanner -
PosterBuilder/- Poster generation -
time-picker-data/- Time picker data
Path Aliases
All configured in config/index.js:30-38:
@/utils → src/utils
@/components → src/components
@/images → src/assets/images
@/assets → src/assets
@/composables→ src/composables
@/api → src/api
@/stores → src/stores
@/hooks → src/hooks
Configuration Management
Environment Config (src/utils/config.js):
⚠️ MUST BE MODIFIED before use:
-
BASE_URL- Set development/production domain -
REQUEST_DEFAULT_PARAMS.f- Set business module identifier -
REQUEST_DEFAULT_PARAMS.client_name- Set application name
Build Config (config/index.js):
- Path aliases
- Design width rules
- NutUI auto-import
- Platform-specific settings
App Config (src/app.config.js):
- Page routes registration
- Window configuration
- Tab bar configuration (optional)
- Subpackages (if needed)
Important Implementation Details
Session Management
- Session ID stored in
localStoragewith keysessionid - Set by
authRedirect.refreshSession()after successful WeChat login - Automatically injected into request headers by
request.jsinterceptor - Cleared on logout or explicit manual intervention
Promise Single-Pattern
The auth system uses Promise singletons to prevent concurrent login attempts:
-
auth_promiseinauthRedirect.jsensures only one refresh at a time - All concurrent 401s share the same refresh Promise
-
.finally()ensures cleanup regardless of success/failure
Request Timeout Handling
- Default timeout: 5 seconds (
src/utils/request.js:79) - Timeout detection via
is_timeout_error()helper - Network error detection via
is_network_error()helper - Both trigger weak network fallback flow
NutUI Auto-Import
NutUI components are auto-imported via unplugin-vue-components (config/index.js:91-93).
No manual imports needed - just use components directly in templates.
TailwindCSS Integration
- Preflight disabled for mini-program compatibility
-
rem2rpxconversion enabled - Content paths configured in
tailwind.config.js - Use Tailwind for layout, spacing, colors
- Use Less for component-specific styles, animations, pseudo-elements
Styling Guidelines
When to use TailwindCSS (80% of cases):
<div class="flex items-center justify-between p-4 bg-white">
<h1 class="text-xl font-bold text-gray-900">Title</h1>
</div>
When to use Less (20% of cases):
- Component-specific styles
- Deep selectors (
:deep()) - Animations and transitions
- Pseudo-elements (
::before,::after) ```less .custom-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px;</li> </ul> <p>:deep(.nut-button) { background-color: rgba(255, 255, 255, 0.2); } }## Optional Features These features can be removed if not needed: - **WeChat Pay**: `src/utils/wechatPay.js`, `src/api/wx/pay.js` - **QR Code**: `src/components/qrCode.vue`, `src/components/qrCodeSearch.vue` - **Poster Builder**: `src/components/PosterBuilder/` - **Time Picker**: `src/components/time-picker-data/` - **Offline Cache**: Entire offline booking cache system (if not used) ## Development Workflow ### Adding New Pages 1. **Create page directory**: ```bash src/pages/your-page/ ├── index.vue └── index.config.js-
Configure page (
index.config.js):export default { navigationBarTitleText: 'Your Page Title', enablePullDownRefresh: true, backgroundColor: '#f5f5f5' } -
Register route in
src/app.config.js:pages: [ 'pages/your-page/index', // ... ] -
Use composition API in
index.vue:<script setup> import { ref } from 'vue' import { useLoad, useShow } from '@tarojs/taro'
const pageId = ref(null)
useLoad((options) => { console.log('Page loaded with options:', options) // Receive navigation parameters if (options.id) { pageId.value = options.id // Fetch data based on ID } })
useShow(() => { console.log('Page shown') })
// Your component logic
**Navigation with Parameters**: ```javascript // From another page import { useGo } from '@/hooks/useGo' const go = useGo() // Navigate with query params go('/pages/product-detail/index', { id: 123, type: 'insurance' })-
Add navigation (optional TabBar integration):
- Import and use
TabBarcomponent - Configure active state based on route
- Import and use
Adding API Calls
-
Define API in
src/api/index.js:export const getProductListAPI = (params) => { return buildApiUrl('product_list', params) } -
Use in page:
import { getProductListAPI } from '@/api' import { fn } from '@/api/fn'
const fetchProducts = async () => { try { const res = await fn(getProductListAPI({ page: 1 })) if (res.code === 1) { products.value = res.data } } catch (err) { console.error('Failed to fetch products:', err) } }
### Using Navigation **Recommended**: Use `useGo` hook for enhanced navigation: ```javascript import { useGo } from '@/hooks/useGo' const go = useGo() // Navigate to page go('/pages/detail/index') // Navigate with params (e.g., product ID) go('/pages/product-detail/index', { id: 123 }) // Navigate with multiple params go('/pages/material-list/index', { category: 'insurance', page: 1 }) // Go back go.back()Receiving Parameters in Target Page:
import { useLoad } from '@tarojs/taro' import { ref } from 'vue' const productId = ref(null) useLoad((options) => { // Access navigation parameters console.log('Received params:', options) productId.value = options.id // Fetch data based on parameters fetchProductDetail(options.id) })Alternative: Use Taro's built-in navigation:
import Taro from '@tarojs/taro' Taro.navigateTo({ url: '/pages/detail/index?id=123' })State Management with Pinia
Create store (
src/stores/yourStore.js):import { defineStore } from 'pinia' import { ref } from 'vue' export const useYourStore = defineStore('yourStore', () => { const state = ref(null) function setState(newState) { state.value = newState } return { state, setState } })Use in component:
import { useYourStore } from '@/stores/yourStore' const store = useYourStore() store.setState('new value')Critical Files Summary
Must Understand Before Modifying
-
src/utils/authRedirect.js- Complete auth flow logic -
src/utils/request.js- HTTP client with interceptors -
src/app.js- App startup sequence and network handling -
src/utils/config.js- Server configuration (requires modification)
Key Business Logic
-
src/api/index.js- API definitions -
src/api/fn.js- Request wrapper -
src/stores/main.js- Primary state management -
src/stores/router.js- Route state for auth callbacks
Reusable Components
-
src/components/TabBar.vue- Bottom navigation bar -
src/components/NavHeader.vue- Custom navigation header -
src/components/IconFont.vue- Icon wrapper
UI/UX Utilities
-
src/utils/uiText.js- Centralized copy management -
src/utils/network.js- Network status utilities -
src/hooks/useGo.js- Enhanced navigation hook
Debugging Tips
When debugging issues:
-
Check environment config:
- Verify
BASE_URLinsrc/utils/config.js - Check business module identifier
fandclient_name
- Verify
-
Verify authentication:
- Check
sessionidin localStorage - Enable verbose logging in
src/utils/request.jsinterceptor - Test 401 refresh flow
- Check
-
Network issues:
- Use Taro's built-in network status monitoring
- Check weak network fallback scenarios
- Verify offline cache interactions
-
Styling problems:
- Confirm design width (375px vs 750px)
- Check if NutUI component or custom component
- Verify TailwindCSS classes are applied
-
Navigation issues:
- Check route is registered in
src/app.config.js - Verify page directory structure matches route
- Use
useGohook for consistent navigation
- Check route is registered in
Best Practices
Component Development
- Use
<script setup>syntax - Composables for reusable logic
- Props should have type definitions
- Use
emitfor child-to-parent communication - Prefer TailwindCSS for styling
API Integration
- Always check
res.code === 1for success - Use
try/catchfor error handling - Show loading states during requests
- Handle network errors gracefully
Performance
- Use page lazy loading (subpackages)
- Optimize images with CDN parameters
- Avoid large data sets without pagination
- Clean up timers and listeners in
onUnmounted
Code Style
- Follow Vue 3 Composition API patterns
- Use descriptive variable names
- Keep functions focused and small (< 50 lines)
- Add JSDoc comments for complex functions
- Run
pnpm lintbefore committing
-