springboot2.x+security+vue2.x后台管理框架---前端搭建(二)

前端搭建

一、安装全局cli脚手架

npm install -g @vue/cli

二、使用vue ui创建项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

三、vue基础配置

1,baseConfig.js配置

const baseConfig = () =>{
    return {
        // 生产环境 TODO
        // context: "/longzy",
        // basePath: "/longzy"
        
        // 开发环境
        context: "/longzy",
        basePath: "http://localhost:8087/longzy",
    }
}
module.exports = baseConfig()

2、vue.config.js配置

'use strict'

const { defineConfig } = require('@vue/cli-service')
const baseConfig = require('./baseConfig')

module.exports = defineConfig({
  // 上下文
  publicPath: baseConfig.context,
  // 输出文件目录
  outputDir: 'dist',
  // 静态资源目录
  assetsDir: '',
  // 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径
  indexPath: 'index.html',
  // 默认在生成的静态资源文件名中包含hash以控制缓存
  filenameHashing: true,
  // 是否在保存的时候使用 `eslint-loader` 进行检查
  lintOnSave: false,
  // 是否使用包含运行时编译器的 Vue 构建版本
  runtimeCompiler: false,
  // dev环境下,webpack-dev-server 相关配置
  devServer: {
    host: '0.0.0.0',
    port: '8080',
    https: false,
    open: false,
    proxy: {
      [baseConfig.basePath]: {
        target: baseConfig.basePath,
        changeOrigin: true,
        pathRewrite: {
          ['^/' + baseConfig.basePath]: ''
        }
      }
    }
  },
  //默认情况下 babel-loader 会忽略所有 node_modules 中的文件
  transpileDependencies: true
  
})

3、axios配置

(1)、引入依赖

npm install axios

npm insatll element-ui -S

(2)、axios.js封装
import axios from 'axios'     //引入
import { Message, MessageBox } from 'element-ui'
import baseConfig from '../../baseConfig' 
import loading from '@/util/loading'
 
let config = {
  baseURL: baseConfig.basePath,
  timeout: 30000, //设置最大请求时间
  headers: {
		'Content-Type': "application/x-www-form-urlencoded; charset=utf-8"
	}       
}
const _axios = axios.create(config)
 
/* 请求拦截器(请求之前的操作) */
_axios.interceptors.request.use(
  config => {
    //如果有需要在这里开启请求时的loading动画效果
    if (config.boolean){
      loading.showLoading(config.boolean)
    }
    config.headers['Authorization'] = localStorage.getItem("token")  //添加token,需要结合自己的实际情况添加,
    return config;
  },
  err => {
    loading.hideLoading()
    Promise.reject(err)
  }
);
 
/* 请求之后的操作 */
_axios.interceptors.response.use(
  res => {
    //在这里关闭请求时的loading动画效果
    loading.hideLoading()
    if (res.data.code != 200 ) {
        Message({
          message: res.data.message,
          type: 'error',
          showClose: true,
          duration: 5 * 1000
        })  
    }
    return res;
  },
  err => {
    if (err) {
      // token过期处理
      if (err.response.status == '401'){
        localStorage.clear()
        sessionStorage.clear()
        window.location.href = '/login'
      }
      //在这里关闭请求时的loading动画效果
      loading.hideLoading()
      Message({
        message: err.message,
        showClose: true,
        type: 'error',
        duration: 5 * 1000
      })
    }
    return Promise.reject(err);
  }
);
 
export default _axios
(3)、request.js
// 导入封装好的axios实例
import axios from './axios'
import qs from 'qs'

const request = {
    /**
     * methods: get请求
     * @param url 请求地址 
     * @param params 请求参数
     * @param boolean 是否显示loading
     */
    get(url, params, boolean){
        const config = {
            method: 'get',
            url:url
        }
        if(params) config.params = qs.stringify(params)
        if(boolean) config.boolean = boolean
        return axios(config)
    },

    /**
     * methods: post请求
     * @param url 请求地址 
     * @param data 请求参数
     * @param boolean 是否显示loading
     */
    post(url,params, boolean){
        const config = {
            method: 'post',
            url:url
        }
        if(params) config.data = qs.stringify(params)
        if(boolean) config.boolean = boolean
        return axios(config)
    }
}
//导出
export default request

