若依管理系统前端实践

这篇具有很好参考价值的文章主要介绍了若依管理系统前端实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

若依管理系统是一套基于若依框架开发的后台管理系统,它是一个前后端分离的项目,前端使用vue, Element, 后端使用Spring Boot & Security。这篇随笔中将记录一下自己在使用过程中前端使用上的一些收获和问题。

目录

  • 1. 路由控制
    • 1.1 简述
    • 1.2 token的检验
    • 1.3 获取角色权限
    • 1.4 生成路由
  • 2. 管理系统的几个组件
    • 2.1 生成菜单
    • 2.2 TagsView
  • 3. 实际实践中自己做出的一些修改
    • 3.1 路由控制
      • 3.1.1 由后端存储路由并分配权限
      • 3.1.2 直接将路由中的角色替换为权限
    • 3.2 权限管理
  • 4. 若依管理路由控制的其他应用
    • 4.1 router
    • 4.2 store
    • 4.3 permissionjs
    • 4.4 登录登出
    • 4.5 操作权限的控制

1. 路由控制

1.1 简述

首先是路由控制。若依管理系统前端路由控制的核心在src/permission.js文件中。其主要逻辑在router.beforeEach中,如下面的流程图所示:
若依管理系统前端实践

其核心代码如下:

  router.beforeEach(async(to, from, next) => {
    NProgress.start() // 开启进度条

    document.title = getPageTitle(to.meta.title) // 设置页面标题

    const hasToken = getToken()

    // 判断是否有token,如果有则获取角色与权限
    if (hasToken) {
      if (to.path === '/login') {
        // 如果已经有token,且访问的是登录页面,则跳转到首页
        next({ path: '/' })
        NProgress.done()
      } else {
        // 从store中获取角色与权限
        const hasRoles = store.getters.roles && store.getters.roles.length > 0
        if (hasRoles) {
          next()
        } else {
          try {
            // 获取用户信息
            const { roles } = await store.dispatch('user/getInfo')

            // 生成路由
            const accessRoutes = await store.dispatch('permission/generateRoutesByRequiredList', roles)

            router.addRoutes(accessRoutes)

            next({ ...to, replace: true })
          } catch (error) {
            // 遇到错误则直接重置token,跳转到登录页面
            await store.dispatch('user/resetToken')
            Message.error(error || 'Has Error')
            next(`/login?redirect=${to.path}`)
            NProgress.done()
          }
        }
      }
    } else {
      // 没有token则直接跳转到登录页面
      if (whiteList.indexOf(to.path) !== -1) {
        // 无需登录的页面则写进whiteList中,可以直接访问
        next()
      } else {
        next(`/login?redirect=${to.path}`)
        NProgress.done()
      }
    }
  })
  router.afterEach(() => {
    NProgress.done() // 结束进度条
    /* 由https://github.com/PanJiaChen/vue-element-admin/pull/2939可知
    * afterEach并不总是会被调用,例如在首页手动跳转到登录页面,上面代码在登录的情况下会跳转到首页,则afterEach不会被调用
    * 所以即使在这里写了NProgress.done(),在某些情况下也要在beforeEach中单独手动调用NProgress.done()
    */
  })

在这其中使用了NProgress来显示页面加载进度条,NProgress是一个轻量级的进度条插件,只需要在路由跳转前调用NProgress.start(),在路由跳转后调用NProgress.done()即可。

1.2 token的检验

在每次路由跳转前先判断了是否有token,如果有token则获取用户角色与权限并进一步生成路由,如果没有token则跳转到登录页面。这里token的检验进入到src/utils/auth.js文件中查看:

import Cookies from 'js-cookie'
const TokenKey = 'Token'
export function getToken() {
  return Cookies.get(TokenKey)
}
export function setToken(token) {
  return Cookies.set(TokenKey, token)
}
export function removeToken() {
  return Cookies.remove(TokenKey)
}

可以看到,token的存储、获取与删除是使用了js-cookie这个插件,这个插件可以方便地操作cookie。在这里,token的存储是使用了cookie,也可以使用localStorage或者sessionStorage。

1.3 获取角色权限

判断是否有角色权限的语句为const hasRoles = store.getters.roles && store.getters.roles.length ,这里的store.getters.roles的实际具体位置在src/store/modules/user.js文件中,而获取角色的store.dispatch('user/getInfo')也同样是在src/store/modules/user.js文件中,这是用了vuex的模块化管理,将不同的模块分别放在不同的文件中,这样可以使得代码更加清晰,方便管理。在src/store/modules/user.js文件中,可以看到如下代码。
首先是state,用于存储用户信息:

const state = {
  token: getToken(),
  name: '',
  avatar: '',
  introduction: '',
  roles: []
}

然后是mutations,用于修改state中的数据,使用时需要使用store.commit('SET_ROLES', roles)的形式:

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_INTRODUCTION: (state, introduction) => {
    state.introduction = introduction
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles
  }
}

最后是actions,用于异步修改state中的数据,使用时需要使用store.dispatch('user/getInfo')的形式,这里主要列一下获取用户信息的代码:

const actions = {
  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
    //   getInfo(state.token).then(response => {
      const data = {
        roles: ['admin'],
        introduction: 'I am a super administrator',
        avatar: 'xxx',
        name: 'Super Admin' }

      if (!data) {
        reject('Verification failed, please Login again.')
      }

      const { avatar, roles, introduction } = data

      // roles must be a non-empty array
      if (!roles || roles.length <= 0) {
        reject('getInfo: roles must be a non-null array!')
      }
      commit('SET_AVATAR', avatar)
      commit('SET_ROLES', roles)
      commit('SET_INTRODUCTION', introduction)
      resolve(data)
    }).catch(error => {
      // eslint-disable-next-line no-undef
      // reject(error)
      console.log(error)
    })
    // })
  },

}

上述代码中的roles是写死的['admin'],实际使用时需要根据后端返回的数据进行修改。

1.4 生成路由

在获取角色权限后,需要根据角色权限生成路由。生成路由的代码在src/store/modules/permission.js中:

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

调用了filterAsyncRoutes函数进行从路由列表中根据角色权限筛选出符合条件的路由,然后将筛选出的路由添加到路由中,最后返回筛选出的路由。

function filterAsyncRoutes(routes, roles) {
  const res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasRole(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}

判断路由是否符合条件的函数为hasRole,主要通过路由文件中的meta中的roles来判断:

function hasRole(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

需要让hasRole正确运行则需要在router/index.js中为每个路由设置好对应的角色权限:

{
  path: '/permission',
  component: Layout,
  redirect: '/permission/page',
  alwaysShow: true, // will always show the root menu
  name: 'Permission',
  meta: {
    title: 'Permission',
    icon: 'lock',
    roles: ['admin', 'editor'] // 可以在根导航中设置角色
  },
  children: [
    {
      path: 'page',
      component: () => import('@/views/permission/page'),
      name: 'PagePermission',
      meta: {
        title: 'Page Permission',
        roles: ['admin'] // 或者在子导航中设置角色
      }
    },
    {
      path: 'directive',
      component: () => import('@/views/permission/directive'),
      name: 'DirectivePermission',
      meta: {
        title: 'Directive Permission'
        // 如果不设置角色,则表示:此页不需要权限
      }
    },
    {
      path: 'role',
      component: () => import('@/views/permission/role'),
      name: 'RolePermission',
      meta: {
        title: 'Role Permission',
        roles: ['admin']
      }
    }
  ]
}

在router/index.js中有两组路由,一组是constantRoutes,一组是asyncRoutesconstantRoutes是不需要权限的路由,asyncRoutes是需要权限的路由。后续生成路由时则是在asyncRoutes中进行筛选的。

2. 管理系统的几个组件

这里主要说一下左侧菜单栏和顶部TagsView。

2.1 生成菜单

在生成路由后,需要根据路由生成菜单。菜单的生成是在src/layout/components/Sidebar/index.vue中进行的,代码如下:

<el-menu
  :default-active="activeMenu"
  :collapse="isCollapse"
  :background-color="variables.menuBg"
  :text-color="variables.menuText"
  :unique-opened="false"
  :active-text-color="variables.menuActiveText"
  :collapse-transition="false"
  mode="vertical"
>
  <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>

这里的permission_routes是用了vuexmapGetters来获取的,代码如下:

import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['permission_routes'])
  }
}

mapGetters的作用是将store中的getters映射到组件的computed中,这样就可以在组件中直接使用this.permission_routes来获取store中的permission_routes

2.2 TagsView

TagsView是用来显示当前打开的页面的,可以缓存之前打开过的页面,效果如下如所示:
若依管理系统前端实践

访问过以及缓存的路由存储在store/modules/tagsView.js中:

const state = {
  visitedViews: [],
  cachedViews: []
}

