coder-common-thin-frontend/CLAUDE.md
Leo 3d39ef37e2 docs(guide): 完善项目开发规范和图标使用指南
文档更新:
- 更新项目名称为Coi Admin,保持品牌一致性
- 新增完整的图标使用规范和最佳实践
- 添加render函数中图标渲染的详细指导
- 提供图标语义映射表和使用示例
- 明确图标使用的强制要求和禁止行为

规范内容:
- 统一使用icon-park-outline图标库
- 模板中直接使用图标组件的正确方式
- render函数中图标组件的导入和使用规范
- 常用操作图标的语义化映射关系
- 完整的代码示例和错误示范对比
2025-07-07 14:34:42 +08:00

29 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

第一部分:核心编程原则 (Guiding Principles)

这是我们合作的顶层思想,指导所有具体的行为。 可读性优先 (Readability First):始终牢记“代码是写给人看的,只是恰好机器可以执行”。清晰度高于一切。 DRY (Don't Repeat Yourself):绝不复制代码片段。通过抽象(如函数、类、模块)来封装和复用通用逻辑。 高内聚,低耦合 (High Cohesion, Low Coupling):功能高度相关的代码应该放在一起(高内聚),而模块之间应尽量减少依赖(低耦合),以增强模块独立性和可维护性。

第二部分:具体执行指令 (Actionable Instructions)

这是 Claude 在日常工作中需要严格遵守的具体操作指南。 沟通与语言规范 默认语言:请默认使用简体中文进行所有交流、解释和思考过程的陈述。 代码与术语:所有代码实体(变量名、函数名、类名等)及技术术语(如库名、框架名、设计模式等)必须保持英文原文。 注释规范:代码注释应使用中文。 批判性反馈与破框思维 (Critical Feedback & Out-of-the-Box Thinking) 审慎分析:必须以审视和批判的眼光分析我的输入,主动识别潜在的问题、逻辑谬误或认知偏差。 坦率直言:需要明确、直接地指出我思考中的盲点,并提供显著超越我当前思考框架的建议,以挑战我的预设。 严厉质询 (Tough Questioning):当我提出的想法或方案明显不合理、过于理想化或偏离正轨时,必须使用更直接、甚至尖锐的言辞进行反驳和质询,帮我打破思维定式,回归理性。 开发与调试策略 (Development & Debugging Strategy) 坚韧不拔的解决问题 (Tenacious Problem-Solving):当面对编译错误、逻辑不通或多次尝试失败时,绝不允许通过简化或伪造实现来“绕过”问题。 逐个击破 (Incremental Debugging):必须坚持对错误和问题进行逐一分析、定位和修复。

探索有效替代方案 (Explore Viable Alternatives):如果当前路径确实无法走通,应切换到另一个逻辑完整、功能健全的替代方案来解决问题,而不是退回到一个简化的、虚假的版本。

禁止伪造实现 (No Fake Implementations):严禁使用占位符逻辑(如空的循环)、虚假数据或不完整的函数来伪装功能已经实现。所有交付的代码都必须是意图明确且具备真实逻辑的。 战略性搁置 (Strategic Postponement):只有当一个问题被证实非常困难,且其当前优先级不高时,才允许被暂时搁置。搁置时,必须以 TODO 形式在代码中或任务列表中明确标记,并清晰说明遇到的问题。在核心任务完成后,必须回过头来重新审视并解决这些被搁置的问题。 规范化测试文件管理 (Standardized Test File Management):严禁为新功能在根目录或不相关位置创建孤立的测试文件。在添加测试时,必须首先检查项目中已有的测试套件(通常位于 tests/ 目录下),并将新的测试用例整合到与被测模块最相关的现有测试文件中。只有当确实没有合适的宿主文件时,才允许在 tests/ 目录下创建符合项目命名规范的新测试文件。 项目与代码维护 (Project & Code Maintenance) 统一文档维护 (Unified Documentation Maintenance):严禁为每个独立任务(如重构、功能实现)创建新的总结文档(例如 CODE_REFACTORING_SUMMARY.md。在任务完成后必须优先检查项目中已有的相关文档如 README.md、既有的设计文档等并将新的总结、变更或补充内容直接整合到现有文档中维护其完整性和时效性。 及时清理 (Timely Cleanup):在完成开发任务时,如果发现任何已无用(过时)的代码、文件或注释,应主动提出清理建议。

项目概述

Coi Admin 是一个基于 Vue3、Vite5、TypeScript 和 Naive UI 的简洁后台管理模板,实现了完整的认证、权限管理、路由管理等功能。

常用命令

开发环境

# 启动开发服务器 (端口 9980)
pnpm dev

# 不同环境启动
pnpm dev:test    # 测试环境
pnpm dev:prod    # 生产环境

构建项目

# 生产环境构建
pnpm build

# 不同环境构建
pnpm build:dev   # 开发环境
pnpm build:test  # 测试环境

代码检查

# 运行 ESLint 检查和类型检查
pnpm lint

# 自动修复代码问题
pnpm lint:fix

# 检查 ESLint 配置
pnpm lint:check

其他工具

# 预览构建结果 (端口 9981)
pnpm preview

# 查看打包体积分析
pnpm sizecheck

项目架构

核心技术栈

  • Vue 3.5.16 + Composition API
  • Vite 6.3.5 构建工具
  • TypeScript 5.8.3 类型安全
  • Naive UI 2.41.1 组件库
  • Pinia 3.0.3 状态管理
  • Vue Router 4.5.1 路由管理
  • UnoCSS 66.2.0 原子化CSS
  • Alova 3.3.2 HTTP客户端

目录结构要点

src/
├── store/              # Pinia状态管理
│   ├── auth.ts        # 认证状态(登录、用户信息)
│   ├── router/        # 路由状态和菜单管理
│   ├── tab.ts         # 标签页状态
│   └── app/           # 应用全局状态
├── router/             # 路由配置
│   ├── index.ts       # 路由实例
│   ├── guard.ts       # 路由守卫
│   ├── routes.inner.ts # 内置路由(登录、错误页等)
│   └── routes.static.ts # 静态路由配置
├── views/              # 页面组件
├── layouts/            # 布局组件
├── components/         # 通用组件
├── service/            # API服务层
│   ├── api/           # 接口定义
│   └── http/          # HTTP配置
├── hooks/              # 组合式函数
├── utils/              # 工具函数
├── typings/            # 类型定义
└── constants/          # 常量定义

核心系统架构

1. 认证系统

  • 基于JWT Token的认证机制
  • 支持双TokenAccessToken + RefreshToken
  • 自动Token刷新和过期处理
  • 本地存储管理localStorage

关键文件:

  • src/store/auth.ts - 认证状态管理
  • src/views/login/ - 登录页面组件
  • src/service/api/login.ts - 登录API接口

2. 权限系统

  • 基于角色的访问控制RBAC
  • 多层权限验证路由级、组件级、API级
  • 权限指令 v-permission 和组合函数 usePermission
  • 支持super角色绕过所有权限检查

关键文件:

  • src/hooks/usePermission.ts - 权限验证组合函数
  • src/directives/permission.ts - 权限指令

3. 路由系统

  • 支持静态路由和动态路由
  • 路由守卫实现权限验证
  • 自动菜单生成和路由缓存
  • 多种布局模式支持

关键文件:

  • src/router/guard.ts - 路由守卫逻辑
  • src/store/router/ - 路由状态管理
  • src/store/router/helper.ts - 路由处理工具函数

4. 状态管理

  • 使用Pinia进行状态管理
  • 支持状态持久化
  • 模块化状态设计

状态模块:

  • authStore - 用户认证状态
  • routeStore - 路由和菜单状态
  • tabStore - 标签页状态
  • appStore - 应用全局状态

重要配置文件

环境配置

  • 支持多环境配置dev、test、prod
  • 环境变量通过 .env.* 文件管理
  • 服务配置通过 service.config.ts 统一管理

路由配置

  • 通过 VITE_ROUTE_LOAD_MODE 控制路由加载模式
  • 静态路由配置在 src/router/routes.static.ts
  • 动态路由通过API从后端获取

权限配置

  • 角色类型super、admin、user、editor
  • 权限验证支持单角色和多角色
  • 默认无权限要求时直接通过

📝 命名规范(强制要求)

1. 接口函数命名

  • 查询列表:getList, get列表名List
  • 新增:add项名, create项名
  • 修改:update项名, edit项名
  • 删除:delete项名, remove项名
  • 详情:get项名ById, get项名Detail

2. 类型定义命名

  • 请求参数:项名Bo (Business Object)
  • 响应数据:项名Vo (View Object)
  • 查询参数:项名QueryBo
  • 分页结果:Page项名Vo

3. 枚举命名

  • 全大写,下划线分隔:USER_LIST, ADD_USER, UPDATE_USER

⚠️ 严格禁止事项

1. 消息提示

// ❌ 严禁使用Element Plus原生消息
// ✅ 必须使用项目封装的消息函数
import { coiMsgError, coiMsgSuccess } from '@/utils/coi.ts'

ElMessage.success('操作成功')
this.$message.error('操作失败')
coiMsgSuccess('操作成功')
coiMsgError('操作失败')

2. 类型定义

// ❌ 严禁使用any类型
const response: any = await getList()

// ✅ 必须使用完整的类型定义
const response: Result<ResponseVo[]> = await getList()

3. 数据访问

// ❌ 错误的数据访问
const data = response

// ✅ 正确的数据访问
const data = response.data

消息提示规范

强制使用 coi.ts 封装的消息提示方法,严禁直接使用 Element Plus 的消息提示:

  • 消息提示:使用 coiMsgcoiMsgSuccesscoiMsgErrorcoiMsgWarningcoiMsgInfo
  • 通知提示:使用 coiNoticecoiNoticeSuccesscoiNoticeErrorcoiNoticeWarningcoiNoticeInfo
  • 确认对话框:使用 coiMsgBoxcoiMsgBoxHtmlcoiMsgBoxAlert
  • 输入对话框:使用 coiMsgBoxPrompt
// 正确示例
import { coiMsgBox, coiMsgError, coiMsgSuccess } from '@/utils/coi'

// 成功提示
coiMsgSuccess('操作成功')

// 错误提示
coiMsgError('操作失败')

// 确认对话框
coiMsgBox('确定要删除吗?', '删除确认').then(() => {
  // 确认操作
}).catch(() => {
  // 取消操作
})

📝 图标使用规范(强制要求)

项目中的图标使用规范,所有按钮和操作界面必须严格遵守

核心原则

  1. 所有按钮必须配备图标 - 提升用户体验和界面美观度
  2. 图标必须语义化 - 图标含义要与按钮功能高度匹配
  3. 统一的图标库 - 统一使用 icon-park-outline 图标库

图标添加方式

正确方式:直接在模板中使用图标组件

<template>
  <!-- 基础按钮 -->
  <NButton type="primary" @click="handleAdd">
    <template #icon>
      <NIcon>
        <icon-park-outline:plus />
      </NIcon>
    </template>
    新增
  </NButton>

  <!-- 条件图标显示 -->
  <NButton type="primary" @click="handleAction">
    <template #icon>
      <NIcon>
        <icon-park-outline:refresh v-if="hasSearchConditions()" />
        <icon-park-outline:plus v-else />
      </NIcon>
    </template>
    {{ actionText }}
  </NButton>

  <!-- 在组件插槽中使用 -->
  <NovaEmpty>
    <template #action>
      <NButton type="primary" round>
        <template #icon>
          <NIcon>
            <icon-park-outline:plus />
          </NIcon>
        </template>
        新增数据
      </NButton>
    </template>
  </NovaEmpty>
</template>

错误方式:字符串传递或复杂处理

<!-- 错误不要通过字符串传递图标 -->
<MyComponent :icon="'icon-park-outline:plus'" />

<!-- 错误不要通过h()函数处理图标 -->
<MyComponent :icon="h('icon-park-outline:plus')" />

<!-- 错误不要在组件内部处理字符串图标 -->
<component :is="iconString" />

常用图标语义映射

操作类型 推荐图标 示例场景
新增/添加 icon-park-outline:plus 新增用户、添加数据
编辑/修改 icon-park-outline:edit 编辑信息、修改配置
删除 icon-park-outline:delete 删除记录、移除项目
搜索 icon-park-outline:search 搜索按钮、查询操作
刷新/重置 icon-park-outline:refresh 重置表单、刷新数据
保存 icon-park-outline:check 保存设置、确认操作
取消 icon-park-outline:close 取消操作、关闭弹框
导出 icon-park-outline:download 导出数据、下载文件
导入 icon-park-outline:upload 导入数据、上传文件
设置 icon-park-outline:setting 系统设置、配置管理
查看 icon-park-outline:preview-open 查看详情、预览内容
复制 icon-park-outline:copy 复制内容、克隆数据

图标使用要求

1. 必须添加图标的场景

  • 所有操作按钮(新增、编辑、删除、保存等)
  • 搜索和重置按钮
  • 导入导出按钮
  • 空状态组件的操作按钮
  • 表单提交和取消按钮

2. 图标尺寸规范

  • 默认按钮:无需指定尺寸
  • 大按钮:可根据需要调整
  • 小按钮:保持图标清晰可见

3. 图标样式要求

<template #icon>
  <NIcon>
    <!-- 图标组件直接使用无需额外样式 -->
    <icon-park-outline:plus />
  </NIcon>
</template>

严格禁止行为

  • 按钮不添加图标
  • 使用字符串方式传递图标
  • 在JavaScript中动态处理图标组件
  • 使用其他图标库(除非特殊需求)
  • 图标与功能语义不匹配

示例:完整的页面按钮实现

<template>
  <div class="page-actions">
    <!-- 主要操作按钮 -->
    <NButton v-permission="PERMISSIONS.USER.ADD" type="primary" @click="handleAdd">
      <template #icon>
        <NIcon><icon-park-outline:plus /></NIcon>
      </template>
      新增
    </NButton>

    <!-- 批量操作按钮 -->
    <NButton type="error" :disabled="selectedRows.length === 0" @click="handleBatchDelete">
      <template #icon>
        <NIcon><icon-park-outline:delete /></NIcon>
      </template>
      删除
    </NButton>

    <!-- 功能按钮 -->
    <NButton @click="handleExport">
      <template #icon>
        <NIcon><icon-park-outline:download /></NIcon>
      </template>
      导出
    </NButton>

    <!-- 搜索重置按钮 -->
    <NButton type="primary" @click="handleSearch">
      <template #icon>
        <NIcon><icon-park-outline:search /></NIcon>
      </template>
      搜索
    </NButton>
    
    <NButton @click="handleReset">
      <template #icon>
        <NIcon><icon-park-outline:refresh /></NIcon>
      </template>
      重置
    </NButton>
  </div>
</template>

遵循此规范可确保项目界面的一致性和专业性!

🚀 render函数中图标渲染规范强制要求

在DataTable等组件的render函数中使用图标时必须遵循以下规范

问题背景

在render函数中不能使用模板语法 <icon-park-outline:edit />,必须使用正确的组件引用方式。

正确的render函数图标使用方式

步骤1导入具体的图标组件

// 从 ~icons 路径导入具体图标组件
import IconParkOutlineEdit from '~icons/icon-park-outline/edit'
import IconParkOutlineDelete from '~icons/icon-park-outline/delete'
import IconParkOutlineRefresh from '~icons/icon-park-outline/refresh'
import IconParkOutlineSetting from '~icons/icon-park-outline/setting'

步骤2在render函数中使用组件引用

// 表格列定义中的正确用法
const columns: DataTableColumns<UserVo> = [
  {
    title: '操作',
    key: 'actions',
    render: (row) => {
      const buttons = []
      
      // 编辑按钮 - 正确方式
      buttons.push(h(NButton, {
        type: 'primary',
        size: 'small',
        onClick: () => handleEdit(row),
      }, {
        icon: () => h(NIcon, { size: 14 }, { default: () => h(IconParkOutlineEdit) }),
        default: () => '编辑',
      }))
      
      // 删除按钮 - 正确方式  
      buttons.push(h(NButton, {
        type: 'error',
        size: 'small',
        onClick: () => handleDelete(row),
      }, {
        icon: () => h(NIcon, { size: 14 }, { default: () => h(IconParkOutlineDelete) }),
        default: () => '删除',
      }))
      
      return h('div', { class: 'flex gap-2' }, buttons)
    }
  }
]

错误方式:使用字符串引用

// 这种方式在render函数中无效
icon: () => h(NIcon, {}, { default: () => h('icon-park-outline:edit') })

// 这种方式也无效  
icon: () => h(NIcon, {}, { default: () => h('IconParkOutlineEdit') })

常用图标的正确导入方式

// 操作类图标
import IconParkOutlineEdit from '~icons/icon-park-outline/edit'           // 编辑
import IconParkOutlineDelete from '~icons/icon-park-outline/delete'       // 删除
import IconParkOutlineRefresh from '~icons/icon-park-outline/refresh'     // 刷新/重置
import IconParkOutlineSetting from '~icons/icon-park-outline/setting'     // 设置/分配

// 功能类图标
import IconParkOutlinePlus from '~icons/icon-park-outline/plus'           // 新增
import IconParkOutlineSearch from '~icons/icon-park-outline/search'       // 搜索
import IconParkOutlineDownload from '~icons/icon-park-outline/download'   // 下载/导出
import IconParkOutlineUpload from '~icons/icon-park-outline/upload'       // 上传/导入
import IconParkOutlinePreviewOpen from '~icons/icon-park-outline/preview-open' // 查看

完整示例:表格操作列实现

import { h } from 'vue'
import { NButton, NIcon, NPopconfirm } from 'naive-ui'
import IconParkOutlineEdit from '~icons/icon-park-outline/edit'
import IconParkOutlineDelete from '~icons/icon-park-outline/delete'
import IconParkOutlineSetting from '~icons/icon-park-outline/setting'

const columns: DataTableColumns<DataType> = [
  {
    title: '操作',
    key: 'actions',
    width: 280,
    align: 'center',
    fixed: 'right',
    render: (row) => {
      const buttons = []

      // 编辑按钮
      if (hasPermission('edit')) {
        buttons.push(h(NButton, {
          type: 'primary',
          size: 'small',
          onClick: () => handleEdit(row),
        }, {
          icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, { 
            default: () => h(IconParkOutlineEdit) 
          }),
          default: () => '编辑',
        }))
      }

      // 删除按钮(带确认)
      if (hasPermission('delete')) {
        buttons.push(h(NPopconfirm, {
          onPositiveClick: () => handleDelete(row.id),
          negativeText: '取消',
          positiveText: '确定',
        }, {
          default: () => '确定删除此项吗?',
          trigger: () => h(NButton, {
            type: 'error',
            size: 'small',
          }, {
            icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, { 
              default: () => h(IconParkOutlineDelete) 
            }),
            default: () => '删除',
          }),
        }))
      }

      // 设置按钮
      if (hasPermission('setting')) {
        buttons.push(h(NButton, {
          type: 'warning',
          size: 'small',
          onClick: () => handleSetting(row),
        }, {
          icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, { 
            default: () => h(IconParkOutlineSetting) 
          }),
          default: () => '设置',
        }))
      }

      return h('div', { class: 'flex items-center justify-center gap-2' }, buttons)
    },
  },
]