四、vue框架总体架构

效果图如下图:
在这里插入图片描述

1、Layout.vue

<template>
	<div class="app-wrapper">
		<div class="layout-aside" :class="{collapse:isCollapse}">
			<div class="layout-logo">
				<router-link to="/" style="text-decoration-line: none;">
					<img src="@/assets/logo.png" alt="logo"/>
					<span class="layout-font" v-show="!isCollapse">VUE 后台管理系统</span>
				</router-link>
			</div>
			<SideBar />
		</div>
		<div class="layout-container" :class="{collapse:isCollapse}">
			<div class="layout-header" :class="{collapse:isCollapse}">
				<Header />
			</div>
			<div class="layout-main">
				<AppMain />
			</div>
		</div>
	</div>
</template>

<script>
    import Header from './components/Header'
    import SideBar from './components/SideBar'
    import AppMain from './components/AppMain'
    import { mapState } from 'vuex'

    export default {
        name: 'layout',
        components: {Header, SideBar, AppMain},
        data(){
            return{
            }
        },
        computed: {
            ...mapState([
                'isCollapse'
            ])
           
        },
        methods: {
        

        }
    }



</script>

<style lang="less" scoped>
    .app-wrapper{
        position:relative;
    }
    .layout-aside{
        position:fixed;
        left:0;
        top:0;
        height:100%;
        width:300px;
        transition:all 0.3s;
        background-color:#304156;
        .layout-logo {
            height:60px;
            background-color:#2b2f3a;
            a{
                display:flex;
                width:100%;
                height:60px;
                justify-content:center;
                align-items:center;
            }
            img{
                width:32px;
                height:32px;
            }
            span{
                font-size:16px;
                line-height:60px;
                color:#fff;
                margin-left:30px;
            }
        }
            
    }
    .layout-aside.collapse{
        width:64px;
    }
    .layout-container{
        position: absolute;
        width: calc(100% - 300px);
        overflow:hidden;
        left: 300px;
    }
    .layout-container.collapse{
        transition:all 0.1s;
        width: calc(100% - 70px);
        left: 68px;
    }
    .layout-header{
        position:fixed;
        z-index:1;
        top:0;
        right:0;
        width:calc(100% - 300px);
        height:60px;
        box-shadow:0 1px 3px rgba(0,21,41,0.08);
        background-color:#fff;
        border-bottom: 5px solid rgb(0 21 41 / 8%);
    }
    .layout-header.collapse{
        width:calc(100% - 64px);
        transition:all 0.1s;
    }
    .layout-main{
        min-height:calc(100% - 100px);
        margin:70px 15px 10px 10px;
        background-color:#fff;
        padding-top: 20px;
    }
</style>

2、Header.vue

<template>
	<div class="header-wrapper">
		<div class="header-left">
			<div class="open-icon">
				<i :class="!isCollapse ? 'el-icon-s-fold' : 'el-icon-s-unfold'" @click="handleCollapse"></i>
			</div>
			<el-breadcrumb separator="/">
				<template v-for="(item,index) in breadcrumbList">
					<el-breadcrumb-item :key="index" v-if="item.meta.title" :to="{path:item.path}">
						{{item.meta.title}}
					</el-breadcrumb-item>
				</template>
			</el-breadcrumb>
		</div>
		<div class="header-right">
			<el-avatar size="small" icon="el-icon-user-solid"></el-avatar>
			<el-dropdown  trigger="click">
				<span class="el-dropdown-link">
					<span class="header-user">{{userInfo.name}}</span>
					<i class="el-icon-arrow-down el-icon--right"></i>
				</span>
				<el-dropdown-menu slot="dropdown">
					<el-dropdown-item icon="el-icon-user">信息编辑</el-dropdown-item>
					<el-dropdown-item icon="el-icon-circle-plus-outline">修改密码</el-dropdown-item>
					<el-dropdown-item icon="el-icon-remove-outline" @click.native="logout">退出登录</el-dropdown-item>
				</el-dropdown-menu>
			</el-dropdown>
		</div>
	</div>
