vue2.0 从0到1搭建后台管理项目(一)

一.使用版本

  • Node.js: 14.3.0
  • Vue: 2.6.10
  • Npm: 6.14.5
  • Webpack: 4.0.0
  • Node-sass: 4.14.1
  • Sass-loader: 7.3.1

二.使用技术

  • Vue 2.0
  • vue-router 路由
  • element-ui 组件
  • vuex 状态存储工具
  • axios 数据交互

三. 环境搭建

  1.      1. 安装node (node -v查询版本号)
  2.            node 安装 (下载地址:https://nodejs.org/en/download/)

  3.      2. 安装淘宝镜像

  4. npm install -g cnpm --registry=https://registry.npm.taobao.org
    1.      3. 安装 webpack,以全局的方式安装

  5. npm install webpack -g
  6.      4.全局安装vue以及脚手架vue-cli

  7. npm install @vue/cli -g --unsafe-perm
  8.      5.创建vue项目 mall-manage-system(项目名称)         

  9. vue create mall-manage-system

           5.1 选择使用vue版本直接回车

  10.    6. 运行当前项目 这个整个项目就搭建好了

   npm run serve 

四. 搭建项目

 1. 引入组件库 - 此项目使用element - ui组件库

      https://element.eleme.cn/#/zh-CN/component/installation

在main.js中引入

//新添
import ElementUI from 'element-ui' 
//新增
import 'element-ui/lib/theme-chalk/index.css'
//新增
Vue.use(ElementUI) 

2.创建路由 (router/index.js)

创建登录页面路由/页面路由

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
 * 处理控制台的下面报错
 * Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/home/list".
 * 添加以下代码
 */
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}
export const constantRoutes = [
  // 重定向,用来指向一打开网页就跳转到哪个路由
  {
    path: '/',
    redirect: '/login'
  },
  //login
  {
    path: '/login',
    component: () => import('@/views/login/login'),
  },

  {
    path: "/404",
    component: () => import("@/views/error-page/404")
  },
  {
    path: "/401",
    component: () => import("@/views/error-page/401")
  },
]
const createRouter = () => new Router({
  scrollBehavior: () => ({
    y: 0
  }),
  routes: constantRoutes
})
const router = createRouter()
export default router

3.创建登录界面

<template>
  <div class="sign-in">
    <el-row>
      <el-col :span="12" :offset="6">
        <h1>后台系统</h1>
        <el-form
          :model="loginForm"
          :rules="loginRules"
          ref="ruleForm"
          label-width="100px"
          class="demo-ruleForm"
        >
          <el-form-item label="手机号:" prop="user_phone">
            <el-input v-model="loginForm.user_phone"></el-input>
          </el-form-item>
          <el-form-item label="密码:" prop="sort">
            <el-input v-model="loginForm.password" type="password"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="handleLogin('ruleForm')">
              立即登录
            </el-button>
            <el-button @click="resetForm('ruleForm')">重置</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import { setBasicInfo } from "@/utils/auth";
import { Login, signIn } from "../../api/login/login";
import store from "@/store";
export default {
  data() {
    return {
      loginForm: {
        user_phone: "admin",
        password: "123123",
      },
      loginRules: {
        name: [{ required: true, message: "请输入手机号", trigger: "blur" }],
        password: [{ required: true, message: "请输入密码", trigger: "blur" }],
      },
    };
  },
  methods: {
    async handleLogin(formName) {
      this.$refs[formName].validate(async (valid) => {
        if (valid) {
         
          const res = await Login()
          if(!res.success) return  this.$message.error('错了哦!系统错误!');
          const data =res.data
          if(this.loginForm.user_phone === data.user_phone&&this.loginForm.password ===data.password){
            // const res2 = await signIn();
            const menu = await setBasicInfo()
            await store.dispatch('apply/getApply')
            const redirect = menu[0].path + '/' + menu[0].children[0].path
            await this.$router.push(redirect);
          }else{
            this.$message.error('错了哦!账号密码不正确!');
          }
        }
      });
    },

    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },
};
</script>

<style scoped lang="scss">
.sign-in {
  padding-top: 100px;
}

h1 {
  text-align: center;
}
</style>

   使用axios进行交互

   3.1 引入axios

         官网: http://www.axios-js.com/

   3.2 安装 axios 

npm install axios -S

    安装完成后在package.json中dependencies下可以看到安装是否成功

   3.3 点击登录进行接口请求获取token,将获取到的返回信息可存储到storage中或VUEX中

   3.4 设置路由守卫进行权限判断进入项目

         views下创建 permission.js文件

/* eslint-disable prefer-const */
import router from './router'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import getPageTitle from '@/utils/get-page-title'
import store from '@/store'
import {getToken, toLogin, setBasicInfo} from '@/utils/auth'

