Динамически загружать компоненты Vue в модуль управления разрешениями

Spring Boot задняя часть внешний интерфейс Spring

Когда текущий бэкенд отделен, обработка проблем с разрешениями немного отличается от нашей традиционной обработки. Автор как раз несколько дней назад руководил модулем управления правами проекта.Сейчас модуль управления правами завершен.Я хочу через 5-6 статей представить возникшие проблемы в проекте и свои решения.Надеюсь на это серия может дать своим друзьям некоторую помощь. Эта серия статей не является практическим руководством. В основном она знакомит с основными идеями и объясняет основной код. Партнеры по полному коду могут пометить и клонировать их на GitHub для изучения. Кроме того, изначальный план состоял в том, чтобы запустить проект и выложить в интернет для просмотра друзьям, но до покупки сервера в целях экономии памяти было всего 512М, и два приложения запустить не удалось (есть уже одноПроект с открытым исходным кодом V Tribeработает), поэтому вы можете только взглянуть на скриншоты ниже. На GitHub есть руководства по развертыванию, и вы также можете увидеть полный эффект при локальном развертывании.


адрес проекта:github.com/lenve/vhr

Если в предыдущих статьях мы в основном решили проблему на стороне сервера и инкапсулировали фронтенд-запрос, то в этой статье мы в основном говорим о входе в систему и динамической загрузке компонентов.

Эта статья является пятой статьей в этой серии.Рекомендуется сначала прочитать предыдущие статьи, чтобы лучше понять эту статью:

1.SpringBoot+Vue разделяет переднюю и заднюю части и использует SpringSecurity для идеального решения проблем с разрешениями (1)
2.SpringBoot+Vue разделяет переднюю и заднюю части и использует SpringSecurity для идеального решения проблем с разрешениями (2)
3.Соление пароля в SpringSecurity и обработка исключений в SpringBoot
4.Инкапсуляция запросов axios и унифицированная обработка исключений

Сохранить статус входа

После того, как пользователь успешно войдет в систему, информацию для входа в систему текущего пользователя необходимо сохранить локально для последующего использования. Конкретная реализация выглядит следующим образом:

Войти успешно сохранить данные

После успешного выполнения операции входа данные отправляются в хранилище через операцию фиксации.Основной код выглядит следующим образом:

this.postRequest('/login', {
    username: this.loginForm.username,
    password: this.loginForm.password
}).then(resp=> {
    if (resp && resp.status == 200) {
    var data = resp.data;
    _this.$store.commit('login', data.msg);
    var path = _this.$route.query.redirect;
    _this.$router.replace({path: path == '/' || path == undefined ? '/home' : path});
    }
});

store

Основной код магазина выглядит следующим образом:

export default new Vuex.Store({
  state: {
    user: {
      name: window.localStorage.getItem('user' || '[]') == null ? '未登录' : JSON.parse(window.localStorage.getItem('user' || '[]')).name,
      userface: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).userface
    }
  },
  mutations: {
    login(state, user){
      state.user = user;
      window.localStorage.setItem('user', JSON.stringify(user));
    },
    logout(state){
      window.localStorage.removeItem('user');
    }
  }
});

Чтобы уменьшить проблемы, данные после успешного входа пользователя в систему будут сохранены в localStorage (для предотвращения потери данных после того, как пользователь нажмет F5 для обновления), сохранены в виде строки и преобразованы в json при извлечении. Когда пользователь выходит и входит в систему, данные в localStorage очищаются.

Компоненты загружаются динамически

В модуле управления правами это ядро ​​внешнего интерфейса.

основная идея

После успешного входа пользователя перед входом на домашнюю страницу на сервер отправляется запрос на получение текущей информации о меню и информации о компонентах.Сервер возвращает строку json в соответствии с ролью текущего пользователя и ресурсами, соответствующими роль в следующем формате:

[
    {
        "id": 2,
        "path": "/home",
        "component": "Home",
        "name": "员工资料",
        "iconCls": "fa fa-user-circle-o",
        "children": [
            {
                "id": null,
                "path": "/emp/basic",
                "component": "EmpBasic",
                "name": "基本资料",
                "iconCls": null,
                "children": [],
                "meta": {
                    "keepAlive": false,
                    "requireAuth": true
                }
            },
            {
                "id": null,
                "path": "/emp/adv",
                "component": "EmpAdv",
                "name": "高级资料",
                "iconCls": null,
                "children": [],
                "meta": {
                    "keepAlive": false,
                    "requireAuth": true
                }
            }
        ],
        "meta": {
            "keepAlive": false,
            "requireAuth": true
        }
    }
]

После того, как интерфейс получает эту строку, он делает две вещи: 1. Динамически добавляет json к текущему маршруту 2. Сохраняет данные в хранилище, а затем каждая страница отображает меню в соответствии с данными в хранилище.

Основная идея не сложна, давайте посмотрим на этапы реализации.

Время запроса данных

Это очень важно.