</template>

<script>
import { mapState } from 'vuex';

	export default{
		name:'Header',
		data(){
			return {
				breadcrumbList:[],
			}
		},
		computed: {
			...mapState([
				'userInfo',
				'isCollapse'
			])
		},
		created() {
			this.getBreadCrumb()
        },
        //这里必须使用监听,否则无法实时获取路由变动信息。
        //监听后路由会实时变动,不然需要手动刷新路径才会改变
        watch:{
          $route(to,from)  {
             this.getBreadCrumb()
          }
        },
		methods:{
			handleCollapse(){
				this.$store.commit("SET_COLLAPSE", !this.isCollapse)
			},
			// 退出
			logout() {
				this.$request.post('/logout').then(res =>{
					localStorage.clear()
					sessionStorage.clear()
					this.$router.push("/login")
				})
			},
			// 面包屑
			getBreadCrumb() {
				let matched = this.$route.matched
				// 判断是否首页
				if (matched[0].name!= undefined && matched[0].name != '/home'){
					matched = [{path: '/home', meta: {title: '首页'}}].concat(matched)
				}
				this.breadcrumbList = matched
			}
		}
	}
</script>

<style lang="less" scoped>
    .header-wrapper{
        display:flex;
        justify-content:space-between;
        align-content:center;
        padding:0 20px;
        height:60px;
        .header-left{
            display:flex;
            align-items:center;
            .open-icon{
                font-size:20px;
                margin-right:15px;
                cursor:pointer;
            }
        }
        .header-right{
            display:flex;
            align-items:center;
			margin-right: 20px;
            .header-user{
                margin-left:15px;
				font-size: 18px;
            }
        }
    }
    .el-dropdown-link{
        cursor:pointer;
        color:#409eff;
        img{
            width:40px;
            height:40px;
            border-radius:5px;
        }
    }
    .el-icon-arrow-down{
        font-size:12px;
    }
</style>

3、SideBar.vue

<template>
	<!-- <el-scrollbar class="sidebar-scroll"> -->
		<el-menu class="el-menu-vertical-demo" 
			router 
			:unique-opened="true" 
			:collapse="isCollapse"  
			background-color="#304156" 
			text-color="#fff" 
			active-text-color="#409eff" 
			style="border:none"
			:collapse-transition="false">
			
		<template>
				
			<el-submenu index="1">
				<template slot="title">
					<i class="el-icon-setting"></i>
					<span>系统管理</span>
				</template>
				<el-menu-item index="/home">
					<template>
						<i class="el-icon-user-solid"></i>
						<span >用户管理</span>
					</template>
				</el-menu-item>
				<el-menu-item index="/menuManagement">
					<template>
						<i class="el-icon-menu"></i>
						<span >菜单管理</span>
					</template>
				</el-menu-item>
			</el-submenu>
			<el-submenu index="2">
				<template slot="title">
					<i class="el-icon-folder"></i>
					<span>资源管理</span>
				</template>
				<el-menu-item index="/dictManagement">
					<template>
						<i class="el-icon-reading"></i>
						<span >字典管理</span>
					</template>
				</el-menu-item>
			</el-submenu>
			
		</template>

		</el-menu>
	<!-- </el-scrollbar> -->
</template>

<script>
import { mapState } from 'vuex'
export default{
	name:'SideBar',
	data(){
		return{
		}
	},
	computed: {
		...mapState([
			'isCollapse'
		])

	},
	methods: {
		
	}
}

</script>
<style lang="less" scoped>
	.el-menu-vertical-demo{
		text-align: left;
	}
	// .sidebar-scroll{
	// 	height:calc(100% - 60px);
	// }
	// .sidebar{
	// 	height:100%;
	// 	text-align:left;
	// 	border-right:none;
	// }

	
</style>

4、AppMain.vue

<template>
	<div class="app-main">
		<transition name="fade-transfrom" mode="out-in">
			<keep-alive>
				<router-view />
			</keep-alive>
		</transition>
	</div>
</template>
<script>
    export default{
        name:'AppMain',
		data(){
			return{
			}
		}
    }
</script>

代码地址:

https://gitee.com/longzyl/longzy-vue2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值