第一步:创建项目

vue create bigscreen

第二步:进行项目的的配置,在vue.config.js配置文件中配置

module.exports = {
    outputDir: process.env.VUE_APP_OUTPUTDIR,
    publicPath: process.env.VUE_APP_PUBLICPATH,

    css: {
        loaderOptions: {
            scss: {
                prependData: `@import "~@/index.scss";`//全局样式
            },
        }
    },

    devServer: {
        host: '',
        port: '80',
        disableHostCheck: true,
        // proxy: {
        //     // 统一认证模块API转发
        //     /* "/paycode/": {
        //          target: '',
        //          changeOrigin: true,
        //          logLevel: 'debug'
        //      },*/
        // },
        // setup: function handleAPIRequest(app) {

        // },
    },

第三步:页面文件夹的创建:

api文件夹,utils文件夹,公共样式style文件夹,router路由文件夹,store公共数据vuex文件夹,iconfont文件夹

第四步:配置项目所需要用到的框架下载:常用到的框架包

dependencies中放置的是我们生产环境中需要的依赖

devDependencies放置的是我们开发时用的依赖项,–save-dev

"axios": "^0.27.2",

"core-js": "^3.6.5",

"echarts": "^5.3.2",

"echarts-liquidfill": "^3.1.0",

"element-ui": "^2.15.8",

"vue": "^2.6.11",

"vue-router": "^3.2.0",

"vue-seamless-scroll": "^1.1.23", //页面滚动包

 "vuex": "^3.4.0"

第五步:配置路由

  • 在router文件夹下建立index.js文件,并且引入页面进行路由的配置
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },

]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router
  • 如果需要对store数据进行模块化,可在store文件夹下建立module文件夹,此文件夹下可以分别建立多个不同的vuex模块分开管理数据,比如建立departments.js
departments.js 文件代码演示

import Layout from '@/layout'
// 员工的路由规则
export default {
  path: '/departments', // 路由地址
  hidden: true,
  name: 'departments', // 给模块的一级路由加一个name属性,后面会用到
  component: Layout, // 布局组件
  children: [{
    // 二级路由的path什么都不写的时候,此时表示是二级路由的默认路由
    path: '', // 这里不写的话,表示二级路由的默认路由
    component: () => import('@/views/departments'), // 路由懒加载
    name: 'departments',
    hidden: true,
    // 路由信息,meta存储数据的地方,可以放任何内容
    meta: {
      title: '组织架构', // 左侧导航读取了这里的title
      icon: 'tree'
    }
  }]
}

---------------------------------------------------------------------------------------
router下的index.js的代码演示

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

/* Layout */
import Layout from '@/layout'

// 引入多个模块的规则
import departmentsRouter from './modules/departments'
import employeesRouter from './modules/employees'
import userRouter from './modules/user'

// 动态路由
export const asyncRoutes = [
  departmentsRouter,
  employeesRouter,
]

// 静态路由constantRoutes
export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: '首页', icon: 'dashboard' }
    }]
  },
  {
    path: '/import',
    component: Layout, // 以及布局布局组件
    hidden: true, // 不显示在左侧菜单中
    children: [{
      path: '', // 二级路由path什么都不写,表示二级默认路由
      component: () => import('@/views/import/index')
    }]
  },
  userRouter
  // 404 page must be placed at the end !!!

]

const createRouter = () => new Router({
  mode: 'history', // require service support
  base: 'rz',
  scrollBehavior: () => ({ y: 0 }),
  routes: [...constantRoutes] // 临时合并所有的路由
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter () {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router
  • 在main.js文件中引入router文件,挂载vue实例
import Vue from 'vue'
import App from './App.vue'
import router from './router'

new Vue({
  router,
  store,
  element,
  render: h => h(App)
}).$mount('#app')

第六步,配置vuex

  • 在store文件夹下建立index.js
index.js代码演示

import Vue from 'vue'
import Vuex from 'vuex' // 引入vuex

Vue.use(Vuex) //vue注册vuex

export default new Vuex.Store({ // 在vuex上挂载store实例配置项
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})
  • 如果需要对vuex数据进行模块化,可在store文件夹下建立module文件夹,此文件夹下可以分别建立多个不同的vuex模块分开管理数据,比如建立user.js
user.js代码演示

// 引入封装本地缓存的方法
import { getToken, setToken, removeToken, setTimeStamp } from '../../../utils/auth'
import { login } from '../../api/user'
//数据状态
const state = {
}
// 修改状态的数据
const mutations = {
}
// 异步请求方法
const actions = {
}
// getters计算属性
const getters = {
}
export default {
    namespaced: true,
    state,
    mutations,
    actions,
    getters
}

----------------------------------------------------------------------------------------
模块化后的store下的index.js代码演示

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    namespaced: true, // 为了解决不同模块命名冲突的问题
    user
  }
})
  • 在main.js文件中引入store文件,挂载vue实例
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false // 这句代码为false,它会阻止你显示显示生产模式的消息,这句代码为true,它会显示你生产模式的消息。所以,在开发环境下,保持默认设置false即可。