核心要点总结

  1. 导入方式:使用 ~icons/icon-park-outline/图标名 导入具体组件
  2. 组件引用在render函数中使用 h(IconComponent) 而非字符串
  3. 命名规范:图标组件名采用 IconParkOutline + 图标名(首字母大写) 格式
  4. 样式调整:可通过 style 属性微调图标位置和样式
  5. 尺寸设置:通过 NIconsize 属性控制图标大小

遵循此规范确保render函数中的图标能正确显示

开发注意事项

添加新页面

  1. src/views/ 创建页面组件
  2. src/router/routes.static.ts 添加路由配置
  3. 配置权限要求roles字段

添加新API

  1. src/service/api/ 定义接口
  2. 使用项目封装的alova实例
  3. 遵循统一的响应处理格式

📁 API文件组织规范强制要求

API文件必须按功能模块组织严格按模块导入严禁聚合导出或混合不同业务模块的API

文件结构规范

src/service/api/
├── auth/                 # 认证相关API
│   └── index.ts         # 登录、注册、验证码等认证API
├── system/              # 系统管理模块
│   ├── user/index.ts    # 用户管理API
│   ├── role/index.ts    # 角色管理API
│   ├── menu/index.ts    # 菜单管理API
│   └── ...
└── ...

API模块创建规则

  1. 模块化原则: 每个业务功能模块必须创建独立的目录和文件
  2. 统一导出: 每个模块目录必须有 index.ts 文件导出所有API
  3. 直接导入: 必须从具体的模块路径导入API禁止聚合导出
  4. 明确导入: 导入路径必须明确指向具体的功能模块

