Vue项目代码详细讲解
1. jsconfig.json - JavaScript配置文件
{"compilerOptions": { // 编译器选项配置"target": "es5", // 编译目标:将代码编译为ES5版本,确保更好的浏览器兼容性"module": "esnext", // 模块系统:使用最新的ES模块系统"baseUrl": "./", // 基础路径:设置为项目根目录"moduleResolution": "node", // 模块解析策略:使用Node.js的模块解析方式"paths": { // 路径映射配置"@/*": [ // 定义@符号作为src目录的别名"src/*" // @/xxx 会被解析为 src/xxx]},"lib": [ // 包含的类型定义库"esnext", // 包含最新的ES特性"dom", // 包含DOM相关的类型定义"dom.iterable", // 包含DOM可迭代对象的类型定义"scripthost" // 包含Windows Script Host的类型定义]}
}
作用说明:
- 这个文件为VSCode等IDE提供JavaScript项目的智能提示
- 配置路径别名让导入更简洁(使用@代替src)
- 确保IDE能正确理解项目的模块系统和语法特性
2. vue.config.js - Vue CLI配置文件
// vue.config.js
const { defineConfig } = require("@vue/cli-service"); // 导入Vue CLI的配置函数module.exports = defineConfig({ // 导出配置对象transpileDependencies: true, // 转译所有依赖包(包括node_modules中的)// 开发服务器配置devServer: {port: 8081, // 前端开发服务器端口host: '0.0.0.0', // 允许任何主机访问(不仅限localhost)open: false, // 启动时不自动打开浏览器allowedHosts: 'all', // 允许所有主机名访问(解决主机名验证问题)proxy: { // 代理配置,解决开发环境的跨域问题"/api": { // 匹配所有以/api开头的请求target: "http://localhost:8080", // 代理目标:后端服务器地址changeOrigin: true, // 改变请求头中的origin(必要的跨域配置)pathRewrite: { // 路径重写规则"^/api": "/api", // 保持/api前缀不变},},},},// 生产环境配置productionSourceMap: false, // 生产环境不生成source map(减小打包体积,提高安全性)// 开发环境生成 source map(用于调试)configureWebpack: {devtool: process.env.NODE_ENV === 'development' ? 'source-map' : false// 开发环境使用source-map便于调试,生产环境关闭},// CSS相关配置css: {loaderOptions: { // CSS加载器选项scss: { // SCSS配置// 全局引入scss变量文件(当前被注释)// additionalData: `@import "@/styles/variables.scss";`// 如果启用,会在每个组件的样式中自动引入这个文件},},},
});
关键配置说明:
- devServer.proxy:解决前后端分离开发时的跨域问题
- host: ‘0.0.0.0’:允许局域网内其他设备访问
- productionSourceMap:生产环境关闭源码映射,保护源代码
3. .eslintrc.js - ESLint配置文件
module.exports = {root: true, // 标识这是项目根目录的ESLint配置,停止向上查找env: { // 指定代码运行环境node: true // Node.js环境(支持require、module等)},extends: [ // 继承的规则集'plugin:vue/vue3-essential', // Vue3基本规则'eslint:recommended' // ESLint推荐规则],parserOptions: { // 解析器选项parser: '@babel/eslint-parser' // 使用Babel解析器(支持最新JS语法)},rules: { // 自定义规则'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',// console语句:生产环境警告,开发环境允许'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',// debugger语句:生产环境警告,开发环境允许'vue/multi-word-component-names': 'off'// 关闭Vue组件名必须多个单词的规则(允许单词组件名如Profile)}
}
规则说明:
- 开发环境允许console和debugger便于调试
- 生产环境对这些语句发出警告,提醒清理
- 关闭组件命名限制,提高开发灵活性
4. babel.config.js - Babel配置文件
module.exports = {presets: ["@vue/cli-plugin-babel/preset"], // 使用Vue CLI提供的Babel预设// 包含了Vue项目所需的所有Babel插件和配置
};
作用:
- 将现代JavaScript转译为兼容性更好的版本
- 支持Vue的JSX语法和其他特性
5. Profile.vue - 个人资料页面组件
模板部分解析
<template><div class="profile"> <!-- 页面根容器 --><el-card> <!-- Element Plus卡片组件 --><template #header> <!-- 卡片头部插槽 --><div class="card-header"><span>个人信息</span><el-button type="primary" size="small" @click="isEdit = !isEdit"><!-- 切换编辑状态的按钮,根据状态显示不同文字 -->{{ isEdit ? "取消编辑" : "编辑信息" }}</el-button></div></template><el-formref="profileFormRef" <!-- 表单引用,用于调用表单方法 -->:model="profileForm" <!-- 绑定表单数据对象 -->:rules="rules" <!-- 表单验证规则 -->:disabled="!isEdit" <!-- 非编辑状态时禁用所有输入 -->label-width="100px" <!-- 标签宽度 -->style="max-width: 600px" <!-- 限制表单最大宽度 -->><!-- 用户ID(始终禁用) --><el-form-item label="用户ID"><el-input v-model="profileForm.id" disabled /></el-form-item><!-- 用户名(始终禁用) --><el-form-item label="用户名"><el-input v-model="profileForm.username" disabled /></el-form-item><!-- 邮箱(可编辑,带验证) --><el-form-item label="邮箱" prop="email"><el-input v-model="profileForm.email" /></el-form-item><!-- 手机号(可编辑,带验证) --><el-form-item label="手机号" prop="phone"><el-input v-model="profileForm.phone" /></el-form-item><!-- 分数显示(使用标签展示,颜色根据分数变化) --><el-form-item label="分数"><el-tag :type="getScoreType(profileForm.score)" size="large">{{ profileForm.score }} 分</el-tag></el-form-item><!-- 账号状态显示 --><el-form-item label="账号状态"><el-tag :type="profileForm.status === 1 ? 'success' : 'danger'">{{ profileForm.status === 1 ? "正常" : "已禁用" }}</el-tag></el-form-item><!-- 时间戳显示 --><el-form-item label="注册时间"><el-input :value="formatDate(profileForm.createTime)" disabled /></el-form-item><el-form-item label="更新时间"><el-input :value="formatDate(profileForm.updateTime)" disabled /></el-form-item><!-- 编辑模式下的操作按钮 --><el-form-item v-if="isEdit"><el-button type="primary" @click="saveProfile">保存修改</el-button><el-button @click="cancelEdit">取消</el-button></el-form-item></el-form></el-card><!-- 修改密码卡片 --><el-card style="margin-top: 20px"><template #header><span>修改密码</span></template><el-formref="passwordFormRef":model="passwordForm":rules="passwordRules"label-width="100px"style="max-width: 600px"><!-- 原密码输入 --><el-form-item label="原密码" prop="oldPassword"><el-inputv-model="passwordForm.oldPassword"type="password" <!-- 密码类型输入框 -->show-password <!-- 显示密码切换按钮 -->/></el-form-item><!-- 新密码输入 --><el-form-item label="新密码" prop="newPassword"><el-inputv-model="passwordForm.newPassword"type="password"show-password/></el-form-item><!-- 确认密码输入 --><el-form-item label="确认密码" prop="confirmPassword"><el-inputv-model="passwordForm.confirmPassword"type="password"show-password/></el-form-item><!-- 密码表单操作按钮 --><el-form-item><el-button type="primary" @click="handleChangePassword">修改密码</el-button><el-button @click="resetPasswordForm">重置</el-button></el-form-item></el-form></el-card></div>
</template>
Script部分解析
<script setup>
// 使用Vue 3的Composition API + setup语法糖
import { ref, reactive, onMounted } from "vue"; // Vue核心响应式API
import { useStore } from "vuex"; // Vuex状态管理
import { useRouter } from "vue-router"; // 路由
import { ElMessage } from "element-plus"; // 消息提示组件
import { getUser, updateUser, changePassword } from "@/api/user"; // API接口const store = useStore(); // 获取Vuex store实例
const router = useRouter(); // 获取路由实例
const currentUser = store.getters.user; // 从store获取当前用户信息// 响应式数据定义
const isEdit = ref(false); // 编辑状态标志
const profileFormRef = ref(); // 个人信息表单引用
const passwordFormRef = ref(); // 密码表单引用// 个人信息表单数据(响应式对象)
const profileForm = reactive({id: "",username: "",email: "",phone: "",score: 0,status: 1,createTime: "",updateTime: "",
});// 修改密码表单数据
const passwordForm = reactive({oldPassword: "",newPassword: "",confirmPassword: "",
});// 表单验证规则
const rules = {email: [{ required: true, message: "请输入邮箱", trigger: "blur" }, // 必填{ type: "email", message: "请输入正确的邮箱地址", trigger: "blur" }, // 邮箱格式],phone: [{pattern: /^1[3-9]\d{9}$/, // 手机号正则:1开头,第二位3-9,后面9位数字message: "请输入正确的手机号",trigger: "blur", // 失去焦点时触发验证},],
};// 密码表单验证规则
const passwordRules = {oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],newPassword: [{ required: true, message: "请输入新密码", trigger: "blur" },{ min: 6, max: 20, message: "密码长度在 6 到 20 个字符", trigger: "blur" },],confirmPassword: [{ required: true, message: "请再次输入新密码", trigger: "blur" },{validator: (rule, value, callback) => { // 自定义验证器if (value !== passwordForm.newPassword) {callback(new Error("两次输入密码不一致"));} else {callback(); // 验证通过}},trigger: "blur",},],
};// 获取分数标签类型(根据分数返回不同颜色)
const getScoreType = (score) => {if (score >= 90) return "success"; // 绿色if (score >= 60) return "warning"; // 橙色return "danger"; // 红色
};// 格式化日期时间
const formatDate = (dateStr) => {if (!dateStr) return "";return new Date(dateStr).toLocaleString("zh-CN"); // 转换为中文格式
};// 加载用户信息
const loadUserInfo = async () => {try {const res = await getUser(currentUser.id); // 调用API获取用户信息Object.assign(profileForm, res.data); // 将返回数据合并到表单对象} catch (error) {ElMessage.error("获取用户信息失败");}
};// 保存个人信息
const saveProfile = async () => {const valid = await profileFormRef.value.validate(); // 表单验证if (!valid) return; // 验证失败则返回try {await updateUser(profileForm.id, {email: profileForm.email,phone: profileForm.phone,});// 更新store中的用户信息store.commit("SET_USER", { ...currentUser, ...profileForm });ElMessage.success("保存成功");isEdit.value = false; // 退出编辑模式} catch (error) {ElMessage.error("保存失败");}
};// 取消编辑
const cancelEdit = () => {isEdit.value = false; // 退出编辑模式loadUserInfo(); // 重新加载原始数据
};// 修改密码
const handleChangePassword = async () => {const valid = await passwordFormRef.value.validate();if (!valid) return;try {// 调用修改密码APIawait changePassword(currentUser.id, {oldPassword: passwordForm.oldPassword,newPassword: passwordForm.newPassword,});ElMessage.success("密码修改成功,请重新登录");// 清空密码表单resetPasswordForm();// 1.5秒后退出登录并跳转到登录页setTimeout(async () => {await store.dispatch("logout"); // 调用store的logout actionrouter.push("/login"); // 跳转到登录页}, 1500);} catch (error) {ElMessage.error(error.message || "密码修改失败");}
};// 重置密码表单
const resetPasswordForm = () => {passwordFormRef.value?.resetFields(); // 使用可选链调用表单重置方法
};// 组件挂载时加载用户信息
onMounted(() => {loadUserInfo();
});
</script>
样式部分
<style scoped lang="scss">
.profile { // 页面容器样式.card-header { // 卡片头部样式display: flex; // 弹性布局justify-content: space-between; // 两端对齐align-items: center; // 垂直居中}
}
</style>
6. UserManagement.vue - 用户管理页面组件
模板部分详解
<template><div class="user-management"><!-- 搜索和操作栏 --><el-card class="search-card"><el-row :gutter="20"> <!-- 栅格布局,列间距20px --><el-col :span="6"> <!-- 占6格(共24格) --><el-inputv-model="searchForm.username" <!-- 双向绑定搜索关键词 -->placeholder="请输入用户名"clearable <!-- 显示清空按钮 -->@clear="handleSearch" <!-- 清空时触发搜索 -->@keyup.enter="handleSearch" <!-- 回车键触发搜索 -->><template #prefix> <!-- 前缀图标插槽 --><el-icon><Search /></el-icon></template></el-input></el-col><el-col :span="6"><el-button type="primary" @click="handleSearch">搜索</el-button><el-button @click="resetSearch">重置</el-button></el-col><el-col :span="12" style="text-align: right"> <!-- 右对齐 --><el-button type="primary" @click="showAddDialog"><el-icon><Plus /></el-icon>新增用户</el-button><el-button type="success" @click="handleExport"><el-icon><Download /></el-icon>导出CSV</el-button><!-- 文件上传组件 --><el-upload:show-file-list="false" <!-- 不显示已上传文件列表 -->:before-upload="handleImport" <!-- 上传前的钩子函数 -->accept=".csv" <!-- 只接受CSV文件 -->style="display: inline-block; margin-left: 10px"><el-button type="warning"><el-icon><Upload /></el-icon>导入CSV</el-button></el-upload></el-col></el-row></el-card><!-- 数据表格 --><el-card class="table-card"><el-table:data="tableData" <!-- 绑定表格数据 -->v-loading="loading" <!-- 加载状态 -->stripe <!-- 斑马纹样式 -->style="width: 100%"><!-- 表格列定义 --><el-table-column prop="id" label="ID" width="80" /><el-table-column prop="username" label="用户名" /><el-table-column prop="email" label="邮箱" /><el-table-column prop="phone" label="手机号" /><!-- 分数列(自定义内容) --><el-table-column prop="score" label="分数" width="80"><template #default="{ row }"> <!-- 作用域插槽,row是当前行数据 --><el-tag :type="getScoreType(row.score)">{{ row.score }}</el-tag></template></el-table-column><!-- 状态列(开关组件) --><el-table-column prop="status" label="状态" width="100"><template #default="{ row }"><el-switchv-model="row.status" <!-- 直接绑定行数据 -->:active-value="1" <!-- 开启时的值 -->:inactive-value="0" <!-- 关闭时的值 -->@change="handleStatusChange(row)" <!-- 状态改变时触发 -->/></template></el-table-column><!-- 创建时间列 --><el-table-column prop="createTime" label="创建时间" width="180"><template #default="{ row }">{{ formatDate(row.createTime) }}</template></el-table-column><!-- 操作列 --><el-table-column label="操作" width="200" fixed="right"> <!-- fixed="right"固定在右侧 --><template #default="{ row }"><el-button type="primary" size="small" @click="showEditDialog(row)">编辑</el-button><el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button></template></el-table-column></el-table><!-- 分页组件 --><el-paginationv-model:current-page="pagination.pageNum" <!-- 当前页 -->v-model:page-size="pagination.pageSize" <!-- 每页条数 -->:page-sizes="[10, 20, 50, 100]" <!-- 可选的每页条数 -->:total="pagination.total" <!-- 总条数 -->layout="total, sizes, prev, pager, next, jumper" <!-- 显示的组件 -->@size-change="handleSizeChange" <!-- 每页条数改变 -->@current-change="handleCurrentChange" <!-- 页码改变 -->style="margin-top: 20px"/></el-card><!-- 新增/编辑对话框 --><el-dialogv-model="dialogVisible" <!-- 控制显示隐藏 -->:title="dialogTitle" <!-- 动态标题 -->width="500px"@close="resetForm" <!-- 关闭时重置表单 -->><el-formref="userFormRef":model="userForm":rules="rules"label-width="80px"><el-form-item label="用户名" prop="username"><el-inputv-model="userForm.username"placeholder="请输入用户名":disabled="isEdit" <!-- 编辑时禁用用户名 -->/></el-form-item><!-- 密码项只在新增时显示 --><el-form-item label="密码" prop="password" v-if="!isEdit"><el-inputv-model="userForm.password"type="password"placeholder="请输入密码"show-password/></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="userForm.email" placeholder="请输入邮箱" /></el-form-item><el-form-item label="手机号" prop="phone"><el-input v-model="userForm.phone" placeholder="请输入手机号" /></el-form-item><el-form-item label="分数" prop="score"><el-input-numberv-model="userForm.score":min="0" <!-- 最小值 -->:max="100" <!-- 最大值 -->placeholder="请输入分数"/></el-form-item><el-form-item label="状态" prop="status"><el-radio-group v-model="userForm.status"><el-radio :value="1">启用</el-radio><el-radio :value="0">禁用</el-radio></el-radio-group></el-form-item></el-form><!-- 对话框底部按钮 --><template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="submitForm">确定</el-button></template></el-dialog></div>
</template>
Script部分详解
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {getUserList,createUser,updateUser,deleteUser,exportUsersCsv,importUsersCsv,
} from "@/api/user"; // 导入所有用户相关API// ===== 响应式数据定义 =====
const loading = ref(false); // 加载状态
const tableData = ref([]); // 表格数据
const dialogVisible = ref(false); // 对话框显示状态
const isEdit = ref(false); // 是否编辑模式
const userFormRef = ref(); // 表单引用// 搜索表单
const searchForm = reactive({username: "", // 搜索关键词
});// 分页配置
const pagination = reactive({pageNum: 1, // 当前页pageSize: 10, // 每页条数total: 0, // 总条数
});// 用户表单数据
const userForm = reactive({id: null,username: "",password: "",email: "",phone: "",score: 0,status: 1, // 默认启用
});// 表单验证规则
const rules = {username: [{ required: true, message: "请输入用户名", trigger: "blur" },{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" },],password: [{ required: true, message: "请输入密码", trigger: "blur" },{ min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" },],email: [{ required: true, message: "请输入邮箱", trigger: "blur" },{ type: "email", message: "请输入正确的邮箱地址", trigger: "blur" },],phone: [{pattern: /^1[3-9]\d{9}$/,message: "请输入正确的手机号",trigger: "blur",},],
};// 动态计算的对话框标题
const dialogTitle = ref("新增用户");// ===== 工具函数 =====// 获取分数对应的标签类型
const getScoreType = (score) => {if (score >= 90) return "success";if (score >= 60) return "warning";return "danger";
};// 格式化日期
const formatDate = (dateStr) => {if (!dateStr) return "";const date = new Date(dateStr);return date.toLocaleString("zh-CN");
};// ===== 业务逻辑函数 =====// 获取用户列表
const getList = async () => {loading.value = true; // 开启加载状态try {const params = {pageNum: pagination.pageNum,pageSize: pagination.pageSize,};// 如果有搜索条件,添加到参数中if (searchForm.username) {params.username = searchForm.username;}const res = await getUserList(params); // 调用APItableData.value = res.data.list; // 设置表格数据pagination.total = res.data.total; // 设置总条数} catch (error) {ElMessage.error("获取用户列表失败");} finally {loading.value = false; // 关闭加载状态}
};// 搜索处理
const handleSearch = () => {pagination.pageNum = 1; // 搜索时重置到第一页getList();
};// 重置搜索
const resetSearch = () => {searchForm.username = ""; // 清空搜索条件handleSearch(); // 重新搜索
};// 分页大小改变
const handleSizeChange = () => {pagination.pageNum = 1; // 改变每页条数时重置到第一页getList();
};// 页码改变
const handleCurrentChange = () => {getList(); // 重新获取数据
};// 显示新增对话框
const showAddDialog = () => {isEdit.value = false; // 设置为新增模式dialogTitle.value = "新增用户";dialogVisible.value = true;
};// 显示编辑对话框
const showEditDialog = (row) => {isEdit.value = true; // 设置为编辑模式dialogTitle.value = "编辑用户";Object.assign(userForm, row); // 将行数据复制到表单dialogVisible.value = true;
};// 提交表单
const submitForm = async () => {const valid = await userFormRef.value.validate(); // 表单验证if (!valid) return;try {if (isEdit.value) {// 编辑模式:调用更新APIawait updateUser(userForm.id, userForm);ElMessage.success("更新成功");} else {// 新增模式:调用创建APIawait createUser(userForm);ElMessage.success("创建成功");}dialogVisible.value = false; // 关闭对话框getList(); // 刷新列表} catch (error) {ElMessage.error(error.message || "操作失败");}
};// 重置表单
const resetForm = () => {userFormRef.value?.resetFields(); // 重置表单验证状态// 重置表单数据Object.assign(userForm, {id: null,username: "",password: "",email: "",phone: "",score: 0,status: 1,});
};// 状态切换
const handleStatusChange = async (row) => {try {await updateUser(row.id, { status: row.status }); // 只更新状态ElMessage.success("状态更新成功");} catch (error) {// 失败时恢复原状态row.status = row.status === 1 ? 0 : 1;ElMessage.error("状态更新失败");}
};// 删除用户
const handleDelete = async (row) => {try {// 显示确认对话框await ElMessageBox.confirm(`确定要删除用户 ${row.username} 吗?`,"删除确认",{confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",});await deleteUser(row.id); // 调用删除APIElMessage.success("删除成功");getList(); // 刷新列表} catch (error) {// 用户取消操作不显示错误if (error !== "cancel") {ElMessage.error("删除失败");}}
};// 导出CSV
const handleExport = async () => {try {const res = await exportUsersCsv(); // 调用导出API// 创建Blob对象(二进制大对象)const url = window.URL.createObjectURL(new Blob([res], { type: "text/csv;charset=utf-8;" }));// 创建临时下载链接const link = document.createElement("a");link.href = url;link.download = `用户数据_${new Date().getTime()}.csv`; // 文件名带时间戳document.body.appendChild(link);link.click(); // 触发下载// 清理:移除链接并释放URL对象setTimeout(() => {document.body.removeChild(link);window.URL.revokeObjectURL(url);}, 100);ElMessage.success("导出成功");} catch (error) {console.error("导出错误:", error);ElMessage.error("导出失败: " + (error.message || "未知错误"));}
};// 导入CSV
const handleImport = async (file) => {// 创建FormData对象用于文件上传const formData = new FormData();formData.append("file", file);try {const res = await importUsersCsv(formData); // 调用导入APIconst { successCount, errorCount, errors } = res.data;// 根据导入结果显示不同提示if (errorCount > 0) {ElMessage.warning(`导入完成:成功 ${successCount} 条,失败 ${errorCount} 条`);console.error("导入错误:", errors); // 控制台输出错误详情} else {ElMessage.success(`导入成功:共 ${successCount} 条数据`);}getList(); // 刷新列表} catch (error) {ElMessage.error("导入失败");}return false; // 阻止el-upload的默认上传行为
};// ===== 生命周期 =====
onMounted(() => {getList(); // 组件挂载时加载数据
});
</script>
样式部分
<style scoped lang="scss">
.user-management {.search-card {margin-bottom: 20px; // 搜索卡片底部间距}.table-card {.el-table {margin-bottom: 20px; // 表格底部间距}}
}
</style>
总结
这个Vue 3项目展示了一个完整的用户管理系统,主要特点:
-
技术栈:
- Vue 3 + Composition API
- Element Plus UI框架
- Vuex状态管理
- Vue Router路由
- Axios HTTP请求
-
功能特性:
- 用户的增删改查
- 分页和搜索
- CSV导入导出
- 表单验证
- 权限状态管理
- 个人信息编辑
- 密码修改
-
最佳实践:
- 组件化开发
- 响应式数据管理
- 统一的错误处理
- 代码注释规范
- 模块化的API管理