Vue.prototype.$echarts = echarts //定义一个全局的变量$echarts挂载到vue原型链上, 在其他vue文件通过this.$echarts就可以访问到echarts

new Vue({
  router,
  store,
  element,
  render: h => h(App)
}).$mount('#app')

第七步:配置网络请求信息axios

  • 在utils文件夹下封装axios模块,先建立request.js引入单独的axios模块
代码演示:

import axios from "axios"
import store from '@/store'
import router from '@/router'

const instance = axios.create({
    // 反向代理是解决不同环境下跨域的问题,设置基础值地址是统一设置url地址。基础地址的设置会触            
    发反向代理的跨域配置,所以要先设置好反向代理
    baseURL: process.env.VUE_APP_API_BASE_URL,
    timeout: 1000 * 60,
    headers: { 'content-type': 'application/x-www-form-urlencoded' }
});

// 添加请求拦截器
// instance 以...为例
// interceptors英文翻译拦截机
instance.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    // 在这个位置需要统一的去注入token
    if (store.getters.token) {
    // 只有在有token的情况下 才有必要去检查时间戳是否超时
    if (IsCheckTimeOut()) {
      // 如果它为true表示 过期了
      // token没用了 因为超时了
      store.dispatch('user/logout') // 登出操作
      // 跳转到登录页
      router.push('/login')
      return Promise.reject(new Error('token超时了'))
    }
    // 如果token存在 注入token
    config.headers['Authorization'] = `Bearer ${store.getters.token}`
  }
  return config // 必须返回配置
    
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    const { success, message, data } = response.data
    if (success) {
      return data
    } else {
      Message.error(message)
      return Promise.reject(new Error(message))
    }
}, function (error) {
    // 对响应错误做点什么
    if (error.response && error.response.data && error.response.data.code === 10002) {
      // 后端告诉我token超时了,被动处理token
      store.dispatch('user/logout') // 登出action 删除token
      router.push('/login')
    } else {
      // 提示错误信息
      Message.error(error.message)
    }
    // 返回执行错误,让当前的执行链跳出来
    return Promise.reject(error)
});


export default instance

第八步:在api请求封装模块中,引入axios并且进行请求方法的封装,比如在api下建立user.js

user.js文件模块展示

import request from '@/utils/request'
import qs from 'qs'

/**
 * 登录接口封装
 */
export function login (data) {
  // 返回一个promise对象
  return request({
    url: '/sys/login',
    method: 'POST',
    data:qs.stringify(data)
  })
}

===

至此项目搭建配置基本完成!

目录知识扩展:

api:放置接口文件

assets:放置图片的静态资源目录

components:放置公共组件

directives:放置负责管理所有的自定义指令

// 定义第一个自定义指令 v-imagerror
export const imagerror = {
  // 指令对象,钩子函数inserted,会在当前的dom元素插入到节点之后执行
  inserted (dom, options) {
    // dom表示当前指令作用的对象,此时就是作用在图片上
    // options 是指令中的变量的解释
    // 当图片有地址,但是地址没有加载成功的时候,会报错,会触发图片的一个事件,onerror
    dom.src = dom.src || options.value // 初始化时有值就赋值,无值使用默认值赋值
    dom.onerror = function () {
      // dom可以注册error事件
      // 当图片出现异常的时候,会将指令配置的默认图片设置为该图片的内容
      dom.src = options.value // 这里不能写死,活的指令多方调用
    }
  },
  // 该函数和inserted一样,也是一个钩子函数
  componentUpdated (dom, options) {
    // 这个函数会在当前指令作用的组件更新数据完毕之后执行
    // inserted只会执行一次,组件更新后就不会在执行inserted了
    // 组件初始化后一旦更新,就不会在进入inserted函数了,会进入当前这个componentUpdated 函数
    dom.src = dom.src || options.value
  }
}

