diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..3b33e38 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,12 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:x.ant.design)", + "WebSearch", + "WebFetch(domain:antd-design-x-vue.netlify.app)", + "Bash(npm install:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..68a4a8a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,221 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a **小智AI (Xiaozhi AI)** voice assistant front-end application built with **uni-app (Vue 3)** targeting multiple platforms (H5, WeChat Mini Program, and other mini-program platforms). The app provides AI conversation, voice interaction, script management, voice cloning, and IoT device configuration capabilities. + +**Core Technologies:** +- uni-app 3.x with Vue 3 Composition API +- Vite 5.2.8 for build tooling +- Pinia 2.1.7 for state management +- vue-i18n 9.1.9 for internationalization + +**Backend Integration:** +- Java Spring Boot backend at `http://localhost:8091` (configurable in `src/utils/api.js`) +- Alibaba Cloud voice services for TTS/ASR/voice cloning +- WeChat OAuth 2.0 for user authentication + +## Development Commands + +```bash +# Install dependencies +npm install + +# H5 development (web browser) +npm run dev:h5 + +# WeChat Mini Program development +npm run dev:mp-weixin + +# Build for H5 production +npm run build:h5 + +# Build for WeChat Mini Program +npm run build:mp-weixin + +# Other platforms (Alipay, Baidu, etc.) +npm run dev:mp-[platform] # development +npm run build:mp-[platform] # production +``` + +Build outputs go to `dist/build/h5/` or `dist/build/mp-weixin/` respectively. + +## Architecture & Code Structure + +### Entry Points +- **`src/main.js`**: Application bootstrap, creates SSR app and registers Pinia +- **`src/App.vue`**: Root component with global lifecycle hooks +- **`src/pages.json`**: Page routing, tabBar configuration, and navigation styles (all pages use custom navigation) +- **`src/manifest.json`**: Platform-specific configurations (WeChat AppID: `wxff56c34ef9aceb62`) + +### State Management (Pinia) +- **`src/stores/user.js`**: User authentication state + - Manages `token`, `nickName`, `avatarUrl`, `openid`, `isLoggedIn`, `hasAgreedToTerms`, `hasVisitedMinePage` + - Provides `login()`, `logout()`, `setUserInfo()`, `clearFakeLoginData()` methods + - Persists state to uni-app local storage (`userInfo`, `custom_token`, `user_token`) + - **Important**: Token is checked from multiple sources (store, `custom_token`, `user_token`, `userInfo.token`) + +### API Layer (`src/utils/api.js`) +- **Request interceptor**: Automatically adds `Authorization: Bearer ` header from multiple token sources +- **Response handling**: Smart extraction of backend responses from various nested structures +- **Text cleaning**: `cleanText()` function strips HTML/Markdown and keeps only text and punctuation + +**API Modules:** +- **`chatAPI`**: + - `syncChat()` - Synchronous chat with fallback for anonymous users + - `asyncChat()` - Asynchronous chat (if needed) + - `getChatHistory()`, `createConversation()` - Conversation management + - **Note**: When user is not logged in, returns `isAnonymous: true` to trigger local simulation +- **`voiceAPI`**: + - `textToSpeech()` - TTS (Text-to-Speech) + - `chatWithTTS()` - AI chat + voice synthesis + - `voiceChat()` - Complete voice interaction (uploads AAC, backend converts to WAV) + - `uploadVoiceChat()` - Audio file upload for voice chat + - **Important**: Frontend sends AAC format, backend must convert to WAV for processing + - Response parsing handles complex nested structures: `data.llmResult.response`, `data.sttResult.text`, `data.ttsResult.audioPath` +- **`rechargeAPI`**: `getUserBalance()`, `createRechargeOrder()`, `getOrderStatus()`, `getRechargeHistory()` +- **`roleAPI`**: `getRoles()`, `getRoleById()` - AI character management +- **`configAPI`**: `getAllConfigs()`, `getModels()`, `getSTTConfigs()`, `getTemplates()`, `getTTSConfigs()` + +### Page Structure +Pages are organized by feature under `src/pages/`: + +**Core Pages:** +- `splash/splash` - Launch screen with agreement flow +- `index/index` - Homepage with drama characters and AI voices (has tabBar) +- `mine/mine` - User profile and settings (has tabBar) +- `chat/chat` - AI conversation with text and voice input +- `create/create` - Script creation entry +- `script/editor` - Script editing interface +- `drama/index` - Drama character details +- `voice/clone` - Voice cloning feature +- `agreement/agreement` - User agreement display +- `recharge/recharge` - Recharge/payment +- `recharge/history` - Recharge history (with pull-down refresh enabled) + +All pages use `navigationStyle: "custom"` for custom navigation bars. + +### Global Styles +- **`src/uni.scss`**: Global SCSS variables and mixins +- **`src/styles/`**: Common stylesheets +- **`src/static/`**: Static assets (images, icons, etc.) + +### Components +- **`src/components/UserAgreement.vue`**: User agreement component +- **`src/components/UserAuth.vue`**: User authentication component + +## Key Architectural Patterns + +### Authentication Flow +1. User triggers login from UI +2. `useUserStore.login()` orchestrates: + - Call `uni.getUserProfile()` for WeChat user info + - Call `uni.login()` to get WeChat `code` + - POST to `/app/login` with `code` + - Save `token`, `nickName`, `avatarUrl`, `openid` to store and local storage +3. All subsequent API calls auto-inject token from store/storage via request interceptor + +### Anonymous User Handling +- When not logged in, `chatAPI.syncChat()` returns `isAnonymous: true` +- UI layer should implement local simulation/fallback responses +- Voice APIs skip backend calls and use degradation handling + +### Voice Interaction Flow +1. **Record**: User records voice (AAC format) using `uni.getRecorderManager()` +2. **Upload**: Frontend uploads AAC to backend via `voiceAPI.voiceChat()` or `uploadVoiceChat()` +3. **Backend Processing**: + - Convert AAC to WAV + - STT (Speech-to-Text) via Alibaba Cloud ASR + - LLM processing for AI response + - TTS (Text-to-Speech) via Alibaba Cloud TTS +4. **Response**: Backend returns structured response with `sttResult.text`, `llmResult.response`, `ttsResult.audioPath` +5. **Playback**: Frontend plays audio using `uni.createInnerAudioContext()` + +### Response Data Extraction Pattern +The API layer uses a flexible extraction strategy to handle varying backend response structures: +- Try multiple field names: `response`, `message`, `content`, `reply`, `answer`, `text` +- Check nested paths: `data.data.response`, `data.response`, root-level `response` +- For voice APIs, specifically look for: `data.llmResult.response`, `data.sttResult.text`, `data.ttsResult.audioPath` +- Always clean responses with `cleanText()` to remove markup + +## Important Configuration Files + +### `src/pages.json` +Defines all pages, tabBar, and global styles. Key settings: +- All pages have `navigationStyle: "custom"` (custom nav bars) +- TabBar: Homepage (`pages/index/index`) and Mine (`pages/mine/mine`) +- `lazyCodeLoading: "requiredComponents"` for performance +- Splash and agreement pages have `disableScroll: true` and `popGesture: "none"` (prevent back gesture) + +### `src/manifest.json` +Platform-specific configurations: +- WeChat Mini Program AppID: `wxff56c34ef9aceb62` +- App permissions and capabilities +- Platform-specific optimizations + +### `vite.config.js` +Minimal Vite config using `@dcloudio/vite-plugin-uni` plugin. + +## Documentation References + +Refer to these files for detailed specifications: +- **`API接口文档.md`**: Complete backend API documentation with request/response examples +- **`产品设计需求文档.md`**: Product requirements, functional specifications, AI service integration details, IoT device configuration requirements +- **`前端交接文档.md`**: Project handoff document with module descriptions and development notes + +## Development Best Practices + +### Working with uni-app +- Use `uni.*` APIs for cross-platform compatibility (e.g., `uni.request`, `uni.showToast`, `uni.navigateTo`) +- Test on both H5 and WeChat Mini Program to ensure compatibility +- WeChat Mini Program has stricter restrictions (e.g., requires HTTPS for production, domain whitelist) + +### State Management +- Always use Pinia store for shared state +- Persist critical state (user info, tokens) to local storage via `uni.setStorageSync()` +- Initialize stores on app launch by calling store's `init()` method + +### API Integration +- Never hardcode API endpoints; use `BASE_URL` in `src/utils/api.js` +- Handle both logged-in and anonymous states gracefully +- Always check for multiple possible response structures when parsing backend data +- Use `cleanText()` for AI-generated text to strip formatting + +### Audio Handling +- Record audio using `uni.getRecorderManager()` with AAC format +- Play audio using `uni.createInnerAudioContext()` +- Always inform backend that AAC will be sent and needs WAV conversion +- Cache frequently used audio responses for better performance + +### Error Handling +- Display user-friendly error messages using `uni.showToast()` +- Log errors to console for debugging +- Implement fallback mechanisms for non-critical features when backend is unavailable + +### Performance +- Use `lazyCodeLoading: "requiredComponents"` (already configured) +- Optimize images and static assets +- Implement code splitting for large pages +- Cache API responses where appropriate (e.g., config data, role lists) + +## Platform-Specific Notes + +### WeChat Mini Program +- Requires domain whitelist configuration in WeChat Developer Console +- OAuth login flow uses `uni.login({ provider: 'weixin' })` +- Recording and playback permissions must be requested from user +- Payment integration uses WeChat Pay API + +### H5 (Web) +- Can be deployed to any web server +- Use browser DevTools for debugging +- May need CORS configuration on backend for local development + +## Git Workflow + +Main branch: `master` +Current branch: `test` + +When creating PRs, target the `master` branch. diff --git a/package.json b/package.json index 50ccd4d..1934906 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,8 @@ "@dcloudio/uni-mp-weixin": "3.0.0-4060420250429001", "@dcloudio/uni-mp-xhs": "3.0.0-4060420250429001", "@dcloudio/uni-quickapp-webview": "3.0.0-4060420250429001", + "ant-design-vue": "^4.2.6", + "ant-design-x-vue": "^1.3.2", "pinia": "^2.1.7", "vue": "^3.4.21", "vue-i18n": "^9.1.9" diff --git a/src/App.vue b/src/App.vue index 8b90be5..533c3c0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,18 +2,26 @@ export default { onLaunch: function () { console.log('App Launch'); - - // 应用启动时的全局初始化逻辑 - // 启动页会自动处理倒计时和页面跳转 - - // 可以在这里添加其他全局初始化逻辑 - // 例如:设置全局变量、注册事件监听器等 + + // 检查是否是首次启动或需要显示启动页 + const hasShownSplash = uni.getStorageSync('hasShownSplash'); + const hasAgreedToTerms = uni.getStorageSync('hasAgreedToTerms'); + + // 如果未显示过启动页或未同意协议,则跳转到启动页 + if (!hasShownSplash || hasAgreedToTerms !== 'true') { + // 延迟一下确保页面已加载 + setTimeout(() => { + uni.redirectTo({ + url: '/pages/splash/splash' + }); + }, 100); + } }, - + onShow: function () { console.log('App Show') }, - + onHide: function () { console.log('App Hide') } diff --git a/src/pages.json b/src/pages.json index 1c447ba..155158e 100644 --- a/src/pages.json +++ b/src/pages.json @@ -1,6 +1,29 @@ { - "lazyCodeLoading": "requiredComponents", - "pages": [ + "pages": [ + { + "path": "pages/drama/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/device/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/chat/chat", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/mine/mine", + "style": { + "navigationStyle": "custom" + } + }, { "path": "pages/splash/splash", "style": { @@ -19,12 +42,6 @@ "disableScroll": false } }, - { - "path": "pages/mine/mine", - "style": { - "navigationStyle": "custom" - } - }, { "path": "pages/create/create", "style": { @@ -37,12 +54,6 @@ "navigationStyle": "custom" } }, - { - "path": "pages/drama/index", - "style": { - "navigationStyle": "custom" - } - }, { "path": "pages/voice/clone", "style": { @@ -55,16 +66,10 @@ "navigationStyle": "custom", "disableScroll": true, "app-plus": { - "popGesture": "none" + "popGesture": "none" } } }, - { - "path": "pages/chat/chat", - "style": { - "navigationStyle": "custom" - } - }, { "path": "pages/recharge/recharge", "style": { @@ -92,8 +97,16 @@ "borderStyle": "white", "list": [ { - "pagePath": "pages/index/index", - "text": "首页" + "pagePath": "pages/drama/index", + "text": "发现" + }, + { + "pagePath": "pages/device/index", + "text": "设备" + }, + { + "pagePath": "pages/chat/chat", + "text": "专属" }, { "pagePath": "pages/mine/mine", diff --git a/src/pages/chat/chat.vue b/src/pages/chat/chat.vue index df9f6d8..825be08 100644 --- a/src/pages/chat/chat.vue +++ b/src/pages/chat/chat.vue @@ -1,9 +1,24 @@ diff --git a/src/pages/chat/chat.vue.backup b/src/pages/chat/chat.vue.backup new file mode 100644 index 0000000..635deac --- /dev/null +++ b/src/pages/chat/chat.vue.backup @@ -0,0 +1,1790 @@ + + + + + diff --git a/src/pages/device/index.vue b/src/pages/device/index.vue new file mode 100644 index 0000000..aff083a --- /dev/null +++ b/src/pages/device/index.vue @@ -0,0 +1,584 @@ + + + + + diff --git a/src/pages/drama/index.vue b/src/pages/drama/index.vue index d29eae3..2e3fc2e 100644 --- a/src/pages/drama/index.vue +++ b/src/pages/drama/index.vue @@ -1,14 +1,16 @@