//NProgress.configure({ showSpinner: false }) // NProgress Configuration
NProgress.configure({
  easing: 'ease', // 动画方式
  speed: 500, // 递增进度条的速度
  showSpinner: false, // 是否显示加载ico
  trickleSpeed: 200, // 自动递增间隔
  minimum: 0.3 // 初始化时的最小百分比
})
// 白名单
const whiteList = ['/login'] // no redirect whitelist.
// 挂载路由导航守卫:to表示将要访问的路径,from表示从哪里来,next是下一个要做的操作
/**
  * to: Route: 即将要进入的目标 路由对象
  * from: Route: 当前导航正要离开的路由
  * next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
  * next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
  * next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  * next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
  * next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
 */
router.beforeEach(async (to, from, next) => {
  NProgress.start()
  document.title = getPageTitle(to.meta.title)
  if (whiteList.includes(to.path)) {
    next()

  } else {
    const token = getToken()
    if (!token) {
      await toLogin()
      return
    }

    if (store.state.permission.asyncRouteFlag === '1') {
      next()
     
    } else {
      try {
        const menu = await setBasicInfo(token)
        next({...to, replace: true})
        await store.dispatch('apply/getApply')
      } catch (e) {
        console.log(e, 'e')
      }
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})

utils/get-page-title.js

import defaultSettings from '@/settings'

const title = defaultSettings.title || 'SaaS'

export default function getPageTitle(pageTitle) {
  if (pageTitle) {
    return `${pageTitle} - ${title}`
  }
  return `${title}`
}

utils/auth.js

import Cookies from "js-cookie";
import { Message } from 'element-ui'
import router from '@/router'
import { userList } from "@/api/login/login";
import { getArea } from '@/api/common'
import store from "@/store";
import { generateRoutes } from '@/utils/generteRoute'
// 设置token
export const setToken = (token) => {
  storage.setItem(tokenKey, token)
}
// 获取token
export const getToken = () => {
  return storage.getItem(tokenKey) || ''
}
/**
 * 删除cookie中的token
 * @returns {*}
 */
export function removeToken() {
  return Cookies.remove(TokenKey);
}
const storage = window.localStorage
export const tokenKey = 'token'
// 清除token
export const clearToken = () => {
  storage.removeItem('swapOperationInfo')
  storage.removeItem('swapOperationCompany')
  store.dispatch('permission/setPreToken', '')
  storage.removeItem(tokenKey)
}
// 跳转到登录页面
// 跳转到登录页面
export const toLogin = () => {
  clearToken()
  // if (process.env.NODE_ENV === 'development') {
  //   router.replace('/login')
  // } else {
  //   setTimeout(() => {
  //     // window.location.replace(`${process.env.VUE_APP_LOGIN_PAGE}?active=${store.getters.app_id}`)
  //     window.location.replace(`${window.location.origin}/#/login`)
  //   }, 500)
  // }
  // // window.location.replace(`${process.env.VUE_APP_LOGIN_PAGE}?active=${store.getters.app_id}`)
}
/**
 * 根据token获取用户和权限信息
 * @param token
 * @returns {Promise<unknown>}
 */
export const setBasicInfo = async (token) => {
    const res = await userList(token);
    return new Promise(async (resolve, reject) => {
    if (res.success) {
      const route = (res.data || {}).route || [];
      if (!route.length) {
        Message({
          message: "抱歉!你没有任何页面的访问权限!",
          duration: 4000,
          type: "error",
        });
      } else {
        const userInfo = {
          telephone: res.data.telephone,
          role_name: res.data.role_name,
          role_id: res.data.role_id,
          user: res.data.user_name,
        };
        // 获取基础数据
        await getBasicData()
        await store.dispatch("user/setUserInfo", userInfo);
        await store.dispatch("user/setUserMenu", res.data.route || [])
        
        const accessRoutes = await generateRoutes(res.data.route)
        // 旧版本已废除 router.addRoutes
        // router.addRoutes(accessRoutes)

        // 新版本使用方法
        for (let item of accessRoutes) {
          router.addRoute(item)
        }

        setToken(res.data.token)
        resolve(res.data.route)
      }
    }
    })
}

// 获取基础数据-运营公司、供应商、地区, 并存入store
export const getBasicData = async () => {
  try {
    const areaRes = await getArea()
    await store.dispatch('options/setArea', (areaRes.data || {}).children || [])
  } catch (e) {
    Message.error('获取地区数据失败')
  }
}

utils/generteRoute.js

import Layout from '@/layout'
import store from '@/store'

// 组件路径转换成模块
export function componentRoutes(routes) {
  const res = []
  routes.forEach(route => {
    const component = route.component === 'Layout' ? Layout : function (resolve) {
      require([`@/views/${route.component}`], resolve)
    }
    const tmp = { ...route, component }
    if (tmp.children && tmp.children.length) {
      tmp.children = componentRoutes(tmp.children)
    }
    res.push(tmp)
  })
  return res
}

// 生成动态路由
export const generateRoutes = (asyncRoutes) => {
  return new Promise(resolve => {
    let accessedRoutes = componentRoutes(asyncRoutes)
    // 标记路由是否已经生成
    store.dispatch('permission/toggleRouteFlag', '1')
    accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
    resolve(accessedRoutes)
  })
}

    store内容查看下面文件夹进行下载

    https://download.csdn.net/download/weixin_64374806/87854628?spm=1001.2014.3001.5503

    

五.进入项目

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值