element+springboot实现简单的商品管理
Posted Qiao_Zhi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了element+springboot实现简单的商品管理相关的知识,希望对你有一定的参考价值。
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
以上是关于element+springboot实现简单的商品管理的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot+RabbitMQ+Redis实现商品秒杀