文章目录
路由二级路由vue电影项目思路和需求后端:前端:
路由传参程序式路由传参路由守卫全局路由守卫局部路由守卫
vuex验证登录后端:前端:
Element
路由
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630152121548.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
配置路由 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630152404763.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
具体如下 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210628100343366.png)
export default {
name: 'App',
data () {
return {
}
},
methods: {
},
components: {
}
}
改成Index.vue ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629013328997.png)
我是首页
export default {
}
我是电影
export default {
}
我的信息是我的信息
export default {
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210628100837165.png)
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/views/Index'
import Movie from '@/views/Movie'
import My from '@/views/My'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/index',
component: Index
},
{
path: '/movie',
component: Movie
},
{
path: '/my',
component: My
}
]
})
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210628102625887.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210628102856779.png)
首页
电影
首页
export default {
}
.toolbar ul{
display: flex;
background-color: yellow;
padding: 0px;
}
.toolbar ul li{
flex: 1;
list-style: none;
text-align: center;
}
.active{
font-style: italic;
background-color: tomato;
}
结果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210628102956390.png)
二级路由
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630154625148.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630154722721.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
vue电影项目
思路和需求
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630152841682.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
后端:
数据库表: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630120043265.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629144222107.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
逆向生成: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629144301145.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629144814236.png)
public interface MoviesService {
/**
* 获得正在热映的电影集合
*/
List getNowPlaying();
/**
* 获得马上要上映的电影集合
*/
ListgetSoonPlaying();
}
@Service
public class MoviesServiceImpl implements MoviesService {
@Autowired
private MoviesMapper moviesMapper;
@Override
public List getNowPlaying() {
//1.获得当前时间
Date now = new Date();
MoviesExample example = new MoviesExample();
example.createCriteria().andUpDateLessThan(now).andDownDataGreaterThan(now);
List movies = moviesMapper.selectByExample(example);
return movies;
}
@Override
public List getSoonPlaying() {
//now(start) ontime end(now+7)
Date start = new Date();
//
Calendar calendar = Calendar.getInstance();
calendar.setTime(start);
//日历加七天
calendar.add(Calendar.DAY_OF_MONTH,7);
//calendar转回date
Date end = calendar.getTime();
MoviesExample example = new MoviesExample();
example.createCriteria().andUpDateBetween(start,end);
List movies = moviesMapper.selectByExample(example);
return movies;
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629203412897.png)
@Slf4j
@RestController
@RequestMapping("/api")
@Api(description = "电影管理【网站】")
@CrossOrigin(
origins = "*",
maxAge = 3600
)
public class ApiMoviesController {
@Autowired
private MoviesService moviesService;
/**
* 获得正在播放的电影
*/
@GetMapping("/nowmovie")
@ApiOperation("获得正在播放的电影")
public R getNowMovie(){
List nowPlaying = moviesService.getNowPlaying();
return new R(ResponseEnum.SUCCESS,nowPlaying);
}
/**
* 获得马上播放的电影
*/
@GetMapping("/soonmovie")
@ApiOperation("获得马上播放的电影")
public R getSoonMovie(){
List soonPlaying = moviesService.getSoonPlaying();
return new R(ResponseEnum.SUCCESS,soonPlaying);
}
}
发布效果: 结果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630085653784.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
后端完成:
前端:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630130402627.png)
import Vue from 'vue'
import App from './App'
import router from './router'
// 导入组件
import ToolBar from './components/ToolBar'
Vue.config.productionTip = false
// 声明全局组件
Vue.component('ToolBar', ToolBar)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: ''
})
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630130105672.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
轮播图
正在热映
即将热映
div.lunbo {
height: 200px;
background-color: chartreuse;
}
div.nav{
height: 50px;
margin-top:5px;
}
div.nav ul{
display: flex;
height: 50px;
margin: 0px;
padding: 0px;
}
div.nav ul li{
list-style-type: none;
flex: 1;
text-align: center;
height: 50px;
line-height: 50px;
}
.active{
background-color: cadetblue;
color: red;
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630130132164.png)
我是首页
export default {
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630130149557.png)
我的信息是我的信息
export default {
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629013220875.png)
{{item.name}}
{{item.info}}
import axios from 'axios'
export default {
data () {
return {
list: []
}
},
created () {
this.getList()
},
methods: {
async getList () {
let response = await axios({
method: 'get',
url: 'http://localhost:9000/api/nowmovie'
})
this.list = (await response).data.data
}
}
}
div.movieitem{
margin-top: 5px;
display: flex;
}
div.movieitem img{
width: 70px;
height: 70px;
margin-right: 10px;
}
{{item.name}}
{{item.info}}
import axios from 'axios'
export default {
data () {
return {
list: []
}
},
created () {
this.getList()
},
methods: {
async getList () {
let response = await axios({
method: 'get',
url: 'http://localhost:9000/api/soonmovie'
})
this.list = (await response).data.data
}
}
}
div.movieitem{
margin-top: 5px;
display: flex;
}
div.movieitem img{
width: 70px;
height: 70px;
margin-right: 10px;
}
其中部分改为: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630174350671.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630130311923.png)
export default {
name: 'App',
data () {
return {
}
},
methods: {
},
components: {
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210629013443436.png)
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/views/Index'
import Movie from '@/views/Movie'
import My from '@/views/My'
import NowPlaying from '@/views/Movie/NowPlaying'
import SoonPlaying from '@/views/Movie/SoonPlaying'
// import App from '@/App'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect: '/index'
},
{
path: '/index',
component: Index
},
{
path: '/movie',
component: Movie,
redirect: '/movie/nowplaying',
children: [{
path: '/movie/nowplaying',
component: NowPlaying
},
{
path: '/movie/soonplaying',
component: SoonPlaying
}]
},
{
path: 'my', component: My
}]
})
``
**App.vue**
```java
export default {
name: 'App',
data () {
return {
}
},
methods: {
},
components: {
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630125906453.png)
首页
电影
我的
.toolbar ul{
display: flex;
background-color: yellow;
padding: 0px;
}
.toolbar ul li{
flex: 1;
list-style: none;
text-align: center;
}
.active{
font-style: italic;
background-color: tomato;
}
效果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630121732400.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
路由传参
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630164233843.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
具体如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630164409607.png)
我是详情组件
export default {
data () {
return {
}
},
// 组件初始化去查询电影的信息
created () {
this.init()
},
methods: {
init () {
console.log(this.$route.params.id)
// 通过id去数据库查询电影的详情并展示
}
}
}
效果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630164537172.png)
程序式路由传参
除上述外还有一种传参: 效果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630175123335.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
路由守卫
全局路由守卫
保护所有的组件 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630181703640.png)
我是登录组件
export default {
mounted () {
this.$.parent.toolbarshow = false
},
destroyed () {
this.$.parent.toolbarshow = true
}
}
index.js ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630182104363.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
局部路由守卫
保护单个的组件: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630192018945.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
vuex
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630214225126.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630214233979.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630205311335.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
// import { constant } from 'async'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.user(Vuex)
// 全局 state 对象,用于保存所有组件的公共数据
// 保存全局的会话数据(session)
const state = {
nowList: [],
soonList: []
}
// 实时监听 state 值的最新状态,注意这里的 getters 可以理解为计算属性
const getters = {
// 在组件中是通过 this.$store.getters.getUser 来获取
getNowList (state) {
return state.nowList
},
getSoonList (state) {
return state.soonList
}
}
// 定义改变 state 初始值的方法,这里是唯一可以改变 state 的地方,缺点是只能同步执行
const mutations = {
// 在组件中是通过 this.$store.commit('updateUser', user); 方法来调用 mutations
updataNowList (state, nowList) {
state.nowList = nowList
},
updataSoonList (state, soonList) {
state.soonList = soonList
}
}
// 定义异步修改状态的值
const actions = {
// 定义触发 mutations 里函数的方法,可以异步执行 mutations 里的函数
asyncUpdateNowList (context, nowList) {
context.commit('updateNowList', nowList)
},
asyncUpdateSoonList (context, soonList) {
context.commit('updateSoonList', soonList)
}
}
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701091317474.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701091519523.png)
自己项目具体实施: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210630214117159.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
验证登录
后端:
选中已经搭建好的框架- ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701120952416.png)
/**
* login的方法
*/
@Transactional(readOnly = true)
public User login(String userName,String userPassword){
UserExample example = new UserExample();
example.createCriteria()
.andUserNameEqualTo(userName);
List users = userMapper.selectByExample(example);
if(users==null||users.size()==0){
throw new AppException(ResponseEnum.USERNAME_NOT_FOUND );
}
User user = users.get(0);
if(!(userPassword.equals(user.getUserPassword()))){
throw new AppException(ResponseEnum.USERNAME_OR_PASSWORD_INVALIDATE);
}
return user;
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701121044800.png)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginDTO {
private String userName;
private String userPassword;
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701121131750.png)
/**
* 登录
*/
@PostMapping("/login")
private R login(String userName,String userPassword){
User userinfo = userService.login(userName,userPassword);
return new R(ResponseEnum.SUCCESS,userinfo);
}
自测: ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021070112132015.png)
前端:
![在这里插入图片描述](https://img-blog.csdnimg.cn/202107011730416.png)
// import { constant } from 'async'
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 全局 state 对象,用于保存所有组件的公共数据
// 保存全局的会话数据(session)
const state = {
nowList: [],
soonList: [],
userinfo: {}
}
// 实时监听 state 值的最新状态,注意这里的 getters 可以理解为计算属性
const getters = {
// 在组件中是通过 this.$store.getters.getUser 来获取
getNowList (state) {
return state.nowList
},
getSoonList (state) {
return state.soonList
},
getUserinfo (state) {
return state.userinfo
}
}
// 定义改变 state 初始值的方法,这里是唯一可以改变 state 的地方,缺点是只能同步执行
const mutations = {
// 在组件中是通过 this.$store.commit('updateUser', user); 方法来调用 mutations
updateNowList (state, nowList) {
state.nowList = nowList
},
updateSoonList (state, soonList) {
state.soonList = soonList
},
updateUserinfo (state, userinfo) {
state.userinfo = userinfo
}
}
// 定义异步修改状态的值
const actions = {
// 定义触发 mutations 里函数的方法,可以异步执行 mutations 里的函数
asyncUpdateNowList (context, nowList) {
context.commit('updateNowList', nowList)
},
asyncUpdateSoonList (context, soonList) {
context.commit('updateSoonList', soonList)
},
asyncUpdateUserinfo (context, userinfo) {
context.commit('updateUserinfo', userinfo)
}
}
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701173123568.png)
用户名:
用户密码:
登录
import axios from 'axios'
import qs from 'qs'
export default {
data () {
return {
user: {
userName: '',
userPassword: ''
}
}
},
methods: {
async login () {
// console.log(this.user)
let response = await axios({
method: 'post',
url: 'http://localhost:9000/api/user/login',
data: qs.stringify(this.user)
})
console.log(response)
if (response.data.code === '200') {
// 进入my之前要同步修改vuex中的userinfo
this.$store.commit('updateUserinfo', response.data.data)
// 进入到my
this.$router.push('/my')
} else {
alert(response.data.message)
}
}
},
mounted () {
this.$.parent.toolbarshow = false
},
destroyed () {
this.$.parent.toolbarshow = true
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701173313536.png)
我的信息是我的信息
import mystore from '@/store'
export default {
// 进入组织之前调用的方法
beforeRouteEnter (to, from, next) {
// 1.是否登录
// 1.1怎么判断是否登录 vuex中的userinfo是否有值
if (mystore.state.userinfo.id) {
next()
} else {
next('/login')
}
}
}
效果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701173447824.png)
Element
https://element.eleme.cn/#/zh-CN/component/form 让布局更美观 插件作为全局来使用: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701194516841.png)
提交
重置
import axios from 'axios'
import qs from 'qs'
export default {
data () {
return {
// form绑定的数据
user: {
userName: '',
userPassword: ''
},
// form绑定的验证规则
rules: {
userName: [
{ required: true, message: '请输入用户名称', trigger: 'blur' }
],
userPassword: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
]
}
}
},
methods: {
login () {
// 看form是否验证通过
this.$refs['ruleForm'].validate(async (valid) => {
console.log(valid)
if (valid) {
// console.log(this.user)
let response = await axios({
method: 'post',
url: 'http://localhost:9000/api/user/login',
data: qs.stringify(this.user)
})
console.log(response)
if (response.data.code === '200') {
// 进入my之前要同步修改vuex中的userinfo
this.$store.commit('updateUserinfo', response.data.data)
// 进入到my
this.$router.push('/my')
} else {
alert(response.data.message)
}
}
})
}
},
mounted () {
this.$.parent.toolbarshow = false
},
destroyed () {
this.$.parent.toolbarshow = true
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701194534766.png)
我的信息是我的信息
import mystore from '@/store'
export default {
// 进入组织之前调用的方法
beforeRouteEnter (to, from, next) {
// 1.是否登录
// 1.1怎么判断是否登录 vuex中的userinfo是否有值
if (mystore.state.userinfo.id) {
next()
} else {
next('/login')
}
}
}
效果 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701194605829.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDY1NTQxOA==,size_16,color_FFFFFF,t_70)
|