示例正确的API组织和导入方式

// src/service/api/auth/index.ts
// 页面中的正确导入方式
import {
  fetchCaptchaPng,
  fetchLogin,
  fetchLogout
} from '@/service/api/auth'

import {
  addUser,
  getUserList,
  updateUser
} from '@/service/api/system/user'

import {
  assignUserRole,
  getRoleList
} from '@/service/api/system/role'

import {
  fetchUserRoutes
} from '@/service/api/system/menu'

export function fetchLogin(data: LoginRequest) { /* ... */ }
export function fetchLogout() { /* ... */ }
export function fetchCaptchaPng() { /* ... */ }

// src/service/api/system/user/index.ts
export interface UserQueryBo { /* ... */ }
export interface UserVo { /* ... */ }
export function getUserList(params: UserQueryBo) { /* ... */ }
export function addUser(data: UserVo) { /* ... */ }

强制要求

  • 每个API模块独立维护和导入
  • 导入路径指向具体功能模块
  • 按功能拆分API到不同模块目录
  • 类型定义与API函数放在同一模块目录下

严格禁止行为

  • 创建 system.ts 等聚合导出文件
  • 在单一文件中混合多个业务模块的API
  • 从聚合文件导入API (如 from '@/service/api/system')
  • 创建功能不明确的API文件
  • 在页面组件中重复定义类型应从API模块导入