上述代码中的visitedViews用来存储访问过的路由,cachedViews用来存储缓存的路由。如果在路由的meta中设置了noCachetrue,则不会缓存该路由。
tagsView.js中的其他代码主要用于添加、删除、清空路由等操作,如addViewdelViewdelOthersViews等,这里不再赘述。
TagsView对应的组件在src/layout/components/TagsView/index.vue中,代码如下:

<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper"@scroll="handleScroll">
  <router-link
    v-for="tag in visitedViews"
    ref="tag"
    :key="tag.path"
    :class="isActive(tag)?'active':''"
    :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
    tag="span"
    class="tags-view-item"
    @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
    @contextmenu.prevent.native="openMenu(tag,$event)"
  >
    {{ tag.title }}
    <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
  </router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}"class="contextmenu">
  <li @click="refreshSelectedTag(selectedTag)">Refresh</li>
  <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
  <li @click="closeOthersTags">Close Others</li>
  <li @click="closeAllTags(selectedTag)">Close All</li>
</ul>
</div>

获取visitedViews是在computed中获取的:

  computed: {
    visitedViews() {
      return this.$store.state.tagsView.visitedViews
    },
    routes() {
      return this.$store.state.permission.routes
    }
  }

3. 实际实践中自己做出的一些修改

自己在实践时的代码放在了github上:
https://github.com/lxmghct/UserRolePermission

3.1 路由控制

可以看到若依管理的前端是通过角色来控制路由生成的,而在我实践的项目中,将权限分为了三级:模块权限、页面权限、操作权限,路由则是由页面权限直接控制的。所以需要进行一些修改。在实际使用中尝试了以下两种修改方式,

3.1.1 由后端存储路由并分配权限

我尝试的第一种是直接将对应页面的路由存储在数据库的权限相应的字段中,当用户登录时,后端将用户所能访问的路由全部返回给前端,前端据此生成对应的路由。在router/index.js中则无需设置路由对应的角色或权限。
在store/modules/user.js中,用户登录时将后端返回的路由component存进localStorage或者sessionStorage中,这一步是必要的,否则在刷新页面时,路由会丢失,导致重新跳转到登录页面。

