前段代码大都来自这个大佬,写的都很详细,建议看他的NEIL_XU_-CSDN博客
后续再来改好点
5.1-5.3
准备环境vue cli安装(前段搭建脚手架)
需要安装node.js
安装node.js----修改环境变量----配置镜像
安装vue cli
执行命令(不知道为什么5.0.8安装失败)
npm install -g @vue/cli
创建web项目
vue create web
选择第三个(手动配置)
选择router,vuex(按空格选择)
选择3.x
是否保存(yes)
保存到哪(package.json)
是否保存模板(y)
保存名字为(train)
进入前段文件
cd web
启动前端
npm run serve
其中package.json相当于maven的pom文件
node module相当于jar包
修改端口号
进入package.json
scripts中的serve后增加--port 9000
快速启动前端工程
右键package.json,选择show npm scripts
集成ant-design vue
安装ant-design vue依赖
npm i ant-design-vue@3.2.15
全局引入依赖 main.js中,新版css文件好像改名了
import Antd from 'ant-design-vue'
createApp(App).use(Antd).use(store).use(router).mount('#app')
import 'ant-design-vue/dist/antd.css'
安装图标
npm install --save@ant-design/icons-vue
全局引入图标依赖,修改app,完整代码如下
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'ant-design-vue/dist/reset.css'
import * as Icons from '@ant-design/icons-vue'
const app=createApp(App);
app.use(Antd).use(store).use(router).mount('#app');
const icons=Icons;
for (const i in icons){
app.component(i,icons[i]);
}
增加页面,router下增加
{
path: '/login',
component: () => import( '../views/login.vue')
}
新增login.vue文件
<template>
<a-row class="login">
<a-col :span="8" :offset="8" class="login-main">
<h1 style="text-align: center"><rocket-two-tone /> 12306售票系统</h1>
<a-form
:model="loginForm"
name="basic"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label=""
name="mobile"
:rules="[{ required: true, message: '请输入手机号!' }]"
>
<a-input v-model:value="loginForm.mobile" placeholder="手机号"/>
</a-form-item>
<a-form-item
label=""
name="code"
:rules="[{ required: true, message: '请输入验证码!' }]"
>
<a-input v-model:value="loginForm.code">
<template #addonAfter>
<a @click="sendCode">获取验证码</a>
</template>
</a-input>
<!--<a-input v-model:value="loginForm.code" placeholder="验证码"/>-->
</a-form-item>
<a-form-item>
<a-button type="primary" block html-type="submit">登录</a-button>
</a-form-item>
</a-form>
</a-col>
</a-row>
</template>
<script>
import { defineComponent, reactive } from 'vue';
export default defineComponent({
name: "login-view",
setup() {
const loginForm = reactive({
mobile: '13000000000',
code: '',
});
const onFinish = values => {
console.log('Success:', values);
};
const onFinishFailed = errorInfo => {
console.log('Failed:', errorInfo);
};
return {
loginForm,
onFinish,
onFinishFailed,
};
},
});
</script>
<style>
.login-main h1 {
font-size: 25px;
font-weight: bold;
}
.login-main {
margin-top: 100px;
padding: 30px 30px 20px;
border: 2px solid grey;
border-radius: 10px;
background-color: #fcfcfc;
}
</style>
新增发送验证码接受类,参数大体和register一致,增加了正则表达式
public class MemberSendCodeReq {
@NotBlank(message = "【手机号】不能为空")
@Pattern(regexp = "^1\\d{10}$",message = "手机号格式错误")
private String mobile;
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
@Override
public String toString() {
return "MemberRegisterReq{" +
"mobile='" + mobile + '\'' +
'}';
}
}
封装获得手机号用户代码
private Member selectByMobile(String mobile) {
MemberExample memberExample=new MemberExample();
memberExample.createCriteria().andMobileEqualTo(mobile);
List<Member> list=memberMapper.selectByExample(memberExample);
return list.get(0);
}
新增sendCode接口和实现
如果存在,则插入,并打印日志
生成验证码
@PostMapping("/sendCode")
public CommonResp sendCode(@Valid@RequestBody MemberSendCodeReq req){
memberService.sendCode(req);
return new CommonResp();
}
public void sendCode(MemberSendCodeReq req) {
Member member=selectByMobile(req.getMobile());
if(ObjectUtil.isNull(member)){
member.setMobile(req.getMobile());
member.setId(SnowUtil.getSnowflaskNextId());
memberMapper.insert(member);
LOG.info("手机号{}不存在,执行注册",req.getMobile());
}
else{
LOG.info("手机号{}不存在,执行登录");
}
String code= RandomUtil.randomString(4);
}
新增登录接口
@PostMapping("/login")
public CommonResp<MemberLoginResp> login(@Valid@RequestBody MemberLoginReq req){
return memberService.login(req);
}
业务层
public CommonResp<MemberLoginResp> login(MemberLoginReq req) {
Member member=selectByMobile(req.getMobile());
if(ObjectUtil.isNull(member)){
throw new BusinessException(BusinessExceptionEnum.MEMBER_MOBILE_EXIST);
}
if(!req.getCode().equals("8888")){
throw new BusinessException(BusinessExceptionEnum.CODE_ERROR);
}
return new CommonResp<>(BeanUtil.copyProperties(member,MemberLoginResp.class));
}
添加异常类
MEMBER_MOBILE_EXIST("手机号已注册"),
MEMBER_MOBILE_NOT_EXIST("请先获取验证码"),
CODE_ERROR("短信验证码错误");
添加注册接口返回类
public class MemberLoginResp {
private Long id;
private String mobile;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@NotBlank(message = "【短信验证码】不能为空")
private String code;
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
@Override
public String toString() {
return "MemberRegisterReq{" +
"mobile='" + mobile + '\'' +
'}';
}
}
完善前端登录功能
安装axios
npm install axios
导入依赖
login.vue script中增加
import axios from "axios";
const sendCode = () => {
axios.post("http://localhost:8000/member/member/sendCode", {
mobile: loginForm.mobile
}).then(response => {
console.log(response);
});
};
return {
loginForm,
onFinish,
onFinishFailed,
sendCode
};
解决跨域问题
gateway中配置文件中增加
# 允许请求来源(老版本叫allowedOrigin)
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedOriginPatterns=*
# 允许携带的头信息
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedHeaders=*
# 允许的请求方式
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedMethods=*
# 是否允许携带cookie
spring.cloud.gateway.globalcors.cors-configurations.[/**].allowCredentials=true
# 跨域检测的有效期,会发起一个OPTION请求
spring.cloud.gateway.globalcors.cors-configurations.[/**].maxAge=3600
完善前端工程
<template>
<a-row class="login">
<a-col :span="8" :offset="8" class="login-main">
<h1 style="text-align: center"><rocket-two-tone /> 12306售票系统</h1>
<a-form
:model="loginForm"
name="basic"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label=""
name="mobile"
:rules="[{ required: true, message: '请输入手机号!' }]"
>
<a-input v-model:value="loginForm.mobile" placeholder="手机号"/>
</a-form-item>
<a-form-item
label=""
name="code"
:rules="[{ required: true, message: '请输入验证码!' }]"
>
<a-input v-model:value="loginForm.code">
<template #addonAfter>
<a @click="sendCode">获取验证码</a>
</template>
</a-input>
<!--<a-input v-model:value="loginForm.code" placeholder="验证码"/>-->
</a-form-item>
<a-form-item>
<a-button type="primary" block @click="login">登录</a-button>
</a-form-item>
</a-form>
</a-col>
</a-row>
</template>
<script>
import { defineComponent, reactive } from 'vue';
import axios from "axios";
import { notification } from 'ant-design-vue';
export default defineComponent({
name: "login-view",
setup() {
const loginForm = reactive({
mobile: '13000000000',
code: '',
});
const onFinish = values => {
console.log('Success:', values);
};
const onFinishFailed = errorInfo => {
console.log('Failed:', errorInfo);
};
const sendCode = () => {
axios.post("http://localhost:8000/member/member/sendCode", {
mobile: loginForm.mobile
}).then(response => {
console.log(response);
let data = response.data;
if (data.success) {
notification.success({ description: '发送验证码成功!' });
loginForm.code = "8888";
} else {
notification.error({ description: data.message });
}
});
};
const login = () => {
axios.post("http://localhost:8000/member/member/login", loginForm).then(response => {
let data = response.data;
if (data.success) {
notification.success({ description: '登录成功!' });
console.log("登录成功:", data.content);
} else {
notification.error({ description: data.message });
}
})
};
return {
loginForm,
sendCode,
login
};
},
});
</script>
<style>
.login-main h1 {
font-size: 25px;
font-weight: bold;
}
.login-main {
margin-top: 100px;
padding: 30px 30px 20px;
border: 2px solid grey;
border-radius: 10px;
background-color: #fcfcfc;
}
</style>
为前端增加拦截器,打印前后端交互信息
main.js下新增
import axios from 'axios';
axios.interceptors.request.use(function (config) {
console.log('请求参数:', config);
return config;
}, error => {
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('返回结果:', response);
return response;
}, error => {
console.log('返回错误:', error);
return Promise.reject(error);
});
login.vue删除console.log相关日志
main.js下新增
axios.defaults.baseURL = process.env.VUE_APP_SERVER;
console.log('环境:', process.env.NODE_ENV);
console.log('服务端:', process.env.VUE_APP_SERVER);
新增文件.env.dev,配置文件需要以VUE_APP开头
NODE_ENV=development
VUE_APP_SERVER=http://localhost:8000
npm控制台中选择dev启动
新增及完善主页面
main.vue
<template>
<a-row class="login">
<a-col :span="8" :offset="8" class="login-main">
<h1 style="text-align: center"><rocket-two-tone /> 12306售票系统</h1>
<a-form
:model="loginForm"
name="basic"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label=""
name="mobile"
:rules="[{ required: true, message: '请输入手机号!' }]"
>
<a-input v-model:value="loginForm.mobile" placeholder="手机号"/>
</a-form-item>
<a-form-item
label=""
name="code"
:rules="[{ required: true, message: '请输入验证码!' }]"
>
<a-input v-model:value="loginForm.code">
<template #addonAfter>
<a @click="sendCode">获取验证码</a>
</template>
</a-input>
<!--<a-input v-model:value="loginForm.code" placeholder="验证码"/>-->
</a-form-item>
<a-form-item>
<a-button type="primary" block @click="login">登录</a-button>
</a-form-item>
</a-form>
</a-col>
</a-row>
</template>
<script>
import { defineComponent, reactive } from 'vue';
import axios from "axios";
import { notification } from 'ant-design-vue';
import router from "@/router";
export default defineComponent({
name: "login-view",
setup() {
const loginForm = reactive({
mobile: '13000000000',
code: '',
});
const onFinish = values => {
};
const onFinishFailed = errorInfo => {
};
const sendCode = () => {
axios.post("/member/member/sendCode", {
mobile: loginForm.mobile
}).then(response => {
console.log(response);
let data = response.data;
if (data.success) {
notification.success({ description: '发送验证码成功!' });
loginForm.code = "8888";
} else {
notification.error({ description: data.message });
}
});
};
const login = () => {
axios.post("/member/member/login", loginForm).then(response => {
let data = response.data;
if (data.success) {
notification.success({ description: '登录成功!' });
} else {
notification.error({ description: data.message });
router.push("/")
}
})
};
return {
loginForm,
sendCode,
login
};
},
});
</script>
<style>
.login-main h1 {
font-size: 25px;
font-weight: bold;
}
.login-main {
margin-top: 100px;
padding: 30px 30px 20px;
border: 2px solid grey;
border-radius: 10px;
background-color: #fcfcfc;
}
</style>
the-header.vue
<template>
<a-layout-header class="header">
<div class="logo" />
<a-menu
v-model:selectedKeys="selectedKeys1"
theme="dark"
mode="horizontal"
:style="{ lineHeight: '64px' }"
>
<a-menu-item key="1">nav 11</a-menu-item>
<a-menu-item key="2">nav 2</a-menu-item>
<a-menu-item key="3">nav 3</a-menu-item>
</a-menu>
</a-layout-header>
</template>
<script>
import {defineComponent, ref} from 'vue';
export default defineComponent({
name: "the-header-view",
setup() {
return {
selectedKeys1: ref(['2']),
};
},
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
the-sider.vue
<template>
<a-layout id="components-layout-demo-top-side-2">
<the-header-view></the-header-view>
<a-layout>
<a-layout-sider width="200" style="background: #fff">
<a-menu
v-model:selectedKeys="selectedKeys2"
v-model:openKeys="openKeys"
mode="inline"
:style="{ height: '100%', borderRight: 0 }"
>
<a-sub-menu key="sub1">
<template #title>
<span>
<user-outlined />
subnav 1
</span>
</template>
<a-menu-item key="1">option1</a-menu-item>
<a-menu-item key="2">option2</a-menu-item>
<a-menu-item key="3">option3</a-menu-item>
<a-menu-item key="4">option4</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub2">
<template #title>
<span>
<laptop-outlined />
subnav 2
</span>
</template>
<a-menu-item key="5">option5</a-menu-item>
<a-menu-item key="6">option6</a-menu-item>
<a-menu-item key="7">option7</a-menu-item>
<a-menu-item key="8">option8</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub3">
<template #title>
<span>
<notification-outlined />
subnav 3
</span>
</template>
<a-menu-item key="9">option9</a-menu-item>
<a-menu-item key="10">option10</a-menu-item>
<a-menu-item key="11">option11</a-menu-item>
<a-menu-item key="12">option12</a-menu-item>
</a-sub-menu>
</a-menu>
</a-layout-sider>
<a-layout style="padding: 0 24px 24px">
<a-breadcrumb style="margin: 16px 0">
<a-breadcrumb-item>Home</a-breadcrumb-item>
<a-breadcrumb-item>List</a-breadcrumb-item>
<a-breadcrumb-item>App</a-breadcrumb-item>
</a-breadcrumb>
<a-layout-content
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
>
Content
</a-layout-content>
</a-layout>
</a-layout>
</a-layout>
</template>
<script>
import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons-vue';
import { defineComponent, ref } from 'vue';
import TheHeaderView from "@/components/the-header";
export default defineComponent({
name: "main-view",
components: {
TheHeaderView,
UserOutlined,
LaptopOutlined,
NotificationOutlined,
},
setup() {
return {
selectedKeys2: ref(['1']),
collapsed: ref(false),
openKeys: ref(['sub1']),
};
},
});
</script>
<style>
#components-layout-demo-top-side-2 .logo {
float: left;
width: 120px;
height: 31px;
margin: 16px 24px 16px 0;
background: rgba(255, 255, 255, 0.3);
}
.ant-row-rtl #components-layout-demo-top-side-2 .logo {
float: right;
margin: 16px 0 16px 24px;
}
.site-layout-background {
background: #fff;
}
</style>
index.js新增
{
path: '/',
component: () => import('../views/main.vue')
}
login.vue修改
const login = () => {
axios.post("/member/member/login", loginForm).then(response => {
let data = response.data;
if (data.success) {
notification.success({ description: '登录成功!' });
} else {
notification.error({ description: data.message });
router.push("/")
}
})
};