Springboot+vue实现基本增查demo,前端小白初学vue
前言
由于本人之前是做后端的,因此本篇后端说明可能较少;主要还是记录下出次学习vue的心得。后端实现springboot+mybatis+mysql;前端vue.
功能介绍:1.报表数据的新增、查询
2.省市区联动
后台搭建
项目结构:
- pom文件引入依赖
<?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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<!--<properties>-->
<!--<java.version>11</java.version>-->
<!--</properties>-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- logback -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置文件
server:
port: 8080
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/myvue?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
#mybatis配置
mybatis:
mapper-locations: classpath:mapping/*.xml
type-aliases-package: com.example.test.domain
#日志配置
logging:
config: classpath:logback.xml
level:
com.example.test: debug
- 其他demo不在贴代码,文章篇后会附上github地址
。。。。。。.
前端搭建
1.首先创建一个空文件夹myvue(名字可随意)
2.win+R->cmd->进入项目目录依次执行命令:vue init webpack projectName、cd projectName、npm install、npm run dev如图所示,前提你已经安装node.js,没有就百度
3.打开浏览器输入:http://localhost:8080
4.IDE打开创建的项目–>myvue,我用的是HBulderX
前端开发记录
1.在项目根目录安装axios执行命令npm install axios,安装element ui 执行命令npm i element-ui -S
axios:解决跨域问题;
element ui:ui组件,地址:https://element.eleme.cn/#/zh-CN/
2.修改App.vue
<template>
<div id="app">
<el-row type="flex" justify="center">
<el-col :xs="24" :sm="22" :md="20" :lg="20" :xl="18">
<router-view/>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
}
},
methods: {
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0px;
padding: 0px;
}
</style>
3.修改main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import elementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'element-ui/lib/theme-chalk/display.css'
Vue.use(elementUI)
import axios from 'axios'
Vue.prototype.axios = axios
import qs from 'qs';
Vue.prototype.qs = qs;
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
4.修改HelloWorld.vue
<template>
<div>
<!-- 查询form -->
<el-form :model="searchForm" ref="searchForm" class="demo-form-inline" label-width="100px" size="medium" >
<el-row>
<el-col :span="8">
<el-form-item label="姓名" prop="personName">
<el-input
v-model="searchForm.personName"
class="search_name"
placeholder="输入姓名查询" style="width: 70%;" >
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="个人定位" prop="personAttr">
<el-select v-model="searchForm.personAttr">
<el-option label="技术型" value="技术型"></el-option>
<el-option label="业务型" value="业务型"></el-option>
<el-option label="营销型" value="营销型"></el-option>
<el-option label="管理型" value="管理型"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入职日期">
<el-date-picker
v-model="searchForm.personDate"
align="right"
type="date"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期"
:picker-options="pickerOptions1">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="省" prop="province">
<el-select @change="selectChanged" v-model="searchForm.province" placeholder="请选择">
<el-option
v-for="item in provinces"
:key="item.cityNum"
:label="item.cityName"
:value="item.cityNum">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="市" prop="city">
<el-select @change="selectChanged" v-model="searchForm.city" placeholder="请选择">
<el-option
v-for="item in citys"
:key="item.cityNum"
:label="item.cityName"
:value="item.cityNum">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="区" prop="regional">
<el-select v-model="searchForm.regional" placeholder="请选择">
<el-option
v-for="item in regionals"
:key="item.cityNum"
:label="item.cityName"
:value="item.cityNum">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :push="16">
<el-button
type="info"
@click="onSearch()"
class="el-icon-search">查询
</el-button>
<el-button
class="el-icon-refresh"
type="info"
@click="reset('searchForm')">重置
</el-button>
<el-button
class="el-icon-circle-plus-outline"
type="info"
@click="dialogVisible = true">添加
</el-button>
</el-col>
</el-row>
</el-form>
<!-- 数据table -->
<el-table
:data="tableData"
highlight-current-row
border
style="width: 100%">
<el-table-column
label="姓名">
<template slot-scope="scope">
<span>{{ scope.row.personName }}</span>
</template>
</el-table-column>
<el-table-column
label="性别">
<template slot-scope="scope">
<span>{{ scope.row.personSex }}</span>
</template>
</el-table-column>
<el-table-column
label="年龄">
<template slot-scope="scope">
<span>{{ scope.row.personAge }}</span>
</template>
</el-table-column>
<el-table-column
label="入职日期">
<template slot-scope="scope">
<i class="el-icon-time hidden-sm-and-down"></i>
<span>{{ scope.row.personDate }}</span>
</template>
</el-table-column>
<el-table-column
label="个人规划">
<template slot-scope="scope">
<span>{{ scope.row.personPlan }}</span>
</template>
</el-table-column>
<el-table-column
label="个人定位">
<template slot-scope="scope">
<span>{{ scope.row.personAttr }}</span>
</template>
</el-table-column>
</el-table>
<!-- 增加 -->
<el-form :model="addForm" :rules="rules" ref="addForm" label-position="right" label-width="100px" class="demo-ruleForm" size="medium">
<el-dialog
title="基础信息"
:append-to-body='true'
:visible.sync="dialogVisible"
width="70%"
:before-close="handleClose">
<el-input type="hidden" v-model="addForm.personName"/>
<el-row>
<el-col :span="8">
<el-form-item label="姓名" prop="personName">
<el-input v-model="addForm.personName"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="性别" prop="personSex">
<el-select v-model="addForm.personSex" placeholder="请选择性别" style="width: 100%;">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="年龄" prop="personAge">
<el-input v-model="addForm.personAge"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="入职日期" prop="personDate">
<el-date-picker type="datetime" placeholder="选择日期" v-model="addForm.personDate" value-format="yyyy-MM-dd HH:mm:ss" :picker-options="pickerOptions1" style="width: 100%;"></el-date-picker>
</el-form-item>
</el-col>
<el-col span="8">
<el-form-item label="邮箱" prop="personEmail">
<el-input v-model="addForm.personEmail"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="personPhone">
<el-input v-model="addForm.personPhone"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="个人规划" prop="personPlan">
<el-input v-model="addForm.personPlan"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="自我定位" prop="personAttr">
<el-checkbox-group v-model="addForm.personAttr">
<el-checkbox label="技术型" ></el-checkbox>
<el-checkbox label="业务型" ></el-checkbox>
<el-checkbox label="营销型" ></el-checkbox>
<el-checkbox label="管理型" ></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="公司所在地址" >
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="省">
<el-select @change="selectChanged" v-model="addForm.province" placeholder="请选择">
<el-option
v-for="item in provinces"
:key="item.cityNum"
:label="item.cityName"
:value="item.cityNum">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="市">
<el-select @change="selectChanged" v-model="addForm.city" placeholder="请选择">
<el-option
v-for="item in citys"
:key="item.cityNum"
:label="item.cityName"
:value="item.cityNum">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="区">
<el-select v-model="addForm.regional" placeholder="请选择">
<el-option
v-for="item in regionals"
:key="item.cityNum"
:label="item.cityName"
:value="item.cityNum">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<center>
<el-button @click="cancel()" size="medium">关 闭</el-button>
<el-button @click="addPerson('addForm')" type="primary" size="medium">保 存</el-button>
</center>
</el-form-item>
</el-dialog>
</el-form>
<br>
<!-- 翻页组件 -->
<div class="pages">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10, 15, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//添加form
addForm: {
personName: '',//姓名
personSex: '',//性别
personAge: '',//年龄
personDate: '',//入职日期
personEmail: '',//邮箱
personPhone: '',//联系方式
personPlan: '',//个人规划
personAttr: [],//个人定位
province: '',//省
city: '',//市
regional: ''//区
},
rules: {
personName: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 7, message: '长度在 2 到 7 个字符', trigger: 'blur' }
],
personAge: [
{ required: true, message: '请输入年龄', trigger: 'blur' },
{ min: 1,max: 2, message: '长度大于 5 个字符', trigger: 'blur' }
],
},
//时间选择
pickerOptions1: {
disabledDate(time) {
return time.getTime() > Date.now();
}
},
//报表数据集合
tableData: [],
search: '',
dialogVisible: false,
pageSize: 5,
currentPage: 1,
total: 0,
//查询form
searchForm: {
personName: '',//用户姓名
personAttr: '',//个人定位
personDate: '',//入职时间
province: '',//省
city: '',//市
regional: ''//区
},
disablePage: false,
provinces: [],//省
citys: [],//市
regionals: []//区
}
},
methods: {
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.pageSize=val;
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.currentPage=val;
this.onSearch();
},
//增加页面关闭事件
handleClose(done) {
this.$confirm('确认关闭?')
.then(_ => {
done();
})
.catch(_ => {});
},
//增加页面取消事件
cancel() {
this.dialogVisible = false;
this.emptyUserData();
},
emptyUserData(){
this.addForm = {
personName: '',//姓名
personSex: '',//性别
personAge: '',//年龄
personDate: '',//入职日期
personEmail: '',//邮箱
personPhone: '',//联系方式
personPlan: '',//个人规划
personAttr: '',//个人定位
province: '',//省
city: '',//市
regional: ''//区
}
},
//增加用户与服务器交互
addPerson(addForm) {
this.$refs[addForm].validate((valid) => {
if (valid) {
this.addPersonSubmit();
this.dialogVisible = false;
} else {
console.log('error submit!!');
return false;
}
});
},
//form提交事件
addPersonSubmit() {
let postData = this.qs.stringify({
personName: this.addForm.personName,//姓名
personSex: this.addForm.personSex,//性别
personAge: this.addForm.personAge,//年龄
personDate: this.addForm.personDate,//入职日期
personEmail: this.addForm.personEmail,//邮箱
personPhone: this.addForm.personPhone,//联系方式
personPlan: this.addForm.personPlan,//个人规划
personAttr: this.addForm.personAttr.join(","),//个人定位
province: this.addForm.province,//省
city: this.addForm.city,//市
regional: this.addForm.regional
});
this.axios({
// headers: {
// 'Content-Type': 'application/json;charset=utf-8'
// },
method: 'post',
//http://127.0.0.1:8080
url:'/api/person/save',
data:postData
}).then(response =>
{
var code = response.data.code;
if(code == 200){
this.currentPage = 1;
this.$message({
type: 'success',
message: '已添加!'
});
this.onSearch();
}else{
this.$message({
type: 'error',
message: response.data.msg
});
}
}).catch(error =>
{
console.log(error);
});
},
//获取所属城市
selectCitys(cityParentNum,cityLeave) {
//http://127.0.0.1:8080
this.axios.get('/api/city/findList',{
params: {
cityParentNum: cityParentNum,
cityLeave: cityLeave,
}
}).then(response =>
{
console.log(response.data);
let data = response.data.data;
//判断城市等级,将对应的数据放到对应的集合,同时初始化子城市
if(cityLeave == '1'){
this.provinces = data;
}else if(cityLeave == '2'){
this.searchForm.city = '';
this.citys = data;
this.searchForm.regional = '';
}else{
this.searchForm.regional = '';
this.regionals = data;
}
}).catch(error =>
{
console.log(error);
});
},
//省市区级联选中事件
selectChanged(cityNum) {
console.log(cityNum);
var cityLeave = '2';
console.log('error'+cityNum.length);
if(cityNum.length > 4){
cityLeave = '3';
}
this.selectCitys(cityNum,cityLeave);
},
//查询按钮
onSearch() {
let postData = this.qs.stringify({
personName: this.searchForm.personName,
personAttr: this.searchForm.personAttr,
personDate: this.searchForm.personDate,
province: this.searchForm.province,
city: this.searchForm.city,
regional: this.searchForm.regional,
pageNum: this.currentPage,
pageSize: this.pageSize,
});
console.log("postData:"+postData);
this.axios({
method: 'post',
//http://127.0.0.1:8080
url: '/api/person/findList',
data: postData
}).then(response =>
{
console.log("person/findList:"+response);
this.tableData = response.data.rows;
this.disablePage = true;
this.total = response.data.total;
}).catch(error =>
{
console.log(error);
});
},
//重置
reset(searchForm) {
this.$refs[searchForm].resetFields()
}
},
created() {
this.onSearch();
this.selectCitys('0','1');
},
}
</script>
<style scoped>
.search_name{
width: 200px;
}
.pages{
margin: 0px;
padding: 0px;
text-align: right;
}
</style>
5.标签记录
el-row:element具有和bootstrap类似的栅格布局,分为24份,:span 进行划分。
<el-row>
<el-col :span="8">
<el-form-item label="姓名" prop="personName">
<el-input v-model="addForm.personName"></el-input>
</el-form-item>
</el-col>
</el-row>
前后端交互
1.配置请求代理
修改proxyTable:
proxyTable: {
'/api/*': {
target:"http://localhost:8080/",
chunkOrigins: true,// 允许跨域
pathRewrite:{
'^/api': '/api' // 路径重写,使用"/api"代替target.
}
}
},
踩坑说明:
网上查了很多,大多是以下写法:
proxyTable: {
'/api':{
target:'http://xxxx.com',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
}
}
但我的跨域代理请求并没有生效,改为上面那种写法就可以了。
2.发送get请求
get请求,后端接口不能使用@RequestBody标注参数。
selectCitys(cityParentNum,cityLeave) {
//http://127.0.0.1:8080
this.axios.get('/api/city/findList',{
params: {
cityParentNum: cityParentNum,
cityLeave: cityLeave,
}
}).then(response =>
{
console.log(response.data);
}).catch(error =>
{
console.log(error);
});
},
3.发送post请求
addPersonSubmit() {
let postData = this.qs.stringify({
personName: this.addForm.personName,//姓名
...
});
this.axios({
// headers: {
// 'Content-Type': 'application/json;charset=utf-8'
// },
method: 'post',
//http://127.0.0.1:8080
url:'/api/person/save',
data:postData
}).then(response =>
{
var code = response.data.code;
if(code == 200){
this.currentPage = 1;
this.$message({
type: 'success',
message: '已添加!'
});
}else{
this.$message({
type: 'error',
message: response.data.msg
});
}
}).catch(error =>
{
console.log(error);
});
},
在用post请求遇到点问题,无法传递json参数,后台用@RequestBody标准也不行。难…难…难…
项目地址
后端:https://github.com/wenbingege/test.git
前台:https://github.com/wenbingege/myvue.git
前台代码下载后可执行以下命令运行:npm install、npm run dev