当前位置: 首页 > news >正文

【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之前端环境搭建

【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之前端环境搭建

  • 2 前端环境搭建
    • 2.1 环境准备
    • 2.2 创建Vue3项目
    • 2.3 项目搭建准备
    • 2.4 安装Element Plus
    • 2.5 安装axios
      • 2.5.1 配置(创建实例,配置请求,响应拦截器)
      • 2.5.2 配置跨域
    • 2.6 Vue Router安装使用
    • 2.7 Pinia状态管理库
    • 2.8 搭建管理页面基础框架
      • 2.8.1 在src/api/下创建user.js,封装请求方法
      • 2.8.2 登陆页面
    • 2.9 运行展示
      • 2.9.1 启动前端
      • 2.9.2 启动后端
      • 2.9.3 测试

主要参考的博客为:

从零搭建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) => {// 请求前回调// 添加tokenconst tokenStore = useTokenStore()// 判断有无tokenif (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-itemv-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);consolebreadList.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.valueconsole.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-menudefault-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) returnconsole.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 = valgetPageList()
}
// 分页插件, 当页码发生改变时
const handleCurrentChange = (val) => {pageData.currentPage = valgetPageList()
}// // 查询所有用户
// 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 = falserestForm()
}// 添加按钮事件
const onAdd = () => {// 打开对话框title.value = '添加用户'isAdd.value = truedialogFormVisible.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 = falserestForm()getPageList()} catch (error) {}} else {console.log("update=======")//修改try {   // 修改成功let result = await updateUserService(form.value)ElMessage.success(result.msg ? result.msg : '修改成功')// 关闭弹窗,清空表单dialogFormVisible.value = falserestForm()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-inputv-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"borderstripestyle="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-buttonsize="small"type="danger"@click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><div style="margin-top: 20px"><el-paginationv-model:current-page="pageData.currentPage"v-model:page-size="pageData.pageSize":page-sizes="[10, 20, 50, 100]"backgroundlayout="->, 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测试时添加的用户登录即可。

在这里插入图片描述

在这里插入图片描述

相关文章:

【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之前端环境搭建

【Springboot3vue3】从零到一搭建Springboot3vue3前后端分离项目之前端环境搭建 2 前端环境搭建2.1 环境准备2.2 创建Vue3项目2.3 项目搭建准备2.4 安装Element Plus2.5 安装axios2.5.1 配置&#xff08;创建实例&#xff0c;配置请求&#xff0c;响应拦截器&#xff09;2.5.2 …...

手写Mybatis框架源码(简写)

pom文件&#xff1a; springboot版本&#xff1a;2.6.5 jdk&#xff1a;8 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&q…...

Flask返回中文Unicode编码(乱码)解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

最大值和最小值的差

最大值和最小值的差 C语言代码C 语言代码Java语言代码Python语言代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输出一个整数序列中最大的数和最小的数的差。 输入 第一行为M&#xff0c;表示整数个数&#xff0c;整数个数不会大于1…...

如何在 IntelliJ IDEA 中为 Spring Boot 应用实现热部署

文章目录 1. 引言2. 准备工作3. 添加必要的依赖4. 配置 IntelliJ IDEA4.1 启用自动编译4.2 开启热部署策略 5. 测试热部署6. 高级技巧7. 注意事项8. 总结 随着现代开发工具的进步&#xff0c;开发者们越来越重视提高生产力的特性。对于 Java 开发者来说&#xff0c;能够在不重启…...

探索 Java 中的 Bug 世界

在 Java 编程的旅程中&#xff0c;我们不可避免地会遇到各种 Bug。这些 Bug 可能会导致程序出现意外的行为、崩溃或者性能问题。了解 Java Bug 的类型、产生原因以及解决方法&#xff0c;对于提高我们的编程技能和开发出稳定可靠的应用程序至关重要。 一、Java Bug 的定义与分类…...

SQL面试题——百度SQL面试题 连续签到领金币

百度SQL面试题 连续签到领金币 今天的这个题目来自百度,而且这个题目很常见,是一个大家日常经常遇到的一个场景,几乎无处不在,就是签到送积分,只不过这里是签到领金币 有用户签到记录表,sign,记录用户当天是否完成签到,请计算出每个用户的每个月获得的金币数量; 签到…...

easyExcel单一下拉框和级联下拉框

文章目录&#xff1a; 单一下拉框级联下拉框 具体实现&#xff1a; 单一下拉框 public class BoolWriteHandler implements SheetWriteHandler {private List<String> dropDown;private List<Integer> indexList;public BoolWriteHandler(List<Integer> i…...

linux-安全-iptables防火墙基础笔记

目录 一、 iptables链结构 五链 二、 iptables表结构 四表 三、 匹配流程 四、 语法 五、 匹配 1. 通用匹配 2. 隐含匹配 3. 显示匹配 六、 SNAT 七、 DNAT 八、 规则备份及还原 1. 备份 2. 还原 这篇将讲解iptables防火墙的基础知识 一、 iptables链结构 规则…...

力扣刷题TOP101: 25.BM32合并二叉树

目录&#xff1a; 目的 思路 复杂度 记忆秘诀 python代码 目的&#xff1a; 已知两颗二叉树&#xff0c;将它们合并成一颗二叉树。合并规则是&#xff1a;都存在的结点&#xff0c;就将结点值加起来&#xff0c;否则空的位置就由另一个树的结点来代替。 思路 我们有两棵二…...

R的中文文本处理包--tmcn

文章目录 介绍tmcn 和 jieba 的关系函数&#xff1a;catUTF8toUTF8实例 介绍 tmcn 包是 R 语言中的一个用于处理和分析中文文本的包&#xff0c;特别适用于中文文本的分词、词频统计和文本挖掘等任务。以下是 tmcn 包的基本用法&#xff0c;包括安装、常用函数和示例。 一个用…...

差异基因富集分析(R语言——GOKEGGGSEA)

接着上次的内容&#xff0c;上篇内容给大家分享了基因表达量怎么做分组差异分析&#xff0c;从而获得差异基因集&#xff0c;想了解的可以去看一下&#xff0c;这篇主要给大家分享一下得到显著差异基因集后怎么做一下通路富集。 1.准备差异基因集 我就直接把上次分享的拿到这…...

scrapy对接rabbitmq的时候使用post请求

之前做分布式爬虫的时候,都是从push url来拿到爬虫消费的链接,这里提出一个问题,假如这个请求是post请求的呢,我观察了scrapy-redis的源码,其中spider.py的代码是这样写的 1.scrapy-redis源码分析 def make_request_from_data(self, data):"""Returns a Reques…...

vue+elementUI+transition实现鼠标滑过div展开内容,鼠标划出收起内容,加防抖功能

文章目录 一、场景二、实现代码1.子组件代码结构2.父组件 一、场景 这两天做项目&#xff0c;此产品提出需求 要求详情页的顶部区域要在鼠标划入后展开里面的内容&#xff0c;鼠标划出要收起部分内容&#xff0c;详情底部的内容高度要自适应&#xff0c;我这里运用了鼠标事件t…...

大模型语料库的构建过程 包括知识图谱构建 垂直知识图谱构建 输入到sql构建 输入到cypher构建 通过智能体管理数据生产组件

以下是大模型语料库的构建过程&#xff1a; 一、文档切分语料库构建 数据来源确定&#xff1a; 首先&#xff0c;需要确定语料库的数据来源。这些来源可以是多种多样的&#xff0c;包括但不限于&#xff1a; 网络资源&#xff1a;利用网络爬虫技术从各种网站&#xff08;如新闻…...

阿里云ECS服务器域名解析

阿里云ECS服务器域名解析&#xff0c;以前添加两条A记录类型&#xff0c;主机记录分别为www和&#xff0c;这2条记录都解析到服务器IP地址。 1.进入阿里云域名控制台&#xff0c;找到域名 ->“解析设置”->“添加记录” 2.添加一条记录类型为A,主机记录为www&#xff0c…...

牛客周赛71:A:JAVA

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 \hspace{15pt}对于给定的两个正整数 nnn 和 kkk &#xff0c;是否能构造出 kkk 对不同的正整数 (x,y)(x,y)(x,y) &#xff0c;使得 xynxynxyn 。 \hspace{15pt}我们认为两对正整数 (…...

查询产品所涉及的表有(product、product_admin_mapping)

文章目录 1、ProductController2、AdminCommonService3、ProductApiService4、ProductCommonService5、ProductSqlService1. 完整SQL分析可选部分&#xff08;条件筛选&#xff09;&#xff1a; 2. 涉及的表3. 总结4. 功能概述 查询指定管理员下所有产品所涉及的表&#xff1f;…...

算法基础学习Day5(双指针、动态窗口)

文章目录 1.题目2.题目解答1.四数之和题目及题目解析算法学习代码提交 2.长度最小的子数组题目及题目解析滑动窗口的算法学习方法一&#xff1a;单向双指针(暴力解法)方法二&#xff1a;同向双指针(滑动窗口) 代码提交 1.题目 18. 四数之和 - 力扣&#xff08;LeetCode&#x…...

docker 部署 mysql 9.0.1

docker 如何部署 mysql 9 &#xff0c;请看下面步骤&#xff1a; 1. 先看 mysql 官网 先点进去 8 版本的 Reference Manual 。 选择 9.0 版本的。 点到这里来看&#xff0c; 这里有一些基础的安装步骤&#xff0c;可以看一下。 - Basic Steps for MySQL Server Deployment wit…...

关于小标join大表,操作不当会导致笛卡尔积,数据倾斜

以前总是说笛卡尔积&#xff0c;笛卡尔积&#xff0c;没碰到过&#xff0c;今天在跑流程调度时&#xff0c;就碰到笛卡尔积了&#xff0c;本来&#xff0c;就是查询几个编码的信息&#xff0c;然后由于使用的是with tmp as&#xff0c;没使用where in ,所以跑的很慢 现象&#…...

SpringMVC全局异常处理

一、Java中的异常 定义&#xff1a;异常是程序在运行过程中出现的一些错误&#xff0c;使用面向对象思想把这些错误用类来描述&#xff0c;那么一旦产生一个错误&#xff0c;即创建某一个错误的对象&#xff0c;这个对象就是异常对象。 类型&#xff1a; 声明异常&#xff1…...

出海服务器可以用国内云防护吗

随着企业国际化进程的加速&#xff0c;越来越多的企业选择将业务部署到海外服务器上&#xff0c;以便更贴近国际市场。然而&#xff0c;海外服务器也面临着来自全球各地的安全威胁和网络攻击。当出海服务器遭受攻击时&#xff0c;是否可以借助国内的云服务器来进行有效的防护呢…...

从零开始的使用SpringBoot和WebSocket打造实时共享文档应用

在现代应用中&#xff0c;实时协作已经成为了非常重要的功能&#xff0c;尤其是在文档编辑、聊天系统和在线编程等场景中。通过实时共享文档&#xff0c;多个用户可以同时对同一份文档进行编辑&#xff0c;并能看到其他人的编辑内容。这种功能广泛应用于 Google Docs、Notion 等…...

Ant Design Pro实战--day01

下载nvm https://nvm.uihtm.com/nvm-1.1.12-setup.zip 下载node.js 16.16.0 //非此版本会报错 nvm install 16.16.0 安装Ant Design pro //安装脚手架 npm i ant-design/pro-cli -g //下载项目 pro create myapp //选择版本 simple 安装依赖 npm install 启动umi yarn add u…...

pcl点云库离线版本构建

某天在摸鱼的小邓接到任务需要进行点云数据的去噪&#xff0c;在万能的github中发现如下pcl库非常好使&#xff0c;so有了此&#xff0c; 1.下载vs2017连接如下&#xff1a; ed2k://|file|mu_visual_studio_community_2017_version_15.1_x86_x64_10254689.exe|1037144|12F5C1…...

字节高频算法面试题:小于 n 的最大数

问题描述&#xff08;感觉n的位数需要大于等于2&#xff0c;因为n的位数1的话会有点问题&#xff0c;“且无重复”是指nums中存在重复&#xff0c;但是最后返回的小于n最大数是可以重复使用nums中的元素的&#xff09;&#xff1a; 思路&#xff1a; 先对nums倒序排序 暴力回…...

ElasticSearch常见面试题汇总

一、ElasticSearch基础&#xff1a; 1、什么是Elasticsearch&#xff1a; Elasticsearch 是基于 Lucene 的 Restful 的分布式实时全文搜索引擎&#xff0c;每个字段都被索引并可被搜索&#xff0c;可以快速存储、搜索、分析海量的数据。 全文检索是指对每一个词建立一个索引…...

Spring Boot如何实现防盗链

一、什么是盗链 盗链是个什么操作&#xff0c;看一下百度给出的解释&#xff1a;盗链是指服务提供商自己不提供服务的内容&#xff0c;通过技术手段绕过其它有利益的最终用户界面&#xff08;如广告&#xff09;&#xff0c;直接在自己的网站上向最终用户提供其它服务提供商的…...

工作中常用springboot启动后执行的方法

前言&#xff1a; 工作中难免会遇到一些&#xff0c;程序启动之后需要提前执行的需求。 例如&#xff1a; 初始化缓存&#xff1a;在启动时加载必要的缓存数据。定时任务创建或启动&#xff1a;程序启动后创建或启动定时任务。程序启动完成通知&#xff1a;程序启动完成后通…...

有哪些做家教网站/聊城seo优化

问题1&#xff1a;在服务器A上有自己需要的代码&#xff0c;但是自己的服务器B上没有代码&#xff0c;因此希望从A服务器上拷贝代码到服务器B上&#xff08;使用的是LINUX操作系统&#xff09;解决办法&#xff1a;使用 scp 命令.e5e587:~/e5/private/huangbiao/E587_11.203.22…...

做手机网站用什么/seo推广培训

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 linux服务器网络编程Socket长连接前言一、pandas是什么&#xff1f;二、使用步骤1.引入库2.读入数据总结Socket长连接 所谓长连接&#xff0c;指在一个TCP连接上可以连续发送…...

wordpress 修改ssl/百度app推广方法

复写onBackPressed方法&#xff1a;Overridepublic void onBackPressed() {// TODO Auto-generated method stubreturn;//super.onBackPressed();}就可以了&#xff0c;死按回退键都不会有半点反应的。...

网站做著作权/网时代教育培训机构怎么样

创建节点、关系 创建节点&#xff08;小明&#xff09;&#xff1a;create (n:people{name:’小明’,age:’18’,sex:’男’}) return n; 创建节点&#xff08;小红&#xff09;: create (n:people{name:’小红’,age:’18’,sex:’女’}) return n; 创建关系&#xff08;小明送…...

网站只显示一个网址/提高基层治理效能

转载请注明出处&#xff1a;http://blog.csdn.net/u010019717更全的内容请看我的游戏蛮牛地址&#xff1a;http://www.unitymanual.com/space-uid-18602.html &#xfeff;&#xfeff;属性 &#xff08;Attribute&#xff09;使用 Unity 的C#语言 &#xff0c;利用属性&#…...

网站开发后端工资多少/百度网页制作

强化顶层设计&#xff0c;集中力量做大做强新一代信息技术产业&#xff0c;加快推进制造业等实体经济数字化转型&#xff0c;全面构筑工业数字经济新业态&#xff0c;拓展经济发展新空间 随着新一代信息技术与实体经济的深度融合&#xff0c;我国传统产业利用数字技术的广度深…...