📝 类型定义组织规范(强制要求)

类型定义必须与API模块对应按功能模块组织禁止在页面组件中重复定义类型

类型文件组织原则

  1. 就近原则: 类型定义与使用它们的API函数在同一模块
  2. 单一职责: 每个types.ts文件只包含一个业务模块的类型
  3. 统一导出: API模块的index.ts必须重新导出types.ts中的类型
  4. 禁止重复: 页面组件不得重复定义已有类型

正确的类型组织示例

// src/service/api/system/user/types.ts
// src/service/api/system/user/index.ts
import type { UserQueryBo, UserSearchForm, UserVo } from './types'

// 页面组件中的正确使用方式
import {
  getUserList

} from '@/service/api/system/user'
import type { UserSearchForm, UserVo } from '@/service/api/system/user'

export interface UserVo {
  userId: number
  loginName: string
  userName: string
  // ...其他字段
}

export interface UserQueryBo {
  pageNo?: number
  pageSize?: number
  loginName?: string
  // ...其他查询条件
}

export interface UserSearchForm {
  loginName?: string
  userName?: string
  timeRange?: [number, number] | null
  // ...其他搜索字段
}

// 重新导出类型供外部使用
export type { UserQueryBo, UserSearchForm, UserVo } from './types'

// API函数定义
export function getUserList(params: UserQueryBo) { /* ... */ }

