# 安装 Node.js (推荐使用 nvm)
nvm install 16
nvm use 16
# 安装 pnpm (可选,替代 npm)
npm install -g pnpm
# 安装依赖
npm install
# 或
pnpm install
# 复制环境变量文件
cp .env.development.example .env.development
# 编辑 .env.development
# 配置后端 API 地址
VITE_API_URL=http://localhost:8000
npm run dev
/src/api/ - API 接口// 示例: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/ - 状态管理// 示例:src/stores/userInfo.ts
import { defineStore } from 'pinia'
export const useUserInfo = defineStore('userInfo', {
state: () => ({
userName: '',
avatar: ''
}),
persist: {
enabled: true
}
})
// ✅ 正确:明确的类型定义
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 {
// ...
}
<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>
UserManagement.vueuser-info.vuegetUserInfoAPI_BASE_URLUserInfocomponent/
├── index.vue # 主组件
├── components/ # 子组件
│ ├── Form.vue
│ └── List.vue
├── api.ts # API 接口
├── types.ts # 类型定义
└── crud.tsx # CRUD 配置(如需要)
src/views/newFeature/
├── index.vue # 主页面
├── api.ts # API 接口
├── types.ts # 类型定义
└── components/ # 子组件
└── Dialog.vue
// src/views/newFeature/types.ts
export interface FeatureItem {
id: number
name: string
description: string
status: 'active' | 'inactive'
}
// 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
})
}
<!-- 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>
// 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)
}
})
查看现有业务模块代码以了解项目风格:
src/views/position/ - 复杂的职位详情编辑src/views/questionBank/ - 题目CRUD和管理src/views/system/account/ - 用户管理示例src/views/JobApplication/ - 申请状态管理<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>
import { successMessage, warningMessage, errorMessage } from '/@/utils/message'
// 成功提示
successMessage('操作成功')
// 警告提示
warningMessage('请填写完整信息')
// 错误提示
errorMessage('操作失败')
<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>
<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>
<template>
<fs-uploader v-model="fileList" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const fileList = ref([])
</script>
<template>
<fs-editor v-model="content" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
const content = ref('')
</script>
<template>
<el-select v-model="value" dict="status">
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
</el-select>
</template>
import { Local } from '/@/utils/storage'
// 保存数据
Local.set('key', { data: 'value' })
// 获取数据
const data = Local.get('key')
// 删除数据
Local.remove('key')
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`)
<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>
安装 Vue DevTools 浏览器插件,用于:
// 打印日志
console.log('Data:', data)
// 打印表格
console.table(dataList)
// 断点调试
debugger
// 打印组件实例
console.log(this.$refs.myComponent)
修改代码后,Vite 会自动热更新,无需手动刷新页面。
使用浏览器开发者工具的 Network 面板:
# 清除缓存
npm cache clean --force
# 删除 node_modules
rm -rf node_modules
# 重新安装
npm install
# 修改 vite.config.ts 中的端口
server: {
port: 3000 // 改为其他端口
}
# 重新生成类型声明
npm run type-check
// 使用 router.push
import { useRouter } from 'vue-router'
const router = useRouter()
router.push('/path')
保持组件精简,单一职责。
提取公共逻辑到 hooks 或 utils。
try {
await saveData()
successMessage('保存成功')
} catch (error) {
errorMessage('保存失败')
console.error(error)
}
提示:遇到问题时,先查阅文档,然后查看已有代码实现,最后在团队中讨论。