filters:放置过滤的验证的方法都可以

// import parseTime, formatTime and set to filter

// 验证手机号
export function checkPhone (rule, value, callback) {
  if (!value) {
    return callback(new Error('手机号不能为空'))
  } else {
    const reg = /^1[3|4|5|7|8][0-9]\d{8}$/
    if (reg.test(value)) {
      callback()
    } else {
      return callback(new Error('请输入正确的手机号'))
    }
  }
}
export function checkPassword (rule, value, callback) {
  if (!value) {
    return callback(new Error('密码不能为空'))
  } else if (value.length < 6) {
    callback(new Error('请至少输入 6 个字符。请不要使用容易被猜到的密码'))
  } else {
    callback()
  }
}
// 手机号证验证
export function checkTel (value, callback) {
  var reg = /^1[3|4|5|7|8][0-9]\d{8}$/
  return reg.test(value)
}
// 身份证验证
export function checkiDNumber (value, callback) {
  var reg = /\d{17}[\d|x]|\d{15}/
  return reg.test(value)
}
// 身份证验证
export function checkEmails (value, callback) {
  var reg = /^[A-Za-zd]+([-_.][A-Za-zd]+)*@([A-Za-zd]+[-.])+[A-Za-zd]{2,5}$/
  return reg.test(value)
}
// 邮箱验证
export function checkEmail (rule, value, callback) {
  if (!value) {
    return callback(new Error('邮箱不能为空'))
  } else {
    var reg = /^[A-Za-zd]+([-_.][A-Za-zd]+)*@([A-Za-zd]+[-.])+[A-Za-zd]{2,5}$/
    if (reg.test(value)) {
      callback()
    } else {
      return callback(new Error('请输入正确的邮箱'))
    }
  }
}
// 英文验证
export function checkCode (value, callback) {
  var reg = /^[A-Za-z]+$/g
  return reg.test(value)
}
// qq验证
export function checkQq (value, callback) {
  var reg = /^[0-9]+$/g
  return reg.test(value)
}
// 银行卡号
export function formatBankNo (BankNo, callback) {
  var strBin = '10,18,30,35,37,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,60,62,65,68,69,84,87,88,94,95,98,99'
  return strBin
}
export function getStrleng (str, max) {
  var myLen = 0
  for (var i = 0; i < str.length && myLen <= max * 2; i++) {
    if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
      myLen++
    } else myLen += 2
  }
  return myLen
}
// 上传图片格式控制
export function updatedImg (file, obj, callback, func) {
  if (file.size < 10100000) {
    var fileName = file.name
    var suffix = fileName
      .substring(fileName.lastIndexOf('.') + 1)
      .toUpperCase()
    if (
      suffix === 'PDF' ||
      suffix === 'JPG' ||
      suffix === 'JPEG' ||
      suffix === 'PNG' ||
      suffix === 'GIF'
    ) {
      return true
    } else {
      var tipType = '文件类型不正确,请重新上传'
      callback(tipType)
      return false
    }
  } else {
    var tipSize = '文件大小超过5M,请重新上传'
    callback(tipSize)
    return false
  }
}
// 上传文档格式控制
export function updatedFile (file, obj, callback, func) {
  if (file.size < 10100000) {
    var fileName = file.name
    var suffix = fileName
      .substring(fileName.lastIndexOf('.') + 1)
      .toUpperCase()
    if (
      suffix === 'DOC' ||
      suffix === 'DOCX' ||
      suffix === 'XLS' ||
      suffix === 'XLSX' ||
      suffix === 'PDF' ||
      suffix === 'ZIP' ||
      suffix === 'RAR'
    ) {
      return true
    } else {
      var tipType = '文件类型不正确,请重新上传'
      callback(tipType)
      return false
    }
  } else {
    var tipSize = '文件大小超过5M,请重新上传'
    callback(tipSize)
    return false
  }
}
export function importFile (file, obj, callback, func) {
  if (file.size < 10100000) {
    var fileName = file.name
    var suffix = fileName
      .substring(fileName.lastIndexOf('.') + 1)
      .toUpperCase()
    if (
      suffix === 'XLS' ||
      suffix === 'XLSX'
    ) {
      return true
    } else {
      var tipType = '文件类型不正确,请重新上传'
      callback(tipType)
      return false
    }
  } else {
    var tipSize = '文件大小超过10M,请重新上传'
    callback(tipSize)
    return false
  }
}