// 使用统一类型,不重复定义
const tableData = ref<UserVo[]>([])
const searchForm = ref<UserSearchForm>({})

类型命名规范

  • 请求参数类型: 实体名QueryBo, 实体名CreateBo, 实体名UpdateBo
  • 响应数据类型: 实体名Vo
  • 分页结果类型: Page实体名Vo
  • 表单类型: 实体名SearchForm, 实体名Form

状态管理

  1. 新增状态优先考虑使用现有store
  2. 需要持久化的状态使用pinia-plugin-persist
  3. 计算属性优先使用computed缓存

组件开发

  1. 优先使用Naive UI组件
  2. 自定义组件放在 src/components/
  3. 使用TypeScript定义组件Props和Emits

🔥 自定义弹框组件规范(强制要求)

在开发任何新页面或功能时,如果需要使用弹框/模态框,必须使用项目封装的 NovaDialog 组件,严禁使用 Naive UI 原生的 n-modal 组件。

NovaDialog 组件位置

  • 组件文件:src/components/common/NovaDialog.vue
  • 这是项目专门封装的统一弹框组件

使用方式

1. 导入组件

import NovaDialog from '@/components/common/NovaDialog.vue'

2. 创建弹框引用

// 为每个弹框创建独立的引用
const userDialogRef = ref()
const editDialogRef = ref()
const deleteDialogRef = ref()

