• 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目录下新建axiosindex.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)srcviewsloginindex.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)修改srcutilsauth.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)修改srclayoutcomponentsNavbar.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 outerindex.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默认没生效,需要修改srclayoutcomponentsSidebarSidebarItem.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

  • 相关阅读:
    linq判断集合是否为空的方法
    MVC控制器取参数值
    linq查询结果转换为指定字段类型的list集合
    C#Web异步操作封装
    js基础细节
    写入临时日志到文本
    css3超过指定宽度文字,显示省略号
    如何判断Javascript对象是否存在
    chrome下input[type=text]的placeholder不垂直居中的问题解决
    sqlserver临时表操作
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/12457979.html
Copyright © 2020-2023  润新知