const actions = {
  // user login
  login({ commit }, userInfo) {
    const params = new URLSearchParams()
    params.append('userName', userInfo.username)
    params.append('password', md5(userInfo.password))
    return new Promise((resolve, reject) => {
      login(params).then(response => {
        commit('SET_TOKEN', 'admin-token')
        // 获取后端返回的路由
        // 将路由的每一级都存储在一个set中,用于生成路由
        const componentSet = new Set()
        if (response.data.component && response.data.component.length > 0) {
          response.data.component.forEach(item => {
            const temp = item.replace(/^\//, '').replace(/\/$/, '').replace(/^system\//, '')
            let index = temp.indexOf('/')
            while (index > 0) {
              componentSet.add(temp.substring(0, index))
              index = temp.indexOf('/', index + 1)
            }
            componentSet.add(temp)
          })
        }
        componentSet.add('')
        localStorage.setItem('component', Array.from(componentSet))
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
}

然后再getInfo中,拿到component存储在state中,并作为返回值返回给src/permissio.js对应的语句,并传递给生成路由的函数。这里我另外定义了一个generateRoutesByRequiredList,代码如下:

const actions = {
  generateRoutesByRequiredList({ commit }, routeList) {
    return new Promise(resolve => {
      const accessedRoutes = filterAsyncRoutesInRequiredList(asyncRoutes, routeList, '')
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

function filterAsyncRoutesInRequiredList(routes, routeList, base) {
  const res = []
  // 过滤掉不在requiredList中的路由
  routes.forEach(route => {
    const tmp = { ...route }
    // 这里对path格式统一处理去掉开头和结尾的'/',便于判断
    let path = tmp.path.replace(/^\//, '').replace(/\/$/, '')
    path = base === '' ? path : base + '/' + path
    path = path.replace(/^\//, '').replace(/^\//, '')
    if (routeList.includes(path)) {
      if (tmp.children) {
        // 考虑到子路由的情况,递归调用
        tmp.children = filterAsyncRoutesInRequiredList(tmp.children, routeList, base + '/' + path)
      }
      res.push(tmp)
    }
  })

  return res
}

这种策略在项目结构极为简单的情况下勉强可以使用。在一般情况下实际使用中有很多弊端。

  • 首先是前端的路由名称与后端数据库要同步,这对新增路由和修改路由名称都会带来很大的麻烦。
  • 其次是由于每个页面都单独对应了一个路由,在管理页面权限的时候,就不得不给每一个页面都加上一个权限,会使权限控制出现大量不必要的重复功能的权限。虽然可以考虑在数据库存储时每个权限可以存储多个路由,但实际上已经将问题复杂化了。

3.1.2 直接将路由中的角色替换为权限

直接将若依管理系统前端路由部分涉及角色的地方替换为权限,或者另外增加一个变量去存储权限。这种方法显然实现起来更容易且更可靠。一开始我尝试采用3.1.1中的方法确实有些多此一举了。

src/store/modules/user.js中在登录时将后端返回的permission存储在sessionStorage或localStorage中,这一步是必要的,否则在刷新页面时,路由会丢失,导致重新跳转到登录页面。这里permission中存储的是当前用户所拥有的的所有权限代码的列表。

const actions = {
  // user login
  login({ commit }, userInfo) {
    const params = new URLSearchParams()
    params.append('username', userInfo.username)
    params.append('password', userInfo.password)
    return new Promise((resolve, reject) => {
      login(params).then(response => {
        commit('SET_TOKEN', 'admin-token')
        localStorage.setItem('userId', response.data.user.id)
        sessionStorage.setItem('permission', response.data.user.permissions || [])
        sessionStorage.setItem('loginInformation', JSON.stringify(response.data))
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      const permissions = sessionStorage.getItem('permission')
      const data = {
        roles: permissions ? permissions.split(',') : [], // 这里为图方便就直接将权限赋值给角色了,省的多建一个变量
        introduction: 'I am a super administrator',
        avatar: 'xxxx',
        name: 'Super Admin' }

      if (!data) {
        reject('Verification failed, please Login again.')
      }

      const { avatar, roles, introduction } = data

      // roles must be a non-empty array
      if (!roles || roles.length <= 0) {
        reject('getInfo: roles must be a non-null array!')
      }
      commit('SET_AVATAR', avatar)
      commit('SET_ROLES', roles)
      console.log(state.roles)
      commit('SET_INTRODUCTION', introduction)
      resolve(data)
    }).catch(error => {
      // eslint-disable-next-line no-undef
      // reject(error)
      console.log(error)
    })
    // })
  }
}

在生成路由的时候,将路由筛选的条件替换为permission,在store/modules/permission.js中修改如下:

function hasPermission(permissions, route) {
  if (route.meta && route.meta.permissions) {
    return permissions.some(permission => route.meta.permissions.includes(permission))
  } else {
    return true
  }
}

function filterAsyncRoutes(routes, permissions) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(permissions, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, permissions)
      }
      res.push(tmp)
    }
  })

  return res
}

const actions = {
  generateRoutes({ commit }, permissions) {
    return new Promise(resolve => {
      const accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

相应的在src/router/index.js中将meta中的roles换为permissions,这里就不多赘述。

3.2 权限管理

由于在实际项目中将权限分成了模块权限、页面权限、操作权限三级,所以权限管理的时候就把所有权限用树形结构展示出来,这样就可以很方便的管理权限了。这里就不多赘述了。

4. 若依管理路由控制的其他应用

从这次若依管理系统的路由控制的实现过程中,我也学到了这样一种动态生成路由的方式。事实上这种方式也可以应用于除了管理系统以外的一般的前端项目,只需将菜单等其他部分忽略,将路由控制的那部分提取出来即可。
这部分代码也放在了github上,
https://github.com/lxmghct/UserRolePermission
这里我没有去使用vuexjs-cookie

4.1 router

首先在router/index.js中,仍然保留constantRoutesasyncRoutes两个变量,同时也保留createRouterresetRouter两个函数。由于不需要生成菜单,所以meta这一属性也可以去掉了,权限的判断直接在每个路由加上permissions属性即可。

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

Vue.use(Router)

export const constantRoutes = [
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login')
  }
]

export const asyncRoutes = [
  {
    path: '/home',
    name: 'Home',
    component: () => import('@/views/Home'),
    permissions: ['TEST_MAIN']
  },
  {
    path: '/page1',
    name: 'Page1',
    component: () => import('@/views/Page1'),
    permissions: ['TEST_PAGE1']
  },
  {
    path: '/page2',
    name: 'Page2',
    component: () => import('@/views/Page2'),
    permissions: ['TEST_MAIN']
  }
]

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

export function resetRouter () {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

4.2 store

在store中只要用到user.js,所以只保留这部分在store/user.js中。

import { resetRouter } from '@/router'

export const userStore = {
  roles: [],
  permissions: []
}

export const UserUtils = {
  resetUserStore () {
    userStore.roles = []
    userStore.permissions = []
    resetRouter()
  }
}

由于没使用vuex,所以也无需创建getters.js。

4.3 permission.js

我把路由守卫以及路由生成的代码都统一放在了src/permission.js中。没使用vuex,不过直接import对应的变量也可以获取到需要的值。没使用js-cookie,所以直接使用sessionStorage来存储用户信息。之所以要另外再弄个user.js将sessionStorage的操作封装起来,我觉得可能是有两个原因,一个是用于判断用户是否已经生成过了路由,还有一个作用就是将用户的权限信息存在内存中,即使用户修改了sessionStorage中的值,也不会影响到用户的权限信息。当然如果想要修改也是能做到的,因为user.js中的内容会在刷新后消失,所以修改sessionStorage中的值后,刷新页面就会重新生成路由了。

import router, { asyncRoutes } from './router'
import { userStore, UserUtils } from './store/user'

function hasPermission (permissions, route) {
  if (route.permissions) {
    return permissions.some(permission => route.permissions.includes(permission))
  } else {
    return true
  }
}

function filterAsyncRoutes (routes, permissions) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(permissions, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, permissions)
      }
      res.push(tmp)
    }
  })

  return res
}

const whiteList = ['/login'] // no redirect whitelist

router.beforeEach(async (to, from, next) => {
  const loginInfo = sessionStorage.getItem('loginInformation')
  if (loginInfo) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/home' })
    } else {
      const hasPermission = userStore.permissions && userStore.permissions.length > 0
      if (hasPermission) {
        if (router.getRoutes().map(item => item.path).includes(to.path)) {
          next()
        } else {
          next({ path: '/login' })
        }
      } else {
        try {
          userStore.permissions = JSON.parse(loginInfo).user.permissions
          // generate accessible routes map
          const accessRoutes = filterAsyncRoutes(asyncRoutes, userStore.permissions)
          if (accessRoutes.length === 0 || userStore.permissions.length === 0) {
            throw new Error('No permission')
          }
          // dynamically add accessible routes
          accessRoutes.forEach(item => { router.addRoute(item) })

          // set the replace: true, so the navigation will not leave a history record
          next({ ...to, replace: true })
        } catch (error) {
          sessionStorage.removeItem('loginInformation')
          UserUtils.resetUserStore()
          next(`/login?redirect=${to.path}`)
        }
      }
    }
  } else {
    /* not logged in */
    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
    }
  }
})

4.4 登录登出

登录时将用户信息存储在sessionStorage中, 登出时将sessionStorage中的用户信息清除。

sessionStorage.setItem('loginInformation', JSON.stringify(res.data));
// sessionStorage.removeItem('loginInformation');

4.5 操作权限的控制

操作权限最主要还是要通过后端去控制,不过前端也可以根据权限去选择隐藏或禁用一些不允许使用的按钮,通过v-if:disabled之类的方式来控制。
可以在main.js中进行相应的配置使权限写起来更方便一些。
首先是在4.2中的user.js中添加一个hasPermission方法,用于判断用户是否有权限。

export const hasPermission = (permissions) => {
  // 为了写起来方便,这里同时支持传入数组和字符串
  if (Array.isArray(permissions)) { 
    return permissions.some(permission => userStore.permissions.includes(permission))
  } else {
    return userStore.permissions.includes(permissions)
  }
}

然后在main.js中全局注册判断权限的方法。

import { hasPermission } from '@/store/user'
Vue.prototype.$permission = { has: hasPermission }

这样在组件中就可以通过this.$permission.has('TEST_MAIN')this.$permission.has(['TEST_MAIN', 'TEST_SUB'])来判断用户是否有权限了。文章来源地址https://www.toymoban.com/news/detail-404554.html

<el-button v-if="$permission.has('TEST_MAIN')" type="primary" @click="handleAdd">新增</el-button>

到了这里,关于若依管理系统前端实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 若依框架后台管理系统-忘记后台管理密码-忘记密码重置方法

    1. 无盐老版 1.1、生成密码密文 1.2、替换数据库中密码 2. 加盐新版 (今天 2022-03-16) 2.1、生成密码密文 2.2、替换数据库中密码 补充说明 参考资料 管理后台忘记密码两步解决: 找到工具类: com.ruoyi.common.utils.SecurityUtils 添加 main 方法:打印出密码密文 大家好,我是笨笨,笨

    2024年02月15日
    浏览(43)
  • 若依管理系统搭建教程,ruoyi-vue环境搭建

    启动后端 修改数据库连接,编辑 resources 目录下的 application-druid.yml 修改服务器配置, 编辑resources目录下的application.yml  开发环境配置 打包工程文件 在 ruoyi 项目的 bin 目录下执行 package.bat 打包Web工程,生成war/jar包文件。 然后会在项目下生成 target 文件夹包含 war 或 jar 提示

    2024年01月22日
    浏览(56)
  • 开源项目学习:若依RuoYi-Vue后台管理系统【环境搭建】

    第一章 环境搭建 第二章 项目运行 第三章 阅读源码:例子-新增用户接口 第四章 基于ruoyi-vue开发新项目 本文尽量贴近零基础入门,献给初入门的学弟学妹们! 文章基本流程:环境配置→运行项目→阅读源码 安装环境时最好修改安装路径! Java开发必备! JDK下载:http://www

    2023年04月19日
    浏览(56)
  • 若依ruoyi——手把手教你制作自己的管理系统【二、修改样式】

    阿里图标一( ̄︶ ̄*)) 图片白嫖一((* ̄3 ̄)╭ ********* 专栏略长 ==== 爆肝万字 ==== 细节狂魔 ==== 请准备好一键三连 ********* 运行成功后: idea后台正常先挂着 我习惯用VScode操作 当然如果有两台机子 一个挂后台一个改前端就更好了 只需修改 vue.config.js 配置文件即可 eg:按 Win+R 打

    2024年02月03日
    浏览(50)
  • 【docker快速部署微服务若依管理系统(RuoYi-Cloud)】

    工作原因,需要一个比较完整的开源项目测试本公司产品。偶然发现RuoYi-Cloud非常适合,它有足够多的中间件,而且官方提供docker安装,但我本人在安装过程中遇到了很多坑,在这里记录一下防止下次会再次遇到。 https://gitee.com/y_project/RuoYi-Cloud 内存至少16G,处理器至少4核 内

    2024年02月09日
    浏览(55)
  • 若依管理系统后端将 Mybatis 升级为 Mybatis-Plus

    若依管理系统是一个非常完善的管理系统模板,里面含有代码生成的方法,可以帮助用户快速进行开发,但是项目使用的是 mybatis ,对于熟悉使用 mybatis-plus 进行开发的小伙伴们不是很便捷,本文主要讲解如何在不影响系统现有功能的基础上,将 mybatis 升级为 mybatis-plus ,以帮

    2024年02月14日
    浏览(37)
  • 若依管理系统RuoYi-Vue(前后端分离版)项目启动教程

    RuoYi-Vue  是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Spring Security、MyBatis、Jwt、Vue),内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置;支持集群,支持多数据源,支持分布式事务

    2024年02月06日
    浏览(61)
  • 若依ruoyi——手把手教你制作自己的管理系统【三、代码生成】

    增删改查导一( ̄︶ ̄*)) 按钮换个色一((* ̄3 ̄)╭ ********* 专栏略长 ==== 爆肝万字 ==== 细节狂魔 ==== 请准备好一键三连 ********* 修改后的页面: 干干净净贼舒服一Ψ( ̄∀ ̄)Ψ——Ψ( ̄∀ ̄)Ψ一 接下来我们要达到的效果如下(自定义菜单 里面有列表数据回显+增删改查) 一、修改

    2023年04月25日
    浏览(48)
  • 基于若依的ruoyi-nbcio流程管理系统自定义业务撤回功能的修复

    更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码: https://gitee.com/nbacheng/nbcio-boot 前端代码:https://gitee.com/nbacheng/nbcio-vue.git 在线演示(包括H

    2024年01月18日
    浏览(51)
  • OSS对象存储后端实现+Vue实现图片上传【基于若依管理系统开发】

    Bucket(存储空间) :用于存储对象的容器,所有对象都属于某个存储空间中,一般是一个项目创建一个Bucket来专门存储该项目的文件 Object(对象) :可以理解为文件,对象在Bucket内部由唯一的Key来标识 Region(地域) :选择数据所存放的物理地址,如北京 Endpoint(访问域名

    2024年02月16日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包