diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..f92631b5 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,92 @@ +module.exports = { + root: true, + + /* 环境配置 */ + env: { + browser: true, + es2021: true, + node: true, + }, + + /* 扩展配置 */ + extends: [ + 'react-app', // Create React App 默认规则 + 'react-app/jest', // Jest 测试规则 + 'eslint:recommended', // ESLint 推荐规则 + 'plugin:react/recommended', // React 推荐规则 + 'plugin:react-hooks/recommended', // React Hooks 规则 + 'plugin:prettier/recommended', // Prettier 集成 + ], + + /* 解析器选项 */ + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + + /* 插件 */ + plugins: ['react', 'react-hooks', 'prettier'], + + /* 规则配置 */ + rules: { + // React + 'react/react-in-jsx-scope': 'off', // React 17+ 不需要导入 React + 'react/prop-types': 'off', // 使用 TypeScript 类型检查,不需要 PropTypes + 'react/display-name': 'off', // 允许匿名组件 + + // 通用 + 'no-console': ['warn', { allow: ['warn', 'error'] }], // 仅警告 console.log + 'no-unused-vars': ['warn', { + argsIgnorePattern: '^_', // 忽略以 _ 开头的未使用参数 + varsIgnorePattern: '^_', // 忽略以 _ 开头的未使用变量 + }], + 'prettier/prettier': ['warn', {}, { usePrettierrc: true }], // 使用项目的 Prettier 配置 + }, + + /* 设置 */ + settings: { + react: { + version: 'detect', // 自动检测 React 版本 + }, + }, + + /* TypeScript 文件特殊配置 */ + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], // 仅对 TS 文件应用以下配置 + parser: '@typescript-eslint/parser', // 使用 TypeScript 解析器 + parserOptions: { + project: './tsconfig.json', // 关联 tsconfig.json + }, + extends: [ + 'plugin:@typescript-eslint/recommended', // TypeScript 推荐规则 + ], + plugins: ['@typescript-eslint'], + rules: { + // TypeScript 特定规则 + '@typescript-eslint/no-explicit-any': 'warn', // 警告使用 any(允许但提示) + '@typescript-eslint/explicit-module-boundary-types': 'off', // 不强制导出函数类型 + '@typescript-eslint/no-unused-vars': ['warn', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }], + '@typescript-eslint/no-non-null-assertion': 'warn', // 警告使用 !(非空断言) + + // 覆盖基础规则(避免与 TS 规则冲突) + 'no-unused-vars': 'off', // 使用 TS 版本的规则 + }, + }, + ], + + /* 忽略文件(与 .eslintignore 等效)*/ + ignorePatterns: [ + 'node_modules/', + 'build/', + 'dist/', + '*.config.js', + 'public/mockServiceWorker.js', + ], +}; diff --git a/CLAUDE.md b/CLAUDE.md index 3995dbee..f3e5d132 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,7 @@ **开发指南**: - [开发工作流](#开发工作流) - 路由、组件、API、Redux 开发指南 +- [TypeScript 接入](#typescript-接入) - TypeScript 渐进式迁移方案与指南 - [常见开发任务](#常见开发任务) - 5 个详细的开发任务教程 - [技术路径与开发指南](#技术路径与开发指南) - UI 框架选型、技术栈演进、最佳实践 @@ -42,6 +43,7 @@ **前端** - **核心框架**: React 18.3.1 +- **类型系统**: TypeScript 5.9.3(渐进式接入中,支持 JS/TS 混合开发) - **UI 组件库**: Chakra UI 2.8.2(主要) + Ant Design 5.27.4(表格/表单) - **状态管理**: Redux Toolkit 2.9.2 - **路由**: React Router v6.30.1 配合 React.lazy() 实现代码分割 @@ -81,6 +83,10 @@ npm test # 运行 React 测试套件(CRACO) npm run lint:check # 检查 ESLint 规则(退出码 0) npm run lint:fix # 自动修复 ESLint 问题 + +npm run type-check # TypeScript 类型检查(不生成输出) +npm run type-check:watch # TypeScript 类型检查监听模式 + npm run clean # 删除 node_modules 和 package-lock.json npm run reinstall # 清洁安装(运行 clean + install) ``` @@ -1386,6 +1392,159 @@ src/views/Community/components/ --- +## TypeScript 接入 + +### 概述 + +项目已于 **2025-11-13** 完成 TypeScript 环境配置,采用**渐进式迁移策略**,支持 JavaScript 和 TypeScript 混合开发。 + +**当前状态**: 环境配置完成,准备开始代码迁移 +**迁移策略**: 新代码使用 TypeScript,旧代码按优先级逐步迁移 +**类型严格度**: 推荐模式(`noImplicitAny: true`,其他严格检查待后续开启) + +### 已完成的环境配置 + +✅ **TypeScript 编译器**: v5.9.3 +✅ **tsconfig.json**: 推荐模式配置,支持 JS/TS 混合开发 +✅ **CRACO 配置**: 支持 `.ts` 和 `.tsx` 文件编译 +✅ **ESLint 配置**: 支持 TypeScript 语法检查 +✅ **路径别名**: 与现有 `@/` 别名保持一致 +✅ **全局类型定义**: 基础类型文件已创建在 `src/types/` + +### 可用类型定义 + +项目已创建以下基础类型定义文件: + +**`src/types/api.ts`** - API 相关类型 +- `ApiResponse` - 通用 API 响应结构 +- `PaginatedResponse` - 分页响应 +- `ApiError` - API 错误类型 +- `ListQueryParams` - 列表查询参数 + +**`src/types/stock.ts`** - 股票相关类型 +- `StockInfo` - 股票基础信息 +- `StockQuote` - 股票行情数据 +- `KLineData` - K 线数据 +- `StockFinancials` - 财务指标 +- `StockPosition` - 股票持仓 +- `Sector` - 概念/行业板块 + +**`src/types/user.ts`** - 用户相关类型 +- `UserInfo` - 用户基础信息 +- `AuthInfo` - 认证信息 +- `LoginParams` / `RegisterParams` - 登录/注册参数 +- `UserSubscription` - 订阅信息 +- `UserAccount` - 资金账户 +- `UserSettings` - 用户设置 + +**使用方式**: +```typescript +// 统一导入 +import type { StockQuote, UserInfo, ApiResponse } from '@/types'; + +// 或从具体文件导入 +import type { StockQuote } from '@/types/stock'; +``` + +### TypeScript 命令 + +```bash +# 类型检查(不生成输出文件) +npm run type-check + +# 类型检查 + 监听模式 +npm run type-check:watch + +# ESLint 检查(包含 TS 文件) +npm run lint:check + +# ESLint 自动修复 +npm run lint:fix +``` + +### 迁移路线图 + +详细的迁移指南请参考 **[TYPESCRIPT_MIGRATION.md](./TYPESCRIPT_MIGRATION.md)** 文档。 + +**简要路线图**: + +1. **优先级 1️⃣**: 工具层(`src/utils/`, `src/constants/`) + - 纯函数,迁移成本低,收益高 + - 提供类型定义给其他模块使用 + +2. **优先级 2️⃣**: 类型定义层(扩展 `src/types/`) + - 添加 `trading.ts`, `community.ts`, `chart.ts` 等 + +3. **优先级 3️⃣**: 服务层(`src/services/`) + - 定义 API 请求/响应类型 + - 使用 `ApiResponse` 包装响应 + +4. **优先级 4️⃣**: Redux 状态层(`src/store/slices/`) + - 定义 `RootState` 和 `AppDispatch` 类型 + - 创建类型化的 hooks + +5. **优先级 5️⃣**: 自定义 Hooks(`src/hooks/`) + - 添加泛型支持 + - 定义完整返回值类型 + +6. **优先级 6️⃣**: 组件层(`src/components/`, `src/views/`) + - Atoms → Molecules → Organisms → Pages + - 优先迁移复用度高的组件 + +### 开发规范 + +**新代码**: +- ✅ **必须使用 TypeScript**(`.ts` 或 `.tsx`) +- ✅ 所有函数参数和返回值添加类型 +- ✅ 组件 Props 使用 `interface` 定义 +- ✅ 避免使用 `any`(特殊情况需添加注释说明) + +**旧代码迁移**: +- 按优先级迁移,不强制一次性完成 +- 迁移前先阅读 [TYPESCRIPT_MIGRATION.md](./TYPESCRIPT_MIGRATION.md) +- 迁移后运行 `npm run type-check` 验证 + +**类型定义**: +- 公共类型定义导出到 `src/types/` +- 组件内部类型可定义在组件文件中 +- 使用 `type` 还是 `interface` 参考 [迁移指南](./TYPESCRIPT_MIGRATION.md) + +### 常见问题 + +**Q: 路径别名 `@/types` 无法识别?** +A: 确保在 `tsconfig.json` 中配置了 `paths`,并重启 IDE。使用 `npm run type-check` 而非命令行 `tsc`。 + +**Q: 如何处理第三方库没有类型定义?** +A: +1. 尝试安装 `@types/library-name` +2. 创建自定义类型声明文件 `src/types/library-name.d.ts` +3. 临时使用 `as any`(需添加 TODO 注释) + +**Q: 迁移期间如何处理 any 类型?** +A: 添加 ESLint 禁用注释和 TODO 说明: +```typescript +/* eslint-disable @typescript-eslint/no-explicit-any */ +// TODO: 待完善类型定义 +const legacyFunction = (data: any): any => { ... }; +``` + +**Q: React 组件的 children 类型如何定义?** +A: 使用 `React.ReactNode`: +```typescript +interface Props { + children: React.ReactNode; +} +``` + +### 参考资源 + +- [TYPESCRIPT_MIGRATION.md](./TYPESCRIPT_MIGRATION.md) - 完整迁移指南 +- [TypeScript 官方文档](https://www.typescriptlang.org/docs/) +- [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) +- [Redux Toolkit TypeScript 指南](https://redux-toolkit.js.org/usage/usage-with-typescript) + +--- + ## 更新本文档 本 CLAUDE.md 文件是一个持续更新的文档。在以下情况下应更新它: diff --git a/craco.config.js b/craco.config.js index ea82db97..946850d5 100644 --- a/craco.config.js +++ b/craco.config.js @@ -133,8 +133,8 @@ module.exports = { '@variables': path.resolve(__dirname, 'src/variables'), '@views': path.resolve(__dirname, 'src/views'), }, - // 减少文件扩展名搜索 - extensions: ['.js', '.jsx', '.json'], + // 减少文件扩展名搜索(优先 TypeScript) + extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], // 优化模块查找路径 modules: [ path.resolve(__dirname, 'src'), diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100755 index 7e20b6c2..00000000 --- a/jsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "src", - "paths": { - "@/*": ["./*"], - "@assets/*": ["assets/*"], - "@components/*": ["components/*"], - "@constants/*": ["constants/*"], - "@contexts/*": ["contexts/*"], - "@data/*": ["data/*"], - "@hooks/*": ["hooks/*"], - "@layouts/*": ["layouts/*"], - "@lib/*": ["lib/*"], - "@mocks/*": ["mocks/*"], - "@providers/*": ["providers/*"], - "@routes/*": ["routes/*"], - "@services/*": ["services/*"], - "@store/*": ["store/*"], - "@styles/*": ["styles/*"], - "@theme/*": ["theme/*"], - "@utils/*": ["utils/*"], - "@variables/*": ["variables/*"], - "@views/*": ["views/*"] - } - }, - "exclude": ["node_modules", "build", "dist"] -} diff --git a/package.json b/package.json index 4e18f16a..d9aaacee 100755 --- a/package.json +++ b/package.json @@ -106,13 +106,20 @@ "deploy": "bash scripts/deploy-from-local.sh", "deploy:setup": "bash scripts/setup-deployment.sh", "rollback": "bash scripts/rollback-from-local.sh", - "lint:check": "eslint . --ext=js,jsx; exit 0", - "lint:fix": "eslint . --ext=js,jsx --fix; exit 0", + "lint:check": "eslint . --ext=js,jsx,ts,tsx; exit 0", + "lint:fix": "eslint . --ext=js,jsx,ts,tsx --fix; exit 0", + "type-check": "tsc --noEmit", + "type-check:watch": "tsc --noEmit --watch", "clean": "rm -rf node_modules/ package-lock.json", "reinstall": "npm run clean && npm install" }, "devDependencies": { "@craco/craco": "^7.1.0", + "@types/node": "^20.19.25", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@typescript-eslint/eslint-plugin": "^8.46.4", + "@typescript-eslint/parser": "^8.46.4", "ajv": "^8.17.1", "autoprefixer": "^10.4.21", "concurrently": "^8.2.2", @@ -131,6 +138,7 @@ "react-error-overlay": "6.0.9", "sharp": "^0.34.4", "ts-node": "^10.9.2", + "typescript": "^5.9.3", "webpack-bundle-analyzer": "^4.10.2", "yn": "^5.1.0" }, diff --git a/src/components/EventCommentSection/CommentInput.js b/src/components/EventCommentSection/CommentInput.js new file mode 100644 index 00000000..0f5d9009 --- /dev/null +++ b/src/components/EventCommentSection/CommentInput.js @@ -0,0 +1,77 @@ +// src/components/EventCommentSection/CommentInput.js +/** + * 评论输入框组件 + * 功能:输入评论内容、字数限制、发布按钮 + */ + +import React from 'react'; +import { + Box, + Textarea, + Button, + HStack, + Text, + useColorModeValue, +} from '@chakra-ui/react'; + +const CommentInput = ({ + value, + onChange, + onSubmit, + isSubmitting, + maxLength = 500, + placeholder = '说点什么...', +}) => { + const bgColor = useColorModeValue('white', 'gray.800'); + const borderColor = useColorModeValue('gray.200', 'gray.600'); + const textColor = useColorModeValue('gray.600', 'gray.400'); + const countColor = useColorModeValue('gray.500', 'gray.500'); + + const handleKeyDown = (e) => { + // Ctrl/Cmd + Enter 快捷键提交 + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { + onSubmit(); + } + }; + + return ( + +