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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM