refactor(#783): 统一API文件命名风格为kebab-case #788
Executable → Regular
Executable → Regular
@@ -8,8 +8,8 @@
|
||||
* - POST /api/v1/templates/{id}/toggle-favorite — 收藏/取消收藏
|
||||
*/
|
||||
import apiClient from "./client"
|
||||
import type { TitleConfig, SubtitleConfig, BgmConfig } from "./editingPlanner"
|
||||
import type { EditPlanConfig } from "./templateEditor"
|
||||
import type { TitleConfig, SubtitleConfig, BgmConfig } from "./editing-planner"
|
||||
import type { EditPlanConfig } from "./template-editor"
|
||||
|
||||
/* ──────────── 类型定义 ──────────── */
|
||||
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
import React, { useState, useMemo, useCallback, useRef, useEffect } from "react"
|
||||
import "./AssetSelector.css"
|
||||
import { Input, Select, Button } from "@/components/ui"
|
||||
import type { MediaAsset } from "@/api/templateEditor"
|
||||
import { MATERIAL_TYPE_LABELS, MATERIAL_TYPE_ICONS, QUALITY_OPTIONS } from "@/api/templateEditor"
|
||||
import type { MediaAsset } from "@/api/template-editor"
|
||||
import { MATERIAL_TYPE_LABELS, MATERIAL_TYPE_ICONS, QUALITY_OPTIONS } from "@/api/template-editor"
|
||||
|
||||
/* ──────────── 类型 ──────────── */
|
||||
|
||||
Executable → Regular
Executable → Regular
@@ -12,8 +12,8 @@
|
||||
*/
|
||||
import React, { useState, useCallback, useRef, useEffect } from "react"
|
||||
import { Modal, Button } from "@/components/ui"
|
||||
import { createVoiceClone, toVoiceClone } from "@/api/voiceClone"
|
||||
import type { VoiceClone } from "@/api/voiceClone"
|
||||
import { createVoiceClone, toVoiceClone } from "@/api/voice-clone"
|
||||
import type { VoiceClone } from "@/api/voice-clone"
|
||||
import { uploadAsset } from "@/api/assets"
|
||||
import "./clone-modal.css"
|
||||
|
||||
|
||||
Executable → Regular
Executable → Regular
@@ -5,8 +5,8 @@
|
||||
* 全部完成(ready / failed)后停止轮询。
|
||||
*/
|
||||
import { useState, useEffect, useCallback, useRef } from "react"
|
||||
import { getVoiceClones } from "@/api/voiceClone"
|
||||
import type { VoiceClone } from "@/api/voiceClone"
|
||||
import { getVoiceClones } from "@/api/voice-clone"
|
||||
import type { VoiceClone } from "@/api/voice-clone"
|
||||
|
||||
const POLL_INTERVAL = 3000 // 3 秒
|
||||
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
@@ -11,7 +11,7 @@ import type {
|
||||
TemplateCategory,
|
||||
TemplateMode,
|
||||
SaveTemplatePayload,
|
||||
} from "@/api/editingPlanner"
|
||||
} from "@/api/editing-planner"
|
||||
import {
|
||||
getEditingTemplates,
|
||||
getEditingTemplate,
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
updateEditingTemplate,
|
||||
getTemplateCategories,
|
||||
MODE_LABELS,
|
||||
} from "@/api/editingPlanner"
|
||||
import type { MediaAsset, TransitionEffect, TitleConfig } from "@/api/templateEditor"
|
||||
import { getMediaAssets, getEditPlan, getEditPlanClips } from "@/api/templateEditor"
|
||||
} from "@/api/editing-planner"
|
||||
import type { MediaAsset, TransitionEffect, TitleConfig } from "@/api/template-editor"
|
||||
import { getMediaAssets, getEditPlan, getEditPlanClips } from "@/api/template-editor"
|
||||
import { useUndoRedo } from "./hooks/useUndoRedo"
|
||||
import type {
|
||||
ClipData,
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
import React, { useRef, useState, useCallback } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import type { TemplateMode } from "@/api/editingPlanner"
|
||||
import type { TemplateMode } from "@/api/editing-planner"
|
||||
import type { ClipData, ClipType } from "../types"
|
||||
import { TRANSITION_OPTIONS } from "@/api/templateEditor"
|
||||
import { TRANSITION_OPTIONS } from "@/api/template-editor"
|
||||
import type { AssetItem } from "@/api/assets"
|
||||
|
||||
interface SubtitleSettings {
|
||||
|
||||
Executable → Regular
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
import React from "react"
|
||||
import { CloseOutlined, InboxOutlined } from "@ant-design/icons"
|
||||
import type { EditPlanGeneration } from "@/api/templateEditor"
|
||||
import { PLAN_STATUS_LABELS } from "@/api/templateEditor"
|
||||
import type { EditPlanGeneration } from "@/api/template-editor"
|
||||
import { PLAN_STATUS_LABELS } from "@/api/template-editor"
|
||||
|
||||
interface GenerationHistoryModalProps {
|
||||
open: boolean
|
||||
|
||||
@@ -7,7 +7,7 @@ import React, { useCallback } from "react"
|
||||
import { Drawer } from "antd"
|
||||
import type { IntroOutroConfig, IntroOutroItem, IntroOutroKind, TransitionType } from "../types"
|
||||
import { DEFAULT_INTRO_OUTRO } from "../types"
|
||||
import { TRANSITION_OPTIONS } from "@/api/templateEditor"
|
||||
import { TRANSITION_OPTIONS } from "@/api/template-editor"
|
||||
|
||||
/* ──────────── 常量 ──────────── */
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* Tab 切换:模板列表 + 素材库
|
||||
*/
|
||||
import React, { useState } from "react"
|
||||
import type { EditingTemplate } from "@/api/editingPlanner"
|
||||
import { MODE_LABELS } from "@/api/editingPlanner"
|
||||
import type { MediaAsset } from "@/api/templateEditor"
|
||||
import AssetSelector from "@/components/AssetSelector/AssetSelector"
|
||||
import type { EditingTemplate } from "@/api/editing-planner"
|
||||
import { MODE_LABELS } from "@/api/editing-planner"
|
||||
import type { MediaAsset } from "@/api/template-editor"
|
||||
import AssetSelector from "@/components/asset-selector/AssetSelector"
|
||||
|
||||
interface MediaPanelProps {
|
||||
templates: EditingTemplate[]
|
||||
|
||||
Executable → Regular
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
import React from "react"
|
||||
import type { ClipData, ClipType } from "../types"
|
||||
import type { TitleConfig } from "@/api/templateEditor"
|
||||
import type { TitleConfig } from "@/api/template-editor"
|
||||
import type { CoverConfig } from "../types"
|
||||
|
||||
interface SubtitleSettings {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import React from "react"
|
||||
import { Modal, Input, Select } from "@/components/ui"
|
||||
import type { TemplateCategory } from "@/api/editingPlanner"
|
||||
import type { TemplateCategory } from "@/api/editing-planner"
|
||||
|
||||
interface SaveModalProps {
|
||||
open: boolean
|
||||
|
||||
Executable → Regular
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
import React, { useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo } from "react"
|
||||
import type { ClipData, ClipType, TrimConfig } from "../types"
|
||||
import { TRANSITION_OPTIONS } from "@/api/templateEditor"
|
||||
import { TRANSITION_OPTIONS } from "@/api/template-editor"
|
||||
|
||||
interface TimelinePanelProps {
|
||||
clips: ClipData[]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
import React, { useCallback } from "react"
|
||||
import { Drawer, Slider } from "antd"
|
||||
import { TRANSITION_OPTIONS } from "@/api/templateEditor"
|
||||
import { TRANSITION_OPTIONS } from "@/api/template-editor"
|
||||
import type { TransitionConfig, TransitionType } from "../types"
|
||||
import { DEFAULT_TRANSITION } from "../types"
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
CreateEditPlanClipRequest,
|
||||
UpdateEditPlanClipRequest,
|
||||
ClipReorderItem,
|
||||
} from "@/api/templateEditor"
|
||||
} from "@/api/template-editor"
|
||||
import {
|
||||
getEditPlanClips,
|
||||
createEditPlanClip,
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
reorderEditPlanClips,
|
||||
batchDeleteEditPlanClips,
|
||||
createClipsFromAssets,
|
||||
} from "@/api/templateEditor"
|
||||
} from "@/api/template-editor"
|
||||
import { useUndoRedo } from "./useUndoRedo"
|
||||
|
||||
const QUERY_KEY = "editPlanClips"
|
||||
|
||||
Executable → Regular
Executable → Regular
@@ -30,15 +30,15 @@ import {
|
||||
getGenerationTaskResults,
|
||||
getGenerationStatus,
|
||||
getEditPlan,
|
||||
} from "@/api/templateEditor"
|
||||
import type { GeneratedVideo, EditPlanConfig, TitleConfig } from "@/api/templateEditor"
|
||||
} from "@/api/template-editor"
|
||||
import type { GeneratedVideo, EditPlanConfig, TitleConfig } from "@/api/template-editor"
|
||||
import type { CoverConfig } from "../editing-planner/types"
|
||||
import { getEditingTemplates } from "@/api/editingPlanner"
|
||||
import { getEditingTemplates } from "@/api/editing-planner"
|
||||
import { getTitles } from "@/api/titles"
|
||||
import { fetchPresetVoices } from "@/api/voices"
|
||||
import type { PresetVoiceItem } from "@/api/voices"
|
||||
import { formatDuration } from "@/api/voiceClone"
|
||||
import type { VoiceClone } from "@/api/voiceClone"
|
||||
import { formatDuration } from "@/api/voice-clone"
|
||||
import type { VoiceClone } from "@/api/voice-clone"
|
||||
import CloneModal from "@/components/voice/CloneModal"
|
||||
import { synthesizeSpeech, getTTSJobStatus, saveTtsToLibrary } from "@/api/tts"
|
||||
import { getTags, createTag } from "@/api/tags"
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
@@ -40,7 +40,7 @@ import {
|
||||
MODE_COLORS,
|
||||
type EditingTemplate,
|
||||
type TemplateMode,
|
||||
} from "@/api/editingPlanner"
|
||||
} from "@/api/editing-planner"
|
||||
import "./MyTemplates.css"
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
@@ -19,8 +19,8 @@ import { Button, Modal, Input, Tooltip } from "@/components/ui"
|
||||
import type { ButtonProps } from "antd"
|
||||
import PageHead from "@/components/layout/PageHead"
|
||||
import { useCloneProgress } from "@/hooks/useCloneProgress"
|
||||
import { deleteVoiceClone, updateVoiceClone, formatDuration } from "@/api/voiceClone"
|
||||
import type { VoiceClone, VoiceCloneStatus } from "@/api/voiceClone"
|
||||
import { deleteVoiceClone, updateVoiceClone, formatDuration } from "@/api/voice-clone"
|
||||
import type { VoiceClone, VoiceCloneStatus } from "@/api/voice-clone"
|
||||
import "./my-voices.css"
|
||||
|
||||
/* ============================================================
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
@@ -25,7 +25,7 @@ import {
|
||||
updateVoiceClone,
|
||||
formatDuration,
|
||||
type VoiceClone as VoiceCloneType,
|
||||
} from "@/api/voiceClone"
|
||||
} from "@/api/voice-clone"
|
||||
import "./voice-clone.css"
|
||||
|
||||
/* ── 状态配置 ─────────────────────────────────────────── */
|
||||
|
||||
Executable → Regular
+1
-1
@@ -34,7 +34,7 @@ import {
|
||||
retryVoiceClone,
|
||||
toVoiceClone,
|
||||
type VoiceClone,
|
||||
} from "@/api/voiceClone"
|
||||
} from "@/api/voice-clone"
|
||||
import { synthesizeSpeech, getTTSJobStatus, saveTtsToLibrary } from "@/api/tts"
|
||||
import {
|
||||
getAssetsByKind,
|
||||
|
||||
Executable → Regular
@@ -23,7 +23,7 @@ import {
|
||||
copyEditPlan,
|
||||
getMediaAssets,
|
||||
getMediaAsset,
|
||||
} from "@/api/templateEditor"
|
||||
} from "@/api/template-editor"
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
+1
-1
@@ -7,7 +7,7 @@ import {
|
||||
deleteEditingTemplate,
|
||||
getTemplateCategories,
|
||||
generateFromTemplate,
|
||||
} from "@/api/editingPlanner"
|
||||
} from "@/api/editing-planner"
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { toVoiceClone, formatDuration } from "@/api/voiceClone"
|
||||
import type { VoiceCloneProfile } from "@/api/voiceClone"
|
||||
import { toVoiceClone, formatDuration } from "@/api/voice-clone"
|
||||
import type { VoiceCloneProfile } from "@/api/voice-clone"
|
||||
|
||||
describe("formatDuration", () => {
|
||||
it("should format seconds correctly", () => {
|
||||
+1
-1
@@ -8,7 +8,7 @@ import {
|
||||
updateVoiceClone,
|
||||
getVoiceCloneStatus,
|
||||
retryVoiceClone,
|
||||
} from "@/api/voiceClone"
|
||||
} from "@/api/voice-clone"
|
||||
|
||||
const mockGet = vi.fn()
|
||||
const mockPost = vi.fn()
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
import React from "react"
|
||||
import { describe, it, expect, vi } from "vitest"
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import AssetSelector from "@/components/AssetSelector/AssetSelector"
|
||||
import type { MediaAsset } from "@/api/templateEditor"
|
||||
import AssetSelector from "@/components/asset-selector/AssetSelector"
|
||||
import type { MediaAsset } from "@/api/template-editor"
|
||||
|
||||
vi.mock("@/components/ui", () => ({
|
||||
Input: ({ placeholder }: any) => <input placeholder={placeholder} />,
|
||||
@@ -18,7 +18,7 @@ vi.mock("@/components/ui", () => ({
|
||||
Button: ({ children, onClick }: any) => <button onClick={onClick}>{children}</button>,
|
||||
}))
|
||||
|
||||
vi.mock("@/components/AssetSelector/AssetSelector.css", () => ({}))
|
||||
vi.mock("@/components/asset-selector/AssetSelector.css", () => ({}))
|
||||
|
||||
const mockAssets: MediaAsset[] = [
|
||||
{
|
||||
@@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest"
|
||||
import { render } from "@testing-library/react"
|
||||
import CloneModal from "@/components/voice/CloneModal"
|
||||
|
||||
vi.mock("@/api/voiceClone", () => ({
|
||||
vi.mock("@/api/voice-clone", () => ({
|
||||
createVoiceClone: vi.fn(),
|
||||
toVoiceClone: vi.fn(),
|
||||
}))
|
||||
|
||||
Executable → Regular
@@ -4,7 +4,7 @@ import { useCloneProgress } from "@/hooks/useCloneProgress"
|
||||
|
||||
// mock API
|
||||
const mockGetVoiceClones = vi.fn()
|
||||
vi.mock("@/api/voiceClone", () => ({
|
||||
vi.mock("@/api/voice-clone", () => ({
|
||||
getVoiceClones: (...args: unknown[]) => mockGetVoiceClones(...args),
|
||||
VoiceCloneStatus: { READY: "ready" },
|
||||
}))
|
||||
|
||||
Executable → Regular
@@ -137,7 +137,7 @@ vi.mock("@/components/ui", () => ({
|
||||
}))
|
||||
|
||||
// === API mocks ===
|
||||
vi.mock("@/api/editingPlanner", () => ({
|
||||
vi.mock("@/api/editing-planner", () => ({
|
||||
getEditingTemplates: vi.fn().mockResolvedValue({ items: [], total: 0 }),
|
||||
getEditingTemplate: vi.fn().mockResolvedValue({}),
|
||||
createEditingTemplate: vi.fn().mockResolvedValue({}),
|
||||
@@ -146,7 +146,7 @@ vi.mock("@/api/editingPlanner", () => ({
|
||||
MODE_LABELS: { pip: "画中画", intro_outro: "片头片尾", watermark: "水印" },
|
||||
}))
|
||||
|
||||
vi.mock("@/api/templateEditor", () => ({
|
||||
vi.mock("@/api/template-editor", () => ({
|
||||
getMediaAssets: vi.fn().mockResolvedValue({ items: [] }),
|
||||
getEditPlanGenerations: vi.fn().mockResolvedValue({ items: [] }),
|
||||
generateCover: vi.fn().mockResolvedValue({}),
|
||||
|
||||
@@ -204,7 +204,7 @@ vi.mock("@/api/voices", () => ({
|
||||
fetchPresetVoices: vi.fn().mockResolvedValue({ items: [] }),
|
||||
}))
|
||||
|
||||
vi.mock("@/api/editingPlanner", () => ({
|
||||
vi.mock("@/api/editing-planner", () => ({
|
||||
getEditingTemplates: vi.fn().mockResolvedValue({ items: [], total: 0 }),
|
||||
MODE_LABELS: { pip: "画中画" },
|
||||
}))
|
||||
@@ -213,7 +213,7 @@ vi.mock("@/api/titles", () => ({
|
||||
getTitles: vi.fn().mockResolvedValue({ items: [] }),
|
||||
}))
|
||||
|
||||
vi.mock("@/api/templateEditor", () => ({
|
||||
vi.mock("@/api/template-editor", () => ({
|
||||
generateEditPlan: vi.fn().mockResolvedValue({
|
||||
plan_id: "test-plan",
|
||||
generation_task_id: "test-task",
|
||||
@@ -234,7 +234,7 @@ vi.mock("@/api/templateEditor", () => ({
|
||||
getGenerationTaskResults: vi.fn().mockResolvedValue({ items: [] }),
|
||||
}))
|
||||
|
||||
vi.mock("@/api/voiceClone", () => ({
|
||||
vi.mock("@/api/voice-clone", () => ({
|
||||
formatDuration: vi.fn((s: number) => `${s}s`),
|
||||
}))
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ vi.mock("@ant-design/icons", () => ({
|
||||
PlusOutlined: () => <span>PlusOutlined</span>,
|
||||
}))
|
||||
|
||||
vi.mock("@/api/editingPlanner", () => ({
|
||||
vi.mock("@/api/editing-planner", () => ({
|
||||
getEditingTemplates: vi.fn(),
|
||||
getTemplateCategories: vi.fn().mockResolvedValue([]),
|
||||
deleteEditingTemplate: vi.fn(),
|
||||
|
||||
@@ -73,7 +73,7 @@ vi.mock("antd", () => ({
|
||||
RadioGroup: ({ children }: any) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
vi.mock("@/api/voiceClone", () => ({
|
||||
vi.mock("@/api/voice-clone", () => ({
|
||||
getVoiceCloneList: vi.fn().mockResolvedValue({ items: [], total: 0 }),
|
||||
deleteVoiceClone: vi.fn().mockResolvedValue({ success: true }),
|
||||
createVoiceClone: vi.fn().mockResolvedValue({ success: true }),
|
||||
|
||||
@@ -73,7 +73,7 @@ vi.mock("antd", () => ({
|
||||
RadioGroup: ({ children }: any) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
vi.mock("@/api/voiceClone", () => ({
|
||||
vi.mock("@/api/voice-clone", () => ({
|
||||
getVoiceCloneList: vi.fn().mockResolvedValue({ items: [], total: 0 }),
|
||||
createVoiceClone: vi.fn().mockResolvedValue({ success: true, id: "1" }),
|
||||
deleteVoiceClone: vi.fn().mockResolvedValue({ success: true }),
|
||||
|
||||
@@ -109,7 +109,7 @@ vi.mock("@ant-design/icons", () => ({
|
||||
vi.mock("@/store/authStore", () => ({
|
||||
useAuthStore: (sel: any) => sel({ user: { id: "1" }, isAuthenticated: true }),
|
||||
}))
|
||||
vi.mock("@/api/voiceClone", () => ({
|
||||
vi.mock("@/api/voice-clone", () => ({
|
||||
createVoiceClone: vi.fn().mockResolvedValue({ success: true }),
|
||||
deleteVoiceClone: vi.fn().mockResolvedValue({ success: true }),
|
||||
retryVoiceClone: vi.fn().mockResolvedValue({ success: true }),
|
||||
|
||||
@@ -32,7 +32,7 @@ vi.mock("antd", () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("@/api/templateEditor", () => ({
|
||||
vi.mock("@/api/template-editor", () => ({
|
||||
getEditPlanClips: vi.fn().mockResolvedValue({ items: [], total: 0 }),
|
||||
createEditPlanClip: vi.fn().mockResolvedValue({}),
|
||||
updateEditPlanClip: vi.fn().mockResolvedValue({}),
|
||||
|
||||
Executable → Regular
Regular → Executable
+83
-2
@@ -445,5 +445,86 @@ def list_all_assets() -> list[Asset]:
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2026-06-15
|
||||
**版本**: v1.0
|
||||
## 8. 前端规范(React + TypeScript)
|
||||
|
||||
### 8.1 文件命名规范
|
||||
|
||||
| 类型 | 风格 | 示例 | 说明 |
|
||||
|------|------|------|------|
|
||||
| **目录名** | kebab-case | `editing-planner/`, `asset-selector/` | 全小写,多单词用短横线连接 |
|
||||
| **组件文件** | PascalCase | `AssetSelector.tsx`, `MediaPanel.tsx` | 与组件导出名一致 |
|
||||
| **页面组件** | PascalCase | `EditingPlanner.tsx`, `MyTemplates.tsx` | 放在 kebab-case 目录中 |
|
||||
| **API 文件** | kebab-case | `template-editor.ts`, `voice-clone.ts` | 与 RESTful 资源路径风格一致 |
|
||||
| **Hooks** | camelCase (use前缀) | `useAuth.ts`, `useCloneProgress.ts` | React 官方惯例 |
|
||||
| **Store** | kebab-case | `auth-store.ts`, `ui-store.ts` | |
|
||||
| **工具函数/helpers** | kebab-case | `format-duration.ts`, `date-utils.ts` | |
|
||||
| **类型定义** | kebab-case | `types.ts`, `subtitle-types.ts` | 目录内类型定义可用 `types.ts` |
|
||||
| **常量** | UPPER_SNAKE_CASE | `MAX_UPLOAD_SIZE`, `API_BASE_URL` | |
|
||||
| **测试文件** | 与被测文件同名 + `.test` | `auth.test.ts`, `AssetSelector.test.tsx` | 放在 `test/` 目录下,保持相同相对路径 |
|
||||
|
||||
### 8.2 组件命名
|
||||
|
||||
- 组件名使用 **PascalCase**,与文件名一致
|
||||
- 默认导出组件名与文件名相同
|
||||
- 高阶组件/包装器用 `with` 前缀:`withAuth(Component)`
|
||||
- 渲染属性组件用 `Render` 后缀:`UserRender`
|
||||
|
||||
```tsx
|
||||
// ✅ 正确
|
||||
// 文件: AssetSelector.tsx
|
||||
const AssetSelector: React.FC<AssetSelectorProps> = ({ assets }) => { ... };
|
||||
export default AssetSelector;
|
||||
|
||||
// ❌ 错误
|
||||
// 文件: asset-selector.tsx
|
||||
const assetSelector = () => { ... };
|
||||
```
|
||||
|
||||
### 8.3 变量与函数命名
|
||||
|
||||
- **变量/函数**:camelCase
|
||||
- **布尔变量**:用 `is/has/should/can` 前缀
|
||||
- **事件处理函数**:用 `handle` 前缀 + 事件名
|
||||
- **事件 handler prop**:用 `on` 前缀
|
||||
|
||||
```tsx
|
||||
// ✅ 正确
|
||||
const isLoading = true;
|
||||
const hasError = false;
|
||||
const handleSubmit = () => { ... };
|
||||
|
||||
<Button onClick={onClick} />
|
||||
```
|
||||
|
||||
### 8.4 导入路径
|
||||
|
||||
- 使用 `@/` 别名引用 `src/` 下的文件
|
||||
- 同一目录内用相对路径 `./`
|
||||
- 导入顺序:React → 第三方库 → @/内部模块 → 相对路径 → 样式
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Modal } from 'antd';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { Asset } from '@/api/asset-selector';
|
||||
import { MediaPanel } from './MediaPanel';
|
||||
import './AssetSelector.css';
|
||||
```
|
||||
|
||||
### 8.5 CSS/样式命名
|
||||
|
||||
- CSS Modules / CSS 类名:kebab-case
|
||||
- styled-components:PascalCase(与组件一致)
|
||||
- Tailwind 工具类遵循官方惯例
|
||||
|
||||
```css
|
||||
/* ✅ 正确 */
|
||||
.asset-selector { ... }
|
||||
.asset-item { ... }
|
||||
.asset-item--active { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2026-07-23
|
||||
**版本**: v1.1
|
||||
|
||||
Reference in New Issue
Block a user