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工程下商品子系统分析及实现

SpringBoot+RabbitMQ+Redis实现商品秒杀

电商门户网站商品品类多级联动SpringBoot+Thymeleaf实现

Vue+Element+computed实现购物车

Vue+Element+Springboot实现图片上传

Vue+Element+Springboot实现图片上传