3. 模板中使用

<template>
  <!-- 用户表单弹框示例 -->
  <NovaDialog
    ref="userDialogRef"
    :title="modalTitle"
    :width="800"
    height="auto"
    confirm-text="确定"
    cancel-text="取消"
    @nova-confirm="handleSubmit"
    @nova-cancel="handleCancel"
  >
    <template #content>
      <div class="px-3 py-2">
        <!-- 弹框内容 -->
        <n-form ref="formRef" :model="formData">
          <!-- 表单内容 -->
        </n-form>
      </div>
    </template>
  </NovaDialog>

  <!-- 仅查看类弹框示例无确认按钮 -->
  <NovaDialog
    ref="viewDialogRef"
    title="查看详情"
    :width="600"
    height="auto"
    cancel-text="关闭"
    :show-confirm="false"
    @nova-cancel="handleClose"
  >
    <template #content>
      <div class="p-3">
        <!-- 查看内容 -->
      </div>
    </template>
  </NovaDialog>
</template>

4. 控制弹框显示/隐藏

// 显示弹框
function handleAdd() {
  modalTitle.value = '新增用户'
  // 初始化表单数据
  formData.value = { /* ... */ }
  userDialogRef.value?.novaOpen()
}

// 隐藏弹框
function handleCancel() {
  userDialogRef.value?.novaClose()
}

// 提交成功后关闭弹框
async function handleSubmit() {
  try {
    await submitForm()
    coiMsgSuccess('操作成功')
    userDialogRef.value?.novaClose()
  }
  catch (error) {
    coiMsgError('操作失败')
  }
}

NovaDialog 核心属性

属性 类型 默认值 说明
title string '' 弹框标题
width number 500 弹框宽度(px)
height string 'auto' 弹框高度
confirm-text string '确定' 确认按钮文字
cancel-text string '取消' 取消按钮文字
show-confirm boolean true 是否显示确认按钮
show-cancel boolean true 是否显示取消按钮

NovaDialog 核心事件

事件 说明
@nova-confirm 点击确认按钮时触发
@nova-cancel 点击取消按钮时触发

NovaDialog 核心方法

方法 说明
novaOpen() 显示弹框
novaClose() 隐藏弹框

插槽支持

插槽 说明
#header 自定义弹框头部内容
#content 弹框主体内容(必需)

已完成的集成示例

  • 角色管理页面:src/views/system/role/index.vue
  • 用户管理页面:src/views/system/user/index.vue

严格禁止行为

  • 使用 n-modal 组件创建新弹框
  • 使用 v-model:show 控制弹框显示/隐藏
  • 忽略 NovaDialog 组件直接使用原生弹框

为什么必须使用 NovaDialog

  1. 统一用户体验:确保所有弹框具有一致的视觉风格和交互行为
  2. 维护便利性:统一的组件便于后续样式调整和功能增强
  3. 团队协作统一的组件API减少开发人员的学习成本
  4. 质量保证封装的组件经过充分测试避免重复开发和潜在bug

📝 Vue3组件文件结构规范强制要求

所有Vue3组件必须严格按照以下顺序组织代码块

组件代码块排布顺序

<!-- Vue 组件文件结构示例 -->
<template>
  <!-- 模板内容 -->
</template>

<script setup lang="ts">
// 脚本内容
</script>

<style scoped>
/* 样式内容 */
</style>

强制要求

  • 必须按照 templatescriptstyle 的顺序排布
  • 使用 <script setup lang="ts"> 语法
  • 样式块使用 scoped 属性确保样式隔离
  • 每个代码块之间保持一个空行间隔

严格禁止行为

  • 任何其他顺序的代码块排布
  • 使用 <script> 而非 <script setup>
  • 省略 lang="ts" 属性
  • 在全局组件中省略 scoped 属性

文档资源

项目包含完整的文档系统:

  • doc/auth-system.md - 认证系统文档
  • doc/permission-system.md - 权限系统文档
  • doc/router-system.md - 路由系统文档
  • doc/architecture.md - 整体架构文档

API接口

项目使用ApiFox进行接口Mock在线文档https://nova-admin.apifox.cn

开发环境要求

  • Node.js 21.x
  • pnpm 10.x
  • 现代浏览器支持ES6+