【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之前端环境搭建
主要参考的博客为:
从零搭建SpringBoot3+Vue3前后端分离项目基座,中小项目可用_springboot+vue3-CSDN博客
记录一下自己的实现过程。
最终实现效果如下:
后端环境搭建参考博客【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之后端环境搭建
2 前端环境搭建
2.1 环境准备
- node安装
- vscode安装
2.2 创建Vue3项目
在将要存放vue3项目的路径打开cmd,使用以下命令创建项目
npm init vue@latest
此时项目创建完成,vscode打开项目目录,在资源目录空白右键,打开终端
执行命令 cnpm install
安装依赖,等待安装完成后执行 cnpm run dev
运行项目
访问路径http://localhost:5173可访问项目
在终端ctrl c 可停止运行项目
项目描述如图
2.3 项目搭建准备
项目中使用组合式API
删除components下的所有文件,将App.vue文件内容修改为如下
<script setup>
</script>
<template>
<router-view></router-view>
</template>
<style scoped>
</style>
2.4 安装Element Plus
-
安装
cnpm install element-plus --save
-
cnpm install @element-plus/icons-vue
2.5 安装axios
- cnpm install axios
2.5.1 配置(创建实例,配置请求,响应拦截器)
在src目录下新建utils,并在utils下创建request.js进行axios配置
src/utils/request.js
// 请求配置
import axios from "axios";
// 定义公共前缀,创建请求实例
// const baseUrl = "http://localhost:8080";
const baseURL = '/api/';
const instance = axios.create({baseURL})
import { ElMessage } from "element-plus"
import { useTokenStore } from "@/stores/token.js"
// 配置请求拦截器
instance.interceptors.request.use(
(config) => {
// 请求前回调
// 添加token
const tokenStore = useTokenStore()
// 判断有无token
if (tokenStore.token) {
config.headers.Authorization = tokenStore.token
}
return config
},
(err) => {
// 请求错误的回调
Promise.reject(err)
}
)
import router from "@/router";
// 添加响应拦截器
instance.interceptors.response.use(
result => {
// 判断业务状态码
if (result.data.code === 1) {
return result.data;
}
// 操作失败
ElMessage.error(result.data.message ? result.data.message : '服务异常')
// 异步操作的状态转换为失败
return Promise.reject(result.data)
},
err => {
// 判断响应状态码, 401为未登录,提示登录并跳转到登录页面
if (err.response.status === 401) {
ElMessage.error('请先登录')
router.push('/login')
} else {
ElMessage.error('服务异常')
}
// 异步操作的状态转换为失败
return Promise.reject(err)
}
)
export default instance
2.5.2 配置跨域
在vite.config.js中加入如下内容
server: {
proxy: {
'/api': { // 获取路径中包含了/api的请求
target: 'http://localhost:9999', // 服务端地址
changeOrigin: true, // 修改源
rewrite:(path) => path.replace(/^\/api/, '') // api 替换为 ''
}
}
}
2.6 Vue Router安装使用
-
安装
cnpm install vue-router@4
-
在src/router/index.js中创建路由器并导出。index.js文件内容如下
// 导入vue-router import {createRouter, createWebHistory} from 'vue-router' // 导入组件 import LoginVue from '@/views/Login.vue' import LayoutVue from '@/views/Layout.vue' import UserList from '@/views/user/UserList.vue' import EditPassword from '@/views/user/EditPassword.vue' import DisplayUser from '@/views/user/DisplayUser.vue' // 定义路由关系 const routes = [ {path: '/login', component: LoginVue}, { path: '/', component: LayoutVue, redirect: '', children: [ {path: '/user/userlist', name: "/user/userlist", component: UserList, meta: { title: "用户列表" },}, {path: '/user/editpassword', name: "/user/editpassword", component: EditPassword, meta: { title: "修改密码" } }, {path: '/user/displayuser', name: "/user/displayuser", component: DisplayUser, meta: { title: "个人信息" }} ] } ] // 创建路由器 const router = createRouter({ history: createWebHistory(), routes: routes }) export default router
-
在vue实例中使用vue-router,修改main.js文件内容为如下
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from '@/router'
import { createPinia } from 'pinia'
const pinia = createPinia()
import zhLocale from 'element-plus/es/locale/lang/zh-cn'
createApp(App).use(router).use(ElementPlus, {locale: zhLocale}).use(pinia).mount('#app')
-
在app.vue中声明router-view标签,展示组件内容。app.vue文件内容如下
<script setup> </script> <template> <router-view></router-view> </template> <style scoped> </style>
2.7 Pinia状态管理库
-
安装
cnpm install pinia
-
安装persist
cnpm install pinia-persistedstate-plugin
-
main.js中使用persist
import { createPersistedState } from 'pinia-persistedstate-plugin' const persist = createPersistedState() pinia.use(persist)
main.js内容整体如下:
import './assets/main.css' import { createApp } from 'vue' import App from './App.vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import router from '@/router' import { createPinia } from 'pinia' const pinia = createPinia() import { createPersistedState } from 'pinia-persistedstate-plugin' const persist = createPersistedState() pinia.use(persist) import zhLocale from 'element-plus/es/locale/lang/zh-cn' createApp(App).use(router).use(ElementPlus, {locale: zhLocale}).use(pinia).mount('#app')
-
src/stores/下定义token.js和userInfo.js来存储token和用户相关信息
token.js
// 定义 store
import { defineStore } from "pinia"
import {ref} from 'vue'
/*
第一个参数:名字,唯一性
第二个参数:函数,函数的内部可以定义状态的所有内容
返回值: 函数
*/
export const useTokenStore = defineStore('token', () => {
// 响应式变量
const token = ref('')
// 修改token值函数
const setToken = (newToken) => {
token.value = newToken
}
// 移除token值函数
const removeToke = () => {
token.value = ''
}
return {
token, setToken, removeToke
}
},
{
persist: true // 持久化存储
}
)
userInfo.js
import { defineStore } from "pinia"
import {ref} from 'vue'
const useUserInfoStore = defineStore('userInfo', () => {
const info = ref({})
const setInfo = (newInfo) => {
info.value = newInfo
}
const removeInfo = () => {
info.value = {}
}
return {info, setInfo, removeInfo}
},
{
persist: true
}
)
export default useUserInfoStore;
2.8 搭建管理页面基础框架
2.8.1 在src/api/下创建user.js,封装请求方法
import request from "@/utils/request.js"
// 登录接口调用函数
export const userLoginService = (loginData) => {
return request.post('/user/login', loginData)
}
// 获取当前登录用户信息
export const currentUserService = () => {
return request.get('/user/currentUser')
}
// 获取所有用户信息
export const allUserService = () => {
return request.get('/user/userList')
}
// 分页查询
export const pageListService = (pageParam) => {
return request.get('/user/pageList', {params: pageParam})
}
// 新增用户
export const addUserService = (addData) => {
return request.post('/user/add', addData)
}
// 根据id获取用户信息
export const getUserById = (id) => {
return request.get('/user/getuserById', {params: id})
}
// 修改用户信息
export const updateUserService = (data) => {
return request.put('/user/update', data)
}
// 删除用户
export const deleteByIdService = (id) => {
console.log("deleteRequestid:", id)
return request.delete('/user/delete/' + id)
}
2.8.2 登陆页面
- 安装
cnpm install sass -D
在src下创建vuew项目,用于存放vue页面组件
Header.vue
<template>
<div class="container">
<!-- div left -->
<div class="left">
<!-- 折叠按钮-->
<div @click="toggleCollapse()">
<el-icon size="24" v-show="!isMenuOpen"><Fold /></el-icon>
<el-icon size="24" v-show="isMenuOpen"><Expand /></el-icon>
</div>
<!-- 面包屑 -->
<div>
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<template v-for="(item, index) in breadList">
<el-breadcrumb-item
v-if="item.name"
:key="index"
:to="item.path"
>{{ item.meta.title }}</el-breadcrumb-item>
</template>
</el-breadcrumb>
</div>
</div>
<!-- div right -->
<div class="right">
<div>
<span >账号:{{userData.loginName}}</span>
</div>
<div>
<el-avatar> {{userData.name}} </el-avatar>
</div>
<div>
<el-dropdown>
<el-icon size="24"><MoreFilled /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-icon><UserFilled /></el-icon>个人信息
</el-dropdown-item>
<el-dropdown-item>
<el-icon><EditPen /></el-icon>修改密码
</el-dropdown-item>
<el-dropdown-item>
<el-icon><ArrowLeft /></el-icon>退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</div>
</template>
<script setup>
import {
Fold,
Expand,
MoreFilled,
EditPen,
ArrowLeft,
UserFilled
} from '@element-plus/icons-vue'
import {ref, defineEmits, watch} from 'vue'
// 面包屑
import { useRouter,useRoute } from 'vue-router'
let router = useRouter()
let route = useRoute()
let breadList = ref()
let getMatched=()=>{
console.log("route.matched:",route.matched);
console
breadList.value = route.matched.filter(item => item.meta && item.meta.title);
}
getMatched()
watch(() => route.path, (newValue, oldValue) => { //监听路由路径是否发生变化,之后更改面包屑
console.log("======")
breadList.value = route.matched.filter(item =>
item.meta && item.meta.title
);
console.log("breadList.value", breadList.value)
})
import useUserInfoStore from '@/stores/userinfo.js'
const userInfoStore = useUserInfoStore();
// 用户数据模型
let userData = ref({
id: '',
name: '',
loginName: ''
})
import {currentUserService} from '@/api/user.js'
// 获取登录用户信息
const getUser = async () => {
let result = await currentUserService()
// console.log(result)
userData.value = result.data;
userInfoStore.setInfo(result.data)
// console.log("userData:",userData)
}
getUser()
// 折叠按钮处理
const emits = defineEmits(["parentClick"])
const isMenuOpen = ref(false)
const toggleCollapse = () => {
isMenuOpen.value = !isMenuOpen.value
console.log(isMenuOpen.value)
emits("parentClick", isMenuOpen.value)
}
</script>
<style lang="scss" scope>
.container {
overflow: auto; /* 清除浮动影响 */
height: 48px;
padding: 10px; /* 内边距 */
border-bottom: 2px solid; /* 设置下边框宽度和样式 */
border-bottom-color: #F5F5F5; /* 设置下边框颜色为红色 */
background-color: #FFFFFF;
}
.left {
height: 48px;
float: left;
display: flex;
align-items: center; /* 垂直居中子项 */
justify-content: center; /* 水平居中子项(如果需要)*/
}
.left > div {
padding-right: 10px; /* 设置直接子元素的 padding */
}
.right {
float: right;
display: flex;
align-items: center; /* 垂直居中子项 */
justify-content: center; /* 水平居中子项(如果需要)*/
}
.right > div {
padding-right: 10px; /* 设置直接子元素的 padding */
}
</style>
Layout.vue
<script setup>
import LeftLayout from './LeftLayout.vue'
import Header from './Header.vue'
import MainView from './MainView.vue'
import {ref} from 'vue'
const isCollapse = ref(false)
const parentClick = (isCollapseValue) => {
isCollapse.value = isCollapseValue;
console.log(isCollapse.value)
}
</script>
<template>
<div class="common-layout">
<el-container>
<LeftLayout :isCollapse='isCollapse' />
<el-container>
<el-header style="padding: 0"><Header @parentClick='parentClick'/></el-header>
<el-main style="padding: 16px 8px 6px 8px"><MainView/></el-main>
<el-footer>后台 ©2024 Created by buzhisuoyun</el-footer>
</el-container>
</el-container>
</div>
</template>
<style scoped>
.el-footer {
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #666;
height: 38px;
padding: 0;
background-color: #FFFFFF;
}
</style>
LeftLayout.vue
<template>
<el-row class="tac">
<el-col >
<el-menu
default-active="2"
class="el-menu-vertical-demo"
:collapse="isCollapse"
:router="true"
>
<!-- 标题 -->
<div class="containerdiv">
<img src="../assets/favicon.ico" alt="Your Image" class="image">
<span class="text">后台管理</span>
</div>
<!-- 菜单 -->
<el-sub-menu index="1">
<template #title>
<el-icon><Share /></el-icon>
<span>API管理</span>
</template>
<el-menu-item index="/api/apilist">API列表</el-menu-item>
<el-menu-item index="1-2">item two</el-menu-item>
<el-menu-item index="1-3">item three</el-menu-item>
<el-sub-menu index="1-4">
<template #title>item four</template>
<el-menu-item index="1-4-1">item one</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="2">
<el-icon><icon-menu /></el-icon>
<span>Navigator Two</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<el-icon><document /></el-icon>
<span>Navigator Three</span>
</el-menu-item>
<el-menu-item index="4">
<el-icon><setting /></el-icon>
<span>Navigator Four</span>
</el-menu-item>
<el-sub-menu index="5">
<template #title>
<el-icon><UserFilled /></el-icon>
<span>用户管理</span>
</template>
<el-menu-item index="/user/userlist">用户列表</el-menu-item>
<el-menu-item index="/user/displayuser">个人信息</el-menu-item>
<el-menu-item index="/user/editpassword">修改密码</el-menu-item>
</el-sub-menu>
</el-menu>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
import {
Document,
Menu as IconMenu,
Location,
Share,
UserFilled,
Setting,
} from '@element-plus/icons-vue'
import {ref, defineProps} from 'vue'
type Props = {
isCollapse: boolean
}
defineProps<Props>()
</script>
<style scoped>
.el-menu-vertical-demo {
height: 100vh;
}
.el-menu-item {
min-width: 0;
}
.containerdiv {
/* 你可以设置容器的样式,例如宽度、高度、背景色等 */
/* width: 300px; /* 示例宽度 */
height: 48px;
padding: 10px; /* 内边距 */
border-bottom: 2px solid; /* 设置下边框宽度和样式 */
border-bottom-color: #F5F5F5; /* 设置下边框颜色为红色 */
}
.image {
display: inline-block;
vertical-align: middle; /* 图片与文字垂直居中对齐 */
margin-right: 6px; /* 图片右边距,可选 */
width: 20px;
}
.text {
display: inline-block;
vertical-align: middle; /* 文字与图片垂直居中对齐 */
font-weight: bold; /* 加粗文字 */
font-size: 14px;
}
</style>
Login.vue
<script setup>
import { User, Lock } from '@element-plus/icons-vue'
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
//定义数据模型
const registerData = ref({
loginName: 'admin',
password:'admin',
rePassword: ''
})
// 定义表单组件的引用
const ruleFormRef = ref(null)
//定义表单校验规则
const rules = ref({
loginName: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 5, max: 16, message: '长度为5~16位非空字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 5, max: 16, 2: '长度为5~16位非空字符', trigger: 'blur' }
]
})
//绑定数据,复用注册表单的数据模型
//表单数据校验
//登录函数
import {userLoginService} from '@/api/user.js'
import {useTokenStore} from '@/stores/token.js'
import {useRouter} from 'vue-router'
const router = useRouter()
const tokenStore = useTokenStore();
const login = async ()=>{
// 校验表单
if (!ruleFormRef.value) return
console.log("校验")
await ruleFormRef.value.validate(async (valid) => {
if (valid) {
console.log("校验成功")
// 调用接口,完成登录
let result = await userLoginService(registerData.value);
/* if(result.code===0){
alert(result.msg? result.msg : '登录成功')
}else{
alert('登录失败')
} */
//alert(result.msg? result.msg : '登录成功')
// ElMessage.success(result.msg ? result.msg : '登录成功')
ElMessage.success(result.msg ? '登录成功': result.msg) //提示信息
//token存储到pinia中
tokenStore.setToken(result.data)
//跳转到首页 路由完成跳转
router.push('/')
} else {
console.log("校验失败")
}
})
}
//定义函数,清空数据模型的数据
const clearRegisterData = ()=>{
registerData.value={
loginName: '',
password:'',
rePassword:''
}
}
</script>
<template>
<el-row class="login-page">
<el-col :span="12" class="bg"></el-col>
<el-col :span="6" :offset="3" class="form">
<!-- 登录表单 -->
<el-form ref="ruleFormRef" :model=registerData size="large" autocomplete="off" :rules="rules">
<el-form-item>
<h1>登录</h1>
</el-form-item>
<el-form-item prop="loginName">
<el-input :prefix-icon="User" placeholder="请输入用户名" v-model="registerData.loginName"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input name="password" :prefix-icon="Lock" type="password" placeholder="请输入密码" v-model="registerData.password"></el-input>
</el-form-item>
<el-form-item class="flex">
<div class="flex">
<el-checkbox>记住我</el-checkbox>
<!-- <el-link type="primary" :underline="false">忘记密码?</el-link> -->
</div>
</el-form-item>
<!-- 登录按钮 -->
<el-form-item>
<el-button class="button" type="primary" auto-insert-space @click="login">登录</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</template>
<style lang="scss" scoped>
/* 样式 */
.login-page {
height: 100vh;
background-color: #fff;
.bg {
background: url('@/assets/login_bg.jpg') no-repeat center / cover;
border-radius: 0 20px 20px 0;
}
.form {
display: flex;
flex-direction: column;
justify-content: center;
user-select: none;
.title {
margin: 0 auto;
}
.button {
width: 100%;
}
.flex {
width: 100%;
display: flex;
justify-content: space-between;
}
}
}
</style>
MainView.vue
<template>
<div class="app-main">
<!-- <transition name="fade-transfrom" mode="out-in">
<router-view></router-view>
</transition> -->
<router-view v-slot="{ Component }">
<transition>
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
<style lang="scss" scope>
.app-main{
width:100%;
height:100%;
background-color: #FFFFFF;
}
</style>
user/DisplayUser.vue
<template>
<div>
<el-form :model="form" >
<el-form-item label="账号" >
<el-input v-model="form.loginName" :disabled="!isAdd"/>
</el-form-item>
<el-form-item label="姓名" >
<el-input v-model="form.name" :disabled="!isAdd"/>
</el-form-item>
<el-form-item label="电话" >
<el-input v-model="form.phone" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.sex" :disabled="!isAdd">
<el-radio value="0" checked>女</el-radio>
<el-radio value="1">男</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div class="dialog-footer">
<el-button @click="onDialogFormCancel">取消</el-button>
<el-button type="primary" @click="onDialogFormConfirm">
确认
</el-button>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue'
const form = ref({
loginName: '',
name: '',
phone: '',
sex: '0'
})
// 重置对话框表单
const restForm = () => {
form.value = {sex: '0'}
title.value = '添加用户'
isAdd.value = true
}
const isAdd = ref(true)
// 提交事件
const onDialogFormConfirm = async () => {
}
// 取消事件
const onDialogFormCancel = () => {
}
</script>
EditPassword.vue
<template>
<div> 修改密码</div>
</template>
UserList.vue
<script setup>
import { Plus } from "@element-plus/icons-vue";
import { ref, reactive } from "vue";
import { allUserService, pageListService, addUserService, getUserById, updateUserService, deleteByIdService } from "@/api/user.js";
import { ElMessage, ElMessageBox } from "element-plus"
// 表单数据
const searchData = ref({
name: "",
});
// 表格数据
const tableData = ref([]);
/** 分页 */
// 分页数据
const pageData = reactive({
currentPage: 1,
pageSize: 10,
total: 20,
})
// 分页插件,每页条数发生改变时
const handleSizeChange = (val) => {
pageData.pageSize = val
getPageList()
}
// 分页插件, 当页码发生改变时
const handleCurrentChange = (val) => {
pageData.currentPage = val
getPageList()
}
// // 查询所有用户
// const getAllUser = async () => {
// const result = await allUserService()
// tableData.value = result.data
// }
// getAllUser()
// 分页查询
const getPageList = async () => {
const params = {
currentPage: pageData.currentPage,
pageSize: pageData.pageSize,
name: searchData.value.name,
}
//console.log("params:", params);
const result = await pageListService(params);
pageData.total = result.data.total;
tableData.value = result.data.items;
//console.log("tableData:", tableData);
}
getPageList()
// 头部表单函数定义
const onSearch = () => {
getPageList()
}
// 重置查询表单
const onRest = () => {
searchData.value = {}
getPageList()
}
/** 添加修改对话框表单 */
const form = ref({
loginName: '',
name: '',
phone: '',
sex: '0'
})
// 重置对话框表单
const restForm = () => {
form.value = {sex: '0'}
title.value = '添加用户'
isAdd.value = true
}
const title = ref('添加用户')
const isAdd = ref(true)
const dialogFormVisible = ref(false)
// 提交对话框表单按钮事件
const onDialogFormConfirm = async () => {
//1.验证表单
if (!ruleFormRef.value) return
//2.提交表单
await ruleFormRef.value.validate((valid) => {
if (valid) { // 校验成功
confirm()
}
})
}
// 取消对话表单框按钮事件
const onDialogFormCancel = () => {
console.log("cancel......")
dialogFormVisible.value = false
restForm()
}
// 添加按钮事件
const onAdd = () => {
// 打开对话框
title.value = '添加用户'
isAdd.value = true
dialogFormVisible.value = true
}
// 修改按钮事件
const handleEdit = async (index, row) => {
title.value = '修改用户'
isAdd.value = false
// 回显数据
console.log("row:", row)
const id = {id: row.id}
let result = await getUserById(id)
form.value = result.data
// 控制只读属性
dialogFormVisible.value = true
}
// 删除按钮事件
const handleDelete = (index, row) => {
ElMessageBox.confirm(
'确认要删除吗?',
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then( async () => {
// 删除
console.log("delete=====")
let result = await deleteByIdService(row.id)
ElMessage.success(result.msg ? result.msg : '删除成功')
getPageList()
})
.catch(() => {
})
}
// 提交表单
const confirm = async () => {
if(isAdd.value) {
// 添加
try { // 添加成功
let result = await addUserService(form.value)
ElMessage.success(result.msg ? result.msg : '添加成功')
// 关闭弹窗,清空表单
dialogFormVisible.value = false
restForm()
getPageList()
} catch (error) {
}
} else {
console.log("update=======")
//修改
try { // 修改成功
let result = await updateUserService(form.value)
ElMessage.success(result.msg ? result.msg : '修改成功')
// 关闭弹窗,清空表单
dialogFormVisible.value = false
restForm()
getPageList()
} catch (error) {
}
}
}
/** 表单校验 */
const ruleFormRef = ref(null) // 定义表单组件的引用
// 定义表单校验规则
const rules = ref({
loginName: [
{ required: true, message: '请输入账号名', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' }
],
phone: [
{ required: true, trigger: 'blur', message: "请输入正确手机号", validator: checkPhone }
]
})
// 手机号自定义校验
var checkPhone = (rule, value, callback) => {
if (!value) {
return callback(new Error('手机号不能为空'))
} else {
const reg = /^1[3|4|5|7|8][0-9]\d{8}$/
console.log(reg.test(value))
if (reg.test(value)) {
callback()
} else {
return callback(new Error('请输入正确的手机号'))
}
}
}
</script>
<template>
<div>
<!-- 工具栏 -->
<div>
<el-row>
<el-col :span="8">
<!-- 操作按钮 -->
<div class="operation-div">
<el-button type="primary" @click="onAdd">添加</el-button>
</div>
</el-col>
<el-col :span="16">
<!-- 条件查询 -->
<div class="search-div">
<el-form :inline="true" :model="searchData" class="demo-form-inline">
<el-form-item label="用户名:">
<el-input
v-model="searchData.name"
placeholder="请输入用户名"
clearable
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSearch">查询</el-button>
<el-button type="primary" @click="onRest">重置</el-button>
</el-form-item>
</el-form>
</div>
</el-col>
</el-row>
</div>
<!-- 表格内容 -->
<div>
<el-table
:data="tableData"
border
stripe
style="width: 100%"
:header-cell-style="{ background: '#ECF5FF' }"
>
<el-table-column type="index" :index="indexMethod" />
<el-table-column prop="loginName" label="账号" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="sex" label="性别">
<template #default="scope">
<el-tag :type="scope.row.sex === '0'? '' : 'success'" disable-transitions>
{{ scope.row.sex === '1' ? "男" : "女" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)"
>编辑</el-button
>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div style="margin-top: 20px">
<el-pagination
v-model:current-page="pageData.currentPage"
v-model:page-size="pageData.pageSize"
:page-sizes="[10, 20, 50, 100]"
background
layout="->, jumper, total, sizes, prev, pager, next"
:total="pageData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<!-- 添加修改对话框表单-->
<el-dialog v-model="dialogFormVisible" :title="title" width="500" draggable overflow @close='onDialogFormCancel'>
<el-form :model="form" ref="ruleFormRef" :rules="rules">
<el-form-item label="账号" prop="loginName">
<el-input v-model="form.loginName" :disabled="!isAdd"/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" :disabled="!isAdd"/>
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input v-model="form.phone" />
</el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.sex" :disabled="!isAdd">
<el-radio value="0" checked>女</el-radio>
<el-radio value="1">男</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="onDialogFormCancel">取消</el-button>
<el-button type="primary" @click="onDialogFormConfirm">
确认
</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>
.operation-div {
width: 100%;
text-align: left;
padding-left: 10px;
padding-top: 10px;
}
.search-div {
width: 100%;
text-align: right;
padding-top: 10px;
}
</style>
2.9 运行展示
2.9.1 启动前端
cnpm run dev
进入http://localhost:5173
2.9.2 启动后端
运行后端项目
2.9.3 测试
登录界面,使用之前swagger测试时添加的用户登录即可。