lang:放置语音包

mixin:放置混入对象方法

import store from '@/store'
// 做一个混入对象
export default {
  // 混入对象是组件的选项对象
  methods: {
    // 检查权限,key是要检查的点
    checkPermission (key) {
      // 去用户的信息中的poins中有没有key,有key则认为 有权限,没有就不能点击
      // store.state.user.userInfo.roles.poins
      const { userInfo } = store.state.user
      if (userInfo.roles && userInfo.roles.points) {
        return userInfo.roles.points.some(item => item === key)
      }
      return false
    }
  }
}

router:放置路由

store:放置vuex数据

styles:放置公共的样式文件

utils:放置工具文件,放置axios的封装文件request,放置本地存储,cookie存储封装的方法

// auth.js封装存储cookie,获取cookle, 移除cookie的方法

import Cookies from 'js-cookie'

const TokenKey = 'hrsaas-ihrm-token' // 设定一个独一无二的key

export function getToken () {
  return Cookies.get(TokenKey)
}

export function setToken (token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken () {
  return Cookies.remove(TokenKey)
}

const timeKey = 'hrsaas-timestamp-key' // 设置一个独一无二的key

// 获取时间戳
export function getTimeStamp () {
  return Cookies.get(timeKey)
}
// 设置时间戳
export function setTimeStamp () {
  Cookies.set(timeKey, Date.now())
}

permission.js:权限管理文件,路由守卫

// 是控制页面登录权限的文件, 此处的代码没有经历构建过程会很难理解, 所以先将此处的代码进行注释,等我们构建权限功能时,再从0到1进行构建。

// 权限拦截在路由跳转,导航守卫
import router from '@/router'
import store from '@/store' // 引入store实例,这个和组件中的this.$store是一回事
import NProgress from 'nprogress' // 引入一份进度条插件
import 'nprogress/nprogress.css' // 引入进度条样式

// 前置守卫
// next是前置是守卫必须执行的钩子,next必须执行,不执行页面就死了
// next()放行
// next(false)跳转终止
// next(地址),跳转到某个地址
// 用户资料有个硬性要求,必须要有token才可以获取,所以可以在确定有token的位置去获取用户资料,跳到主页了,有token不用处理,在else放行时处理
window.rrr = router
router.beforeEach(async (to, from, next) => {
  NProgress.start()
  if (store.getters.token) {
    // 如果有token,判断是否是登录页
    if (to.path === '/login') {
      next('/') // 跳转主页
    } else {
      // 只有在放行的时候才去获取资料
      // 是每次都获取资料吗,如果当前vuex中有用户的资料的id,表示已经有资料了,不需要在获取了,如果没有id才需要 获取
      if (!store.getters.userId) {
        // async函数return的内容,用await就可以接收到
        const { roles } = await store.dispatch('user/getUserInfo')
        // console.log(roles)
        // 如果后续需要根据用户资料获取数据的话,这里必须改成头部
        // 这里得到的routes就是筛选得到的动态路由
        const routes = await store.dispatch('permission/filterRoutes', roles.menus) // 筛选得到当前用户可用的动态路由
        console.log(routes)
        router.addRoutes([...routes], { path: '*', redirect: '/404', hidden: true })
        next(to.path)
      }
      next() // 有token但不是登录页,就放行
    }
  } else {
    // 如果没有token
    const whiteList = ['/login', '/404']
    if (whiteList.indexOf(to.path) !== -1) {
      // 表示要去的地址在白名单
      next()
    } else {
      next('/login')
    }
  }
  // 手动强制关闭进度条
  NProgress.done()
})
// 后置守卫
router.afterEach(() => {
  NProgress.done()
})

setting.js:

// settings.js`**则是对于一些项目信息的配置,里面有三个属性 **`title`**(项目名称),**`fixedHeader`**(固定头部),**`sidebarLogo`**(显示左侧菜单logo)

// **`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: true
}

Logo

前往低代码交流专区

更多推荐