Некоторые друзья могут сказать, насколько это сложно.Можно ли сделать запрос после успешного входа в систему? Да, после успешного входа есть возможность запросить ресурс меню.После получения запроса мы сохраним его в магазине для следующего использования, но будет другая проблема.Если пользователь авторизуется успешно, нажмите на подстранице, войдите на подстраницу, а затем нажмите F5 для обновления, на этот раз это GG, потому что данные в магазине исчезают после обновления F5, и мы запрашиваем ресурсы меню только один раз при успешном входе в систему. Есть два пути решения этой проблемы: 1. Сохранять ресурсы меню не в хранилище, а в localStorage, чтобы данные оставались там даже после обновления F5 2. Непосредственно в смонтированном методе каждой страницы переходим на Загрузите ресурс меню один раз.

Так как ресурс меню очень чувствителен, то лучше не сохранять его локально, поэтому от схемы 1 отказываемся, но нагрузка по схеме 2 немного велика, поэтому я использую способ ее упрощения, и способ сделать это пользоваться навигацией в стороже маршрута.

навигационная охрана маршрута

Моя конкретная реализация такова: сначала создайте массив маршрутов в хранилище, который является пустым массивом, а затем включите глобальную защиту маршрутизации следующим образом:

router.beforeEach((to, from, next)=> {
    if (to.name == 'Login') {
      next();
      return;
    }
    var name = store.state.user.name;
    if (name == '未登录') {
      if (to.meta.requireAuth || to.name == null) {
        next({path: '/', query: {redirect: to.path}})
      } else {
        next();
      }
    } else {
      initMenu(router, store);
      next();
    }
  }
)

Код здесь очень короткий, позвольте мне дать простое объяснение: 1. Если страница, на которую вы хотите перейти, является страницей входа в систему, это ничего не говорит, просто перейдите напрямую.

2. Если это не страница входа, я сначала получаю текущий статус входа в магазин.Если я не вошел в систему, я буду использовать атрибут requireAuth метаатрибута в маршруте, чтобы определить, нужна ли страница, на которую я хочу перейти. для входа в систему. Если необходимо войти в систему, я вернусь на страницу входа в систему и передам путь к странице, на которую вы хотите перейти, в качестве параметра на страницу входа, чтобы вы могли перейти на целевую страницу. после успешного входа в систему. Если вам не нужно входить в систему, вы можете перейти напрямую (на самом деле, только страница входа в этом проекте не требует входа в систему); если вы уже вошли в систему, сначала инициализируйте меню, а потом прыгать.

Меню инициализации работает следующим образом:

export const initMenu = (router, store)=> {
  if (store.state.routes.length > 0) {
    return;
  }
  getRequest("/config/sysmenu").then(resp=> {
    if (resp && resp.status == 200) {
      var fmtRoutes = formatRoutes(resp.data);
      router.addRoutes(fmtRoutes);
      store.commit('initMenu', fmtRoutes);
    }
  })
}
export const formatRoutes = (routes)=> {
  let fmRoutes = [];
  routes.forEach(router=> {
    let {
      path,
      component,
      name,
      meta,
      iconCls,
      children
    } = router;
    if (children && children instanceof Array) {
      children = formatRoutes(children);
    }
    let fmRouter = {
      path: path,
      component(resolve){
        if (component.startsWith("Home")) {
          require(['../components/' + component + '.vue'], resolve)
        } else if (component.startsWith("Emp")) {
          require(['../components/emp/' + component + '.vue'], resolve)
        } else if (component.startsWith("Per")) {
          require(['../components/personnel/' + component + '.vue'], resolve)
        } else if (component.startsWith("Sal")) {
          require(['../components/salary/' + component + '.vue'], resolve)
        } else if (component.startsWith("Sta")) {
          require(['../components/statistics/' + component + '.vue'], resolve)
        } else if (component.startsWith("Sys")) {
          require(['../components/system/' + component + '.vue'], resolve)
        }
      },
      name: name,
      iconCls: iconCls,
      meta: meta,
      children: children
    };
    fmRoutes.push(fmRouter);
  })
  return fmRoutes;
}

В меню инициализации сначала определите, существуют ли данные в хранилище.Если они есть, значит, этот переход является обычным переходом, а не тем, что пользователь нажимает F5 или напрямую в адресной строке вводит адрес для входа. В противном случае перейдите в меню загрузки. Получив меню, сначала преобразуйте json, возвращаемый сервером, в формат, требуемый маршрутизатором, с помощью метода formatRoutes.Основная цель здесь — преобразовать компонент, потому что компонент, возвращаемый сервером, представляет собой строку, а то, что маршрутизатор needs — это компонент, поэтому мы можем динамически загружать необходимые компоненты в методе formatRoutes. После успешной подготовки формата данных, с одной стороны, данные сохраняются в хранилище, а с другой стороны, метод addRoutes в маршруте используется для их динамического добавления в маршрут.

рендеринг меню

Наконец, на главной странице получите меню json из магазина и преобразуйте его в меню. Соответствующий код можно найти вHome.vueПроверьте это, без дальнейших церемоний.

ОК, после этого разные пользователи могут видеть разные меню после успешного входа в систему.

Подпишитесь на официальный аккаунт, чтобы вовремя получать свежие статьи: