|
|
@@ -0,0 +1,568 @@
|
|
|
+# 开发者指南
|
|
|
+
|
|
|
+## 📖 目录
|
|
|
+- [开发环境搭建](#开发环境搭建)
|
|
|
+- [项目结构说明](#项目结构说明)
|
|
|
+- [代码规范](#代码规范)
|
|
|
+- [开发流程](#开发流程)
|
|
|
+- [常用功能示例](#常用功能示例)
|
|
|
+- [调试技巧](#调试技巧)
|
|
|
+- [常见问题](#常见问题)
|
|
|
+
|
|
|
+## 🛠️ 开发环境搭建
|
|
|
+
|
|
|
+### 1. 安装必要工具
|
|
|
+```bash
|
|
|
+# 安装 Node.js (推荐使用 nvm)
|
|
|
+nvm install 16
|
|
|
+nvm use 16
|
|
|
+
|
|
|
+# 安装 pnpm (可选,替代 npm)
|
|
|
+npm install -g pnpm
|
|
|
+
|
|
|
+# 安装依赖
|
|
|
+npm install
|
|
|
+# 或
|
|
|
+pnpm install
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 配置开发环境
|
|
|
+```bash
|
|
|
+# 复制环境变量文件
|
|
|
+cp .env.development.example .env.development
|
|
|
+
|
|
|
+# 编辑 .env.development
|
|
|
+# 配置后端 API 地址
|
|
|
+VITE_API_URL=http://localhost:8000
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 启动开发服务器
|
|
|
+```bash
|
|
|
+npm run dev
|
|
|
+```
|
|
|
+
|
|
|
+## 📁 项目结构说明
|
|
|
+
|
|
|
+### 核心目录
|
|
|
+
|
|
|
+#### `/src/api/` - API 接口
|
|
|
+```typescript
|
|
|
+// 示例:src/api/login/index.ts
|
|
|
+import request from '/@/utils/service'
|
|
|
+
|
|
|
+export function login(data: any) {
|
|
|
+ return request({
|
|
|
+ url: '/api/system/auth/login/',
|
|
|
+ method: 'post',
|
|
|
+ data
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### `/src/views/` - 页面视图
|
|
|
+```
|
|
|
+views/
|
|
|
+├── position/ # 职位管理页面
|
|
|
+│ ├── list/ # 职位列表
|
|
|
+│ ├── detail/ # 职位详情
|
|
|
+│ └── crud.tsx # CRUD 配置
|
|
|
+├── questionBank/ # 题库管理
|
|
|
+├── talent/ # 人才管理
|
|
|
+└── system/ # 系统管理
|
|
|
+```
|
|
|
+
|
|
|
+#### `/src/components/` - 公共组件
|
|
|
+- `auth/` - 权限控制组件
|
|
|
+- `editor/` - 富文本编辑器
|
|
|
+- `fileSelector/` - 文件选择器
|
|
|
+- `table/` - 表格组件
|
|
|
+
|
|
|
+#### `/src/utils/` - 工具函数
|
|
|
+- `request.ts` - 请求封装
|
|
|
+- `message.ts` - 消息提示
|
|
|
+- `storage.ts` - 本地存储
|
|
|
+- `formatTime.ts` - 时间格式化
|
|
|
+
|
|
|
+#### `/src/stores/` - 状态管理
|
|
|
+```typescript
|
|
|
+// 示例:src/stores/userInfo.ts
|
|
|
+import { defineStore } from 'pinia'
|
|
|
+
|
|
|
+export const useUserInfo = defineStore('userInfo', {
|
|
|
+ state: () => ({
|
|
|
+ userName: '',
|
|
|
+ avatar: ''
|
|
|
+ }),
|
|
|
+ persist: {
|
|
|
+ enabled: true
|
|
|
+ }
|
|
|
+})
|
|
|
+```
|
|
|
+
|
|
|
+## 📝 代码规范
|
|
|
+
|
|
|
+### 1. TypeScript 规范
|
|
|
+```typescript
|
|
|
+// ✅ 正确:明确的类型定义
|
|
|
+interface UserInfo {
|
|
|
+ id: number
|
|
|
+ name: string
|
|
|
+ email: string
|
|
|
+}
|
|
|
+
|
|
|
+function getUserInfo(id: number): Promise<UserInfo> {
|
|
|
+ return request({
|
|
|
+ url: `/api/user/${id}/`,
|
|
|
+ method: 'get'
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// ❌ 错误:使用 any
|
|
|
+function getUserInfo(id: any): any {
|
|
|
+ // ...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Vue 3 规范
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <div class="container">
|
|
|
+ <el-table :data="tableData">
|
|
|
+ <el-table-column prop="name" label="名称" />
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue'
|
|
|
+
|
|
|
+interface TableItem {
|
|
|
+ name: string
|
|
|
+}
|
|
|
+
|
|
|
+const tableData = ref<TableItem[]>([])
|
|
|
+
|
|
|
+const getData = async () => {
|
|
|
+ // 获取数据逻辑
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+</style>
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 命名规范
|
|
|
+- **组件名**:PascalCase - `UserManagement.vue`
|
|
|
+- **文件名**:kebab-case - `user-info.vue`
|
|
|
+- **变量/函数**:camelCase - `getUserInfo`
|
|
|
+- **常量**:UPPER_SNAKE_CASE - `API_BASE_URL`
|
|
|
+- **类型/接口**:PascalCase - `UserInfo`
|
|
|
+
|
|
|
+### 4. 文件组织
|
|
|
+```
|
|
|
+component/
|
|
|
+ ├── index.vue # 主组件
|
|
|
+ ├── components/ # 子组件
|
|
|
+ │ ├── Form.vue
|
|
|
+ │ └── List.vue
|
|
|
+ ├── api.ts # API 接口
|
|
|
+ ├── types.ts # 类型定义
|
|
|
+ └── crud.tsx # CRUD 配置(如需要)
|
|
|
+```
|
|
|
+
|
|
|
+## 🔄 开发流程
|
|
|
+
|
|
|
+### 1. 创建新功能模块
|
|
|
+
|
|
|
+#### 步骤一:创建目录结构
|
|
|
+```bash
|
|
|
+src/views/newFeature/
|
|
|
+ ├── index.vue # 主页面
|
|
|
+ ├── api.ts # API 接口
|
|
|
+ ├── types.ts # 类型定义
|
|
|
+ └── components/ # 子组件
|
|
|
+ └── Dialog.vue
|
|
|
+```
|
|
|
+
|
|
|
+#### 步骤二:定义类型
|
|
|
+```typescript
|
|
|
+// src/views/newFeature/types.ts
|
|
|
+export interface FeatureItem {
|
|
|
+ id: number
|
|
|
+ name: string
|
|
|
+ description: string
|
|
|
+ status: 'active' | 'inactive'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 步骤三:定义 API
|
|
|
+```typescript
|
|
|
+// src/views/newFeature/api.ts
|
|
|
+import request from '/@/utils/service'
|
|
|
+
|
|
|
+export function getFeatureList(params: any) {
|
|
|
+ return request({
|
|
|
+ url: '/api/feature/',
|
|
|
+ method: 'get',
|
|
|
+ params
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+export function createFeature(data: any) {
|
|
|
+ return request({
|
|
|
+ url: '/api/feature/',
|
|
|
+ method: 'post',
|
|
|
+ data
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 步骤四:创建页面
|
|
|
+```vue
|
|
|
+<!-- src/views/newFeature/index.vue -->
|
|
|
+<template>
|
|
|
+ <div class="feature-page">
|
|
|
+ <el-table :data="tableData">
|
|
|
+ <el-table-column prop="name" label="名称" />
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
+import { getFeatureList } from './api'
|
|
|
+import type { FeatureItem } from './types'
|
|
|
+
|
|
|
+const tableData = ref<FeatureItem[]>([])
|
|
|
+
|
|
|
+const loadData = async () => {
|
|
|
+ const res = await getFeatureList({})
|
|
|
+ tableData.value = res.data
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadData()
|
|
|
+})
|
|
|
+</script>
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 使用 FastCRUD
|
|
|
+```typescript
|
|
|
+// src/views/feature/crud.tsx
|
|
|
+import { useCrud } from '@fast-crud/fast-crud'
|
|
|
+import { createFeature, updateFeature, deleteFeature } from './api'
|
|
|
+
|
|
|
+export default useCrud({
|
|
|
+ async doAdd({ form }) {
|
|
|
+ await createFeature(form)
|
|
|
+ },
|
|
|
+ async doEdit({ form }) {
|
|
|
+ await updateFeature(form.id, form)
|
|
|
+ },
|
|
|
+ async doDel({ row }) {
|
|
|
+ await deleteFeature(row.id)
|
|
|
+ }
|
|
|
+})
|
|
|
+```
|
|
|
+
|
|
|
+## 💡 常用功能示例
|
|
|
+
|
|
|
+### 0. 项目中的实际示例
|
|
|
+查看现有业务模块代码以了解项目风格:
|
|
|
+- **职位管理**:`src/views/position/` - 复杂的职位详情编辑
|
|
|
+- **题库管理**:`src/views/questionBank/` - 题目CRUD和管理
|
|
|
+- **系统管理**:`src/views/system/account/` - 用户管理示例
|
|
|
+- **求职申请**:`src/views/JobApplication/` - 申请状态管理
|
|
|
+
|
|
|
+### 1. 权限控制
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <!-- 按钮权限 -->
|
|
|
+ <auths value="system:user:add">
|
|
|
+ <el-button @click="handleAdd">新增</el-button>
|
|
|
+ </auths>
|
|
|
+
|
|
|
+ <!-- 字段权限 -->
|
|
|
+ <auth value="system:user:name:view">
|
|
|
+ <span>{{ userInfo.name }}</span>
|
|
|
+ </auth>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 消息提示
|
|
|
+```typescript
|
|
|
+import { successMessage, warningMessage, errorMessage } from '/@/utils/message'
|
|
|
+
|
|
|
+// 成功提示
|
|
|
+successMessage('操作成功')
|
|
|
+
|
|
|
+// 警告提示
|
|
|
+warningMessage('请填写完整信息')
|
|
|
+
|
|
|
+// 错误提示
|
|
|
+errorMessage('操作失败')
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 表格操作
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <fs-table>
|
|
|
+ <fs-table-column prop="name" label="名称" />
|
|
|
+ <template #action="{ row }">
|
|
|
+ <el-button @click="handleEdit(row)">编辑</el-button>
|
|
|
+ <el-button @click="handleDelete(row)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </fs-table>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 表单验证
|
|
|
+```vue
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+
|
|
|
+const form = ref({
|
|
|
+ name: '',
|
|
|
+ email: ''
|
|
|
+})
|
|
|
+
|
|
|
+const rules = {
|
|
|
+ name: [
|
|
|
+ { required: true, message: '请输入名称', trigger: 'blur' }
|
|
|
+ ],
|
|
|
+ email: [
|
|
|
+ { required: true, message: '请输入邮箱', trigger: 'blur' },
|
|
|
+ { type: 'email', message: '请输入正确的邮箱', trigger: 'blur' }
|
|
|
+ ]
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <el-form :model="form" :rules="rules" ref="formRef">
|
|
|
+ <el-form-item label="名称" prop="name">
|
|
|
+ <el-input v-model="form.name" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="邮箱" prop="email">
|
|
|
+ <el-input v-model="form.email" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+### 5. 文件上传
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <fs-uploader v-model="fileList" />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue'
|
|
|
+
|
|
|
+const fileList = ref([])
|
|
|
+</script>
|
|
|
+```
|
|
|
+
|
|
|
+### 6. 富文本编辑器
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <fs-editor v-model="content" />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue'
|
|
|
+
|
|
|
+const content = ref('')
|
|
|
+</script>
|
|
|
+```
|
|
|
+
|
|
|
+### 7. 字典数据获取
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <el-select v-model="value" dict="status">
|
|
|
+ <el-option label="启用" value="1" />
|
|
|
+ <el-option label="禁用" value="0" />
|
|
|
+ </el-select>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+### 8. 本地存储
|
|
|
+```typescript
|
|
|
+import { Local } from '/@/utils/storage'
|
|
|
+
|
|
|
+// 保存数据
|
|
|
+Local.set('key', { data: 'value' })
|
|
|
+
|
|
|
+// 获取数据
|
|
|
+const data = Local.get('key')
|
|
|
+
|
|
|
+// 删除数据
|
|
|
+Local.remove('key')
|
|
|
+```
|
|
|
+
|
|
|
+### 9. 路由跳转
|
|
|
+```typescript
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+
|
|
|
+const router = useRouter()
|
|
|
+
|
|
|
+// 普通跳转
|
|
|
+router.push('/position/list')
|
|
|
+
|
|
|
+// 带参数跳转
|
|
|
+router.push({
|
|
|
+ path: '/position/detail',
|
|
|
+ query: { id: 123 }
|
|
|
+})
|
|
|
+
|
|
|
+// 在新标签页打开
|
|
|
+window.open(`/position/detail?id=123`)
|
|
|
+```
|
|
|
+
|
|
|
+### 10. 表格数据操作
|
|
|
+```vue
|
|
|
+<script setup lang="ts">
|
|
|
+const tableData = ref([])
|
|
|
+const loading = ref(false)
|
|
|
+
|
|
|
+const loadData = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const res = await request({
|
|
|
+ url: '/api/position/list/',
|
|
|
+ method: 'get',
|
|
|
+ params: { page: 1, limit: 20 }
|
|
|
+ })
|
|
|
+ tableData.value = res.data
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 刷新数据
|
|
|
+const refresh = () => {
|
|
|
+ loadData()
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <el-table :data="tableData" v-loading="loading">
|
|
|
+ <el-table-column prop="name" label="职位名称" />
|
|
|
+ <el-table-column prop="status" label="状态" />
|
|
|
+ </el-table>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+## 🐛 调试技巧
|
|
|
+
|
|
|
+### 1. Vue DevTools
|
|
|
+安装 Vue DevTools 浏览器插件,用于:
|
|
|
+- 查看组件树
|
|
|
+- 检查组件状态
|
|
|
+- 追踪组件更新
|
|
|
+- 性能分析
|
|
|
+
|
|
|
+### 2. 控制台调试
|
|
|
+```typescript
|
|
|
+// 打印日志
|
|
|
+console.log('Data:', data)
|
|
|
+
|
|
|
+// 打印表格
|
|
|
+console.table(dataList)
|
|
|
+
|
|
|
+// 断点调试
|
|
|
+debugger
|
|
|
+
|
|
|
+// 打印组件实例
|
|
|
+console.log(this.$refs.myComponent)
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Vite HMR
|
|
|
+修改代码后,Vite 会自动热更新,无需手动刷新页面。
|
|
|
+
|
|
|
+### 4. 网络请求调试
|
|
|
+使用浏览器开发者工具的 Network 面板:
|
|
|
+- 查看请求/响应
|
|
|
+- 检查请求头
|
|
|
+- 查看响应数据
|
|
|
+
|
|
|
+## ❓ 常见问题
|
|
|
+
|
|
|
+### 1. 依赖安装失败
|
|
|
+```bash
|
|
|
+# 清除缓存
|
|
|
+npm cache clean --force
|
|
|
+
|
|
|
+# 删除 node_modules
|
|
|
+rm -rf node_modules
|
|
|
+
|
|
|
+# 重新安装
|
|
|
+npm install
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 端口被占用
|
|
|
+```bash
|
|
|
+# 修改 vite.config.ts 中的端口
|
|
|
+server: {
|
|
|
+ port: 3000 // 改为其他端口
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3. TypeScript 类型错误
|
|
|
+```bash
|
|
|
+# 重新生成类型声明
|
|
|
+npm run type-check
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 样式不生效
|
|
|
+- 检查样式文件导入
|
|
|
+- 检查 scoped 属性
|
|
|
+- 检查 Tailwind CSS 配置
|
|
|
+
|
|
|
+### 5. 路由跳转失败
|
|
|
+```typescript
|
|
|
+// 使用 router.push
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+const router = useRouter()
|
|
|
+router.push('/path')
|
|
|
+```
|
|
|
+
|
|
|
+## 📚 参考资料
|
|
|
+
|
|
|
+- [Vue 3 官方文档](https://cn.vuejs.org/)
|
|
|
+- [Element Plus 文档](https://element-plus.org/)
|
|
|
+- [FastCRUD 文档](http://fast-crud.docmirror.cn/)
|
|
|
+- [Pinia 文档](https://pinia.vuejs.org/)
|
|
|
+- [TypeScript 文档](https://www.typescriptlang.org/)
|
|
|
+
|
|
|
+## 🎯 最佳实践
|
|
|
+
|
|
|
+### 1. 组件拆分
|
|
|
+保持组件精简,单一职责。
|
|
|
+
|
|
|
+### 2. 代码复用
|
|
|
+提取公共逻辑到 hooks 或 utils。
|
|
|
+
|
|
|
+### 3. 性能优化
|
|
|
+- 使用 v-if 替代 v-show
|
|
|
+- 使用 computed 缓存计算
|
|
|
+- 大列表使用虚拟滚动
|
|
|
+
|
|
|
+### 4. 错误处理
|
|
|
+```typescript
|
|
|
+try {
|
|
|
+ await saveData()
|
|
|
+ successMessage('保存成功')
|
|
|
+} catch (error) {
|
|
|
+ errorMessage('保存失败')
|
|
|
+ console.error(error)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+**提示**:遇到问题时,先查阅文档,然后查看已有代码实现,最后在团队中讨论。
|
|
|
+
|