关键词:
element是饿了么团队开发的PC端用的基于vue的框架,之前在写app端的时候用的是Mint UI(饿了么团队)、vux(这个比较好用)。
element官网: https://element.eleme.cn/#/zh-CN
在这里直接下载git上别人写好的: vue-admin-template
1.下载运行vue-admin-template
git地址: https://github.com/PanJiaChen/vue-admin-template
下载之后进入到项目,执行安装相关依赖:
npm install --registry=https://registry.npm.taobao.org
运行之后还缺失一些模块,继续执行下面即可:
cnpm install
然后运行项目:
npm run dev
运行起来,访问即可,默认端口是9528:
补充:将该模板汉化。
默认是英语,参考:src/main.js第7行和第32行。如下我们使用日期控件的时候是英语证明当前的语言是英语:
切换汉语,注释掉src/main.js的第7行和32行并且放开第34行代码,最终如下:
Vue.use(ElementUI)
2.连接后台项目进行登录,前后端分离实现登录(springboot+SSM)
0.去掉一些不必要的js等文件
一开始我直接引入axios的时候发送的数据老是到不了后台,我去掉了其中的一些JS,因为模板本身对axios进行了拦截处理,我去掉之后可以正常发送数据。
1.引入axios
(1)到项目的根目录下面安装axios:
cnpm install --save axios
(2)src目录下新建axios\index.js,内容如下:(所有的请求加一个代理地址,对响应信息过滤处理)
import axios from "axios"; import { MessageBox } from 'element-ui'; // 引入常量模块 const defaultSettings = require('../settings.js') // 修改axios请求的默认配置(配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。) //` baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。 axios.defaults.baseURL = defaultSettings.serverBasePath; // 添加请求拦截器 axios.interceptors.request.use(function(config) { // 模拟处理前增加token return config; }, function(error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 axios.interceptors.response.use(function(response) { // 对响应数据做点什么 if(response.data.success) { // 如果是成功返回信息之后提取出来返回以供后面的调用链使用(后台返回的JSON数据) return response.data; } else { MessageBox.alert(response.data.msg, "提示信息"); // 返回一个新的Promise对象就相当于接触链式调用 return new Promise(function(resolve, reject) { // resolve('success1'); // reject('error'); }); } }, function(error) { // 对响应错误做点什么 return Promise.reject(error); }); export default axios;
(3)修改settings.js加入后台服务基地址
module.exports = { title: '丝绸之路商城', /** * @type {boolean} true | false * @description Whether fix the header */ fixedHeader: false, /** * @type {boolean} true | false * @description Whether show the logo in sidebar */ sidebarLogo: false, /** * 后台服务基地址,每个axios请求都会加这个,拦截请求进行代理 */ serverBasePath: '/api' }
(4)vue.config.js增加代理信息以及引入jquery
'use strict' const path = require('path') const defaultSettings = require('./src/settings.js') const webpack = require("webpack") function resolve(dir) { return path.join(__dirname, dir) } const name = defaultSettings.title || 'vue Admin Template' // page title // If your port is set to 80, // use administrator privileges to execute the command line. // For example, Mac: sudo npm run // You can change the port by the following methods: // port = 9528 npm run dev OR npm run dev --port = 9528 const port = process.env.port || process.env.npm_config_port || 9528 // dev port // All configuration item explanations can be find in https://cli.vuejs.org/config/ module.exports = { /** * You will need to set publicPath if you plan to deploy your site under a sub path, * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, * then publicPath should be set to "/bar/". * In most cases please use '/' !!! * Detail: https://cli.vuejs.org/config/#publicpath */ publicPath: '/', outputDir: 'dist', assetsDir: 'static', lintOnSave: process.env.NODE_ENV === 'development', productionSourceMap: false, devServer: { port: port, open: true, overlay: { warnings: false, errors: true }, proxy: { '/api': { target: 'http://localhost:8088', ws: true, changeOrigin: true, pathRewrite: { '^/api': '' } } } }, configureWebpack: { // provide the app's title in webpack's name field, so that // it can be accessed in index.html to inject the correct title. name: name, resolve: { alias: { '@': resolve('src') } }, plugins: [ new webpack.ProvidePlugin({ jQuery: "jquery", $: "jquery" }) ] }, chainWebpack(config) { config.plugins.delete('preload') // TODO: need test config.plugins.delete('prefetch') // TODO: need test // set svg-sprite-loader config.module .rule('svg') .exclude.add(resolve('src/icons')) .end() config.module .rule('icons') .test(/\.svg$/) .include.add(resolve('src/icons')) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader') .options({ symbolId: 'icon-[name]' }) .end() // set preserveWhitespace config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap(options => { options.compilerOptions.preserveWhitespace = true return options }) .end() config // https://webpack.js.org/configuration/devtool/#development .when(process.env.NODE_ENV === 'development', config => config.devtool('cheap-source-map') ) config .when(process.env.NODE_ENV !== 'development', config => { config .plugin('ScriptExtHtmlWebpackPlugin') .after('html') .use('script-ext-html-webpack-plugin', [{ // `runtime` must same as runtimeChunk name. default is `runtime` inline: /runtime\..*\.js$/ }]) .end() config .optimization.splitChunks({ chunks: 'all', cacheGroups: { libs: { name: 'chunk-libs', test: /[\\/]node_modules[\\/]/, priority: 10, chunks: 'initial' // only package third parties that are initially dependent }, elementUI: { name: 'chunk-elementUI', // split elementUI into a single package priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm }, commons: { name: 'chunk-commons', test: resolve('src/components'), // can customize your rules minChunks: 3, // minimum common number priority: 5, reuseExistingChunk: true } } }) config.optimization.runtimeChunk('single') } ) } }
2.修改登录逻辑
(1)src\views\login\index.vue修改登录处理(登录成功之后设置token,并且跳转路由到桌面。后台返回的是user信息,包括基本信息以及roles角色)
async handleLogin() { // 异步登录 var response = await axios.post('/doLoginJSON.html', { username: this.loginForm.username, password: this.loginForm.password }); // 登录成功之后的处理 if(response.success) { // 显示文字 Message({message: '登录成功', type: 'success'}); // 将用户信息作为token存入sessionStorage setToken(response.data); // 跳转路由 this.$router.replace("/dashboard"); } },
(2)修改src\utils\auth.js中setToken和getToken的方法(原来是存到cookie中,现在我存到sessionStorage中。roles也是后台返回的roles数组信息)。
const TokenKey = 'vue_admin_template_token' export function getToken() { const token = sessionStorage.getItem(TokenKey); if (token) { return JSON.parse(token); } return ""; } export function setToken(token) { if (!token) { return; } // 将用户存入sessionStorage sessionStorage.setItem("userid", token.id); sessionStorage.setItem("username", token.username); sessionStorage.setItem("fullname", token.fullname); sessionStorage.setItem(TokenKey, JSON.stringify(token)); } export function removeToken() { sessionStorage.removeItem("userid"); sessionStorage.removeItem("username"); sessionStorage.removeItem("fullname"); sessionStorage.removeItem(TokenKey); } export function getRoles() { const rolesArray = []; const token = sessionStorage.getItem(TokenKey); if (token) { rolesArray.push(JSON.parse(token).roles); } return rolesArray; }
(3)后台登录逻辑如下
/** * 处理登陆请求(JSON数据) * * @param username * @param password * @param session * @return */ @RequestMapping("doLoginJSON") @ResponseBody public JSONResultUtil doLoginJSON(@RequestBody User user, HttpSession session, HttpServletRequest request) { User loginUser = userService.getUserByUserNameAndPassword(user.getUsername(), user.getPassword()); logger.debug("loginUser: {}", loginUser); if (loginUser == null) { return JSONResultUtil.error("账号或者密码错误"); } session.setAttribute("user", loginUser); return new JSONResultUtil<User>(true, "登录成功", loginUser); }
3.修改登出逻辑
(1)修改src\layout\components\Navbar.vue
<el-dropdown-item divided @click.native="doLogout"> <span style="display:block;">退出</span> </el-dropdown-item>
登出方法如下:向后台发送登录请求,跳转路由之后调用auth.utils的removeToken方法删掉token信息。
async doLogout() { const logoutUrl = "/logoutJSON.html"; const response = await axios.post(logoutUrl); // 跳转路由 this.$router.replace("/login"); // 删除token removeToken(); },
(2)后台springboot登录逻辑如下:
package cn.qs.controller.system; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import cn.qs.utils.JSONResultUtil; /** * 退出登陆 * * @author Administrator * */ @Controller public class LogoutController { @RequestMapping("logout") public String logout(HttpSession session) { session.removeAttribute("user"); return "redirect:/login.html"; } @RequestMapping("logoutJSON") @ResponseBody public JSONResultUtil logoutJSON(HttpSession session) { session.removeAttribute("user"); return JSONResultUtil.ok(); } }
4. 模板中加入自己的菜单,并且实现权限控制
(1)src\router\index.js文件中constantRoutes里面加入用户管理菜单:
{ path: '/user', component: Layout, redirect: '/user/list', name: 'user', alwaysShow: true, meta: { title: '用户管理', icon: 'example', roles: ["系统管理员"] }, children: [ { path: 'list', name: 'List', component: () => import('@/views/user/index'), meta: { title: '用户列表', icon: 'table' } } ] },
这个有好几个属性,在文件头部也说明了。
/** * Note: sub-menu only appear when route children.length >= 1 * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html * * hidden: true if set true, item will not show in the sidebar(default is false) * alwaysShow: true if set true, will always show the root menu * if not set alwaysShow, when item has more than one children route, * it will becomes nested mode, otherwise not show the root menu * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb * name:'router-name' the name is used by <keep-alive> (must set!!!) * meta : { roles: ['admin','editor'] control the page roles (you can set multiple roles) title: 'title' the name show in sidebar and breadcrumb (recommend set) icon: 'svg-name' the icon show in the sidebar breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) activeMenu: '/example/list' if set path, the sidebar will highlight the path you set } */
简单解释几个有用的:
hidden: 是否隐藏,默认否,隐藏之后不会在左边菜单栏显示。
alwaysShow:为true的时候表示只有一个子菜单也显示父菜单,false会隐藏父菜单,只显示子菜单。
redirect: 重定向的路由
name:路由名称。
meta:[
title: '菜单显示的名称',
icon:'显示的图标'
roles; ['r1', 'r2'] // 需要的权限
]
(2)上面的meta的roles默认没生效,需要修改src\layout\components\Sidebar\SidebarItem.vue文件:
增加hasRoles(item)方法进行解释判断,获取当前用户存到sessionStorage的角色信息进行匹配。
<template> <div v-if="!item.hidden && hasRoles(item)"> <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> </el-menu-item> </app-link> </template> <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> <template slot="title"> <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> </template> <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" /> </el-submenu> </div> </template> <script> import path from 'path' import { isExternal } from '@/utils/validate' import Item from './Item' import AppLink from './Link' import FixiOSBug from './FixiOSBug' import { getRoles } from '@/utils/auth' export default { name: 'SidebarItem', components: { Item, AppLink }, mixins: [FixiOSBug], props: { // route object item: { type: Object, required: true }, isNest: { type: Boolean, default: false }, basePath: { type: String, default: '' } }, data() { // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237 // TODO: refactor with render function this.onlyOneChild = null return {} }, methods: { // 根据角色过滤按钮 hasRoles(item) { if (item && item.meta && item.meta.roles && item.meta.roles.length >0 ) { const userRoles = getRoles(); if (!userRoles) { return false; } var index = 0; for (index in userRoles) { if (item.meta.roles.indexOf(userRoles[index]) > -1) { return true; } } return false; } return true; }, hasOneShowingChild(children = [], parent) { const showingChildren = children.filter(item => { if (item.hidden) { return false } else { // Temp set(will be used if only has one showing child) this.onlyOneChild = item return true } }) // When there is only one child router, the child router is displayed by default if (showingChildren.length === 1) { return true } // Show parent if there are no child router to display if (showingChildren.length === 0) { this.onlyOneChild = { ... parent, path: '', noShowingChildren: true } return true } return false }, resolvePath(routePath) { if (isExternal(routePath)) { return routePath } if (isExternal(this.basePath)) { return this.basePath } return path.resolve(this.basePath, routePath) } } } </script>
实际中还实现了整合vue-kindeditor实现文本编辑器(参考:这个)、分页查询商品等操作。
git地址: https://github.com/qiao-zhi/vue_springboot_shop
基于vue+springboot实现的前后端分离的商城项目,包含秒杀模块(毕设)
本项目前后端分离,前端基于Vue+Vue-router+Vuex+Element-ui+Axios,后端基于SpringBoot+Redis+RabbitMQ+MySQL实现。实现了用户注册与登录,商城首页展示,商品分类展示,商品详情页,购物车, 查看详情
电商门户网站商品品类多级联动springboot+thymeleaf实现
...做的,所以不说明前端实现,只介绍后端实现。搭建部署SpringBoot环境配置文件配置:开启了对Thymeleaf模块引擎的支持server:port:8081#logging:#config:classpath:logback_spring.xml#level:#com.muses.taoshop:de 查看详情
java之springcloud微服务的开源配置中心apollo(第四个阶段)springboot项目实现商品服务器端调用(代码片段)
...ava之SpringCloud微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务Eureka(第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务搭建Ribbon... 查看详情
java网络商城项目springboot+springcloud+vue网络商城(ssm前后端分离项目)十一(商品的功能实现-商品的增删改查)(代码片段)
Java网络商城项目SpringBoot+SpringCloud+Vue网络商城(SSM前后端分离项目)十一(商品的功能实现)一、商品的新增1、页面分析(1)点击新增商品触发事件弹出窗口show对应的true和false使其显示和隐藏点击... 查看详情
springboot+rabbitmq+redis实现商品秒杀
业务分析一般而言,商品秒杀大概可以拆分成以下几步:用户校验校验是否多次抢单,保证每个商品每个用户只能秒杀一次下单订单信息进入消息队列,等待消费减少库存消费订单消息,减少商品库存,增加订单记录付款十五分... 查看详情
java之springcloud微服务的开源配置中心apollo(第四个阶段)springboot项目实现商品服务器端调用(代码片段)
...ava之SpringCloud微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务Eureka(第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务搭建Ribbonÿ 查看详情
java网络商城项目springboot+springcloud+vue网络商城(ssm前后端分离项目)六(商品分类功能实现)(代码片段)
一、业务实现1、实现商品分类查询商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息,其关系如图所示∶一个商品分类下有很多商品一个商品分类下有很多品牌而一... 查看详情
java之springcloud微服务的链路追踪sleuth和zipkin(第三个阶段)springboot项目实现商品服务器端是调用
...ava之SpringCloud微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务Eureka(第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务搭建Ribbonÿ 查看详情
java之springcloud微服务eureka(第一个阶段)springboot项目实现商品服务器端是调用
...ava之SpringCloud微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务Eureka(第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务搭建Ribbonÿ 查看详情
基于springboot实现商品进销存管理系统(代码片段)
...目编号:BS-XX-127一,项目简介本项目实现了基于springboot的进销存管理系统,主要用户开设网 查看详情
基于springboot实现商品进销存管理系统(代码片段)
...目编号:BS-XX-127一,项目简介本项目实现了基于springboot的进销存管理系统,主要用户开设网 查看详情
springboot实现发送简单邮件(代码片段)
文章目录SpringBoot实现发送简单邮件0、发送邮件的简单原理介绍1、开启我们发送方邮件的STMP服务2、加入Mail依赖,使得SpringBoot项目支持邮件服务3、配置信息,连接邮箱服务器以及STMP服务4、使用SpringBoot提供的对象使用邮件服... 查看详情
springboot结合rabbitmq与redis实现商品的并发下单springboot系列12(代码片段)
...ringBooot项目开发与SpringCloud微服务系列项目开发1项目准备SpringBoot整合RabbitMQ消息队列【SpringBoot系列11】本文章基于这个项目来开发本文章是系列文章,每节文章都有对应的代码,每节的源码都是在上一节的基础上配置而... 查看详情
springboot实现简单的文件上传
承接上一篇,这里记录一下简单的springboot文件上传的方式首先,springboot简单文件上传不需要添加额外的jar包和配置这里贴一下后端controller层的实现代码补一份前台的HTML代码补充,这里实现的是简单的单文件上传,没有指定存... 查看详情
java之springcloud微服务搭建consul(第一个阶段)springboot项目实现商品服务器端是调用(代码片段)
...ava之SpringCloud微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务Eureka(第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】Java之SpringCloud微服务搭建Ribbonÿ 查看详情
redis--springboot实现redis的分布式锁
目录1.redis的应用场景2.redis的分布式锁3.通过redisson框架实现redis分布式锁1.redis的应用场景商品秒杀点赞等现在有一个减少商品的场景,我们很容易能写出其代码@Controller@ResponseBodypublicclassTest{@AutowiredprivateStringRedisTemplateredisTemplate;... 查看详情
springboot最简单的错误页面实现方法
工具:MyEclipse2016SpringBoot版本:2.0.4实现方法,如图:只要按照以上目录层次,创建对应的异常提示页面,便不需要自定义错误跳转的的异常捕捉类,即可实现对应的异常信息展示,应该是SpringBoot的内部默认机制。省时省力方便... 查看详情
十九springboot整合elasticsearch实战(万字篇)
本章开始学习springboot整合ElasticSearch 7.X版本并通过小demo实现基本的增删改查。实现如下案例:1、当向数据新增一个商品信息时,同时向rabbitMQ发起消息(异步实现),让监听到消息的类去向ElasticS... 查看详情