登入頁面
不同的路徑,在顯示不同的東西。不同的router路徑在router/index.js配置,配置路徑還有對應的頁面(vue)
const routes = [
{
path: '/',
name: 'Login',
component: Login
}
]
服務端登入接口制作
總體步驟:
1、逆向工程,建立model包和mapper包
2、Hr實體類,實作
UserDetails
接口,并重寫七個方法
3、建立HrService,根據使用者名,通過HrMapper擷取hr對象。并傳回該對象
4、建立
SecurityConfig
配置類,繼承
WebSecurityConfiguerAdapter
類;
配置密碼加密;
重寫
configure(AuthenticationManagerBuilder auth)
方法,并将hrService傳入
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService);
}
6、寫一個helloController來測試一下
7、測試成功,說明,Spring Security已經和資料庫連接配接上了
8、重寫
configure(HttpSecurity http)
方法,
寫登入成功時的回調,登入失敗時的回調,登出的回調
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() // 剩餘所有請求都需要登入才能通路
.and()
.formLogin() // 表單登入
.usernameParameter("username") // 攜帶參數 username的名稱
.passwordParameter("password") // 攜帶參數 passowrd的名稱
.loginProcessingUrl("/doLogin") // 如果使用PostMan,通過通路這個路徑來登入
.loginPage("/login")
.successHandler(new AuthenticationSuccessHandler() { // 登入成功後的操作
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
// authentication 表示登入成功的使用者資訊
Hr hr = (Hr) authentication.getPrincipal();
// 傳回RespBean對象
RespBean ok = RespBean.ok("登入成功", hr);
// 輸出成字元串
String s = new ObjectMapper().writeValueAsString(ok);
out.write(s);
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() { // 登入失敗的操作
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException exception) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error("登入失敗");
if(exception instanceof LockedException){
respBean.setMsg("賬戶被鎖定,請聯系管理者");
}else if(exception instanceof CredentialsExpiredException){
respBean.setMsg("密碼過期");
}else if(exception instanceof AccountExpiredException){
respBean.setMsg("賬戶過期,請聯系管理者");
}else if(exception instanceof DisabledException){
respBean.setMsg("賬戶被禁用,請聯系管理者");
}else if(exception instanceof BadCredentialsException){
respBean.setMsg("使用者名或者密碼錯誤,請聯系管理者");
}
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
})
.permitAll() //
.and()
.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() { // 登出的操作
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
}
})
.permitAll()
.and()
.csrf().disable();
}
9、使用PostMan通路
http://localhost:8080/doLogin
測試一下,
1、在hr類實作
UserDetails
接口,實作方法
2、建立HrService,并實作
UserDetailsService
接口,實作
loadUserByUsername
方法
2.1、查詢hr對象,使用MyBatis查詢。如果等于null,抛出異常
UsernameNotFoundException
2.2、查詢
3、配置security配置類,繼承
WebSecurityConfiguerAdapter
類
3.1、注入hrService
@Autowired
HrService hrService;
3.2、配置Bean,密碼加密的bean
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
3.3、實作
configure
方法
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService);
}
3.4、然後測試下
寫一個helloController,get方式請求,傳回一個hello就好
如果登入成功,說明已經介入到資料庫了
3.5、因為是前後端分離,是以需要配置使用JSON的方式登入
登陸成功:傳回一個登入成功的JSON
登入失敗:傳回一個登入失敗的JSON
定義一個統一的傳回Bean:RespBean,看源碼裡邊有
實作 configure(HttpSecurity http)
接口:
configure(HttpSecurity http)
.permitAll() 表示登入相關的頁面/接口不要被攔截。
登入成功的回調:
.successHandler
authentication:登入成功的使用者資訊
登入成功傳回的是一個RespBean對象
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Hr hr = (Hr) authentication.getPrincipal();
RespBean ok = RespBean.ok("登入成功", hr);
String s = new ObjectMapper().writeValueAsString(ok);
out.write(s);
out.flush();
out.close();
}
})
登入成功後,不顯示密碼
hr.setPassword(null)
登入失敗的回調:
.failureHandler
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean error = RespBean.error("登入失敗");
if (e instanceof LockedException) {
error.setMsg("賬戶被鎖定,請聯系管理者");
} else if (e instanceof CredentialsExpiredException) {
error.setMsg("密碼過期,請聯系管理者");
} else if (e instanceof AccountExpiredException) {
error.setMsg("賬戶過期,請聯系管理者");
}else if (e instanceof DisabledException) {
error.setMsg("賬戶被禁用,請聯系管理者");
}else if(e instanceof BadCredentialsException){
error.setMsg("使用者名或者密碼輸入錯誤,請聯系管理者");
}
String s = new ObjectMapper().writeValueAsString(error);
out.write(s);
out.flush();
out.close();
}
})
定義一個
LoginController
來處理登入頁的問題
@RestController
public class LoginController {
@GetMapping("/login")
public RespBean login(){
return RespBean.error("尚未登入,請登入");
}
}
登出的回調:
.logoutSuccessHandler
authentication:表示需要登出的使用者
直接傳回一個RespBean對象
前後端接口對接
安裝axios,
npm install axios
1、封裝網絡請求,建立utils包,再建立api.js。
- 在裡邊寫一個響應攔截器。
這是http的響應碼,success.status
這是我們傳回的JSON的響應碼success.data.status
import axios from 'axios'
import {Message} from 'element-ui'
/*響應的攔截器*/
axios.interceptors.response.use(success=>{
if(success.status && success.status ==200 && success.data.status==500){
Message.error({message:success.data.msg})
return;
}
return success.data;
},error => {
if(error.response.status == 504 ||error.response.status == 404 ){
Message.error({message:'沒找到伺服器o(╯□╰)o'})
}else if(error.response.status == 403 ){
Message.error({message: '權限不足,請聯系管理者'})
}else if(error.response.status == 401 ){
Message.error({message: '沒有登入,請登入'})
}else{
if(error.response.data.msg){
Message.error({message:error.response.data.nsg})
}else{
Message.error({message: '未知錯誤!'})
}
}
return;
})
- 再寫一個POST請求的封裝,(新增 封裝多個請求
let base = '';
// 傳遞key value的請求
export const postKeyValueRequest=(url,params)=>{
return axios({
method:'post',
url:`${base}${url}`,
data:params,
transformRequest:[function(data){
let ret = '';
for(let i in data){
ret +=encodeURIComponent(i) +'=' + encodeURIComponent(data[i])+'&'
}
console.log(ret);
return ret;
}],
headers:{
'Content-Type':'application/x-www-form-urlencoded'
}
});
}
// 傳遞JSON的請求
export const postRequest=(url,params)=>{
return axios({
method:'post',
url:`${base}${url}`,
data:params
})
}
export const putRequest=(url,params)=>{
return axios({
method:'put',
url:`${base}${url}`,
data:params
})
}
export const getRequest=(url,params)=>{
return axios({
method:'get',
url:`${base}${url}`,
data:params
})
}
export const deleteRequest=(url,params)=>{
return axios({
method:'delete',
url:`${base}${url}`,
data:params
})
}
将封裝的請求,做成vue插件
在main.js把封裝的請求都導入進來
import {postRequest} from "./utils/api";
import {postKeyValueRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";
import {getRequest} from "./utils/api";
Vue.prototype.postRequest = postRequest;
Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.getRequest = getRequest;
使用:
this.封裝的請求.
如:this.postRequest()
- 使用封裝的請求
- 1、先導入:
import {postKeyValueRequest} from "../utils/api"
- 2、使用:
- 另外一種使用方法:
前提是已經做成了一個vue插件this.postRequest()
- 1、先導入:
postKeyValueRequest('/doLogin', this.loginForm).then(resp=>{
if(resp) {
alert(JSON.stringify(resp))
}
})
通過
if(resp)
判斷請求成功還是失敗,失敗不用管,因為在攔截器已經處理過了
- 跨域問題處理:配置 nodejs 請求轉發代理vue.config.js
let proxyObj = {};
proxyObj['/']={
ws:false,
target:'http://localhost:8081', // 把攔截的請求轉發到8081端口區
changeOrigin: true,
pathRewrite:{
'^/':''
}
}
module.exports={
devServer:{
host:'localhost',
port:8080,
proxy:proxyObj
}
}
2、儲存請求回來的資料到
sessionStorage
3、儲存後再進行頁面跳轉,跳轉到
Home.vue
頁面
3.1、建立
Home.vue
頁面
3.2、在
Router.js
中引入,并設定
3.3、跳轉
this.$router.replace('/home')
postKeyValueRequest('/doLogin', this.loginForm).then(resp=>{
if(resp) {
window.sessionStorage.setItem("user", JSON.stringify(resp.obj));
this.$router.replace('/home')
}
})
Home頁面制作
從
sessionStorage
讀取使用者資訊
登出登入要把
sessionStorage
的資訊清空
api.js加上一個登入成功的提示
if(success.data.msg) {
Message.success({message:success.data.msg})
}
左邊導航菜單制作
1、在右邊顯示視窗加一個
2、在router包下index.js添加
{
path: '/home',
name: '導航一',
component: Home,
children:[
{
path: '/test1', // 要跳轉的頁面,這裡為vue
name: '選項1',
component: Test1
}, {
path: '/test2',
name: '選項2',
component: Test2
}
]
}
3、統一Home.vue和index.js的選項頁面
渲染index.js的routes到Home.vue中,添加hidden給不需要渲染的routes,用來區分。
<el-menu router>
<el-submenu index="1" v-for="(item,index) in this.$router.options.routes" v-if="!item.hidden" :key="index">
<template slot="title">
<i class="el-icon-location"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item :index="child.path" v-for="(child,indexj) in item.children " :key="indexj">
{{child.name}}
</el-menu-item>
</el-submenu>
</el-menu>
4、通過資料庫動态修改左邊導航菜單
- menu表裡存有導航菜單,分有一級菜單,二級菜單。enabled表示菜單是否啟用,requireAuth表示需要登入才能通路。
- 根據使用者id擷取角色id,根據角色id擷取menu id,再通過menu id擷取使用者可以操作的菜單。
目的:通過服務端根據使用者id傳回一個menu菜單。
1、修改menu實體類,改成駝峰法(看個人)
2、将額外的字段放到一個meta類裡邊,
這裡将keepAlive,requireAuth定義成meta類的成員變量
在menu實體類增加一個meta成員變量,
在menu實體類增加一個children成員變量,List
類型,
在MenuMapper的resultMap修改:
<association property="meta" javaType="org.javaboy.vhr.model.Meta">
<result column="keepAlive" property="keepAlive" jdbcType="BIT" />
<result column="requireAuth" property="requireAuth" jdbcType="BIT" />
</association>
3、寫一個controller查詢需要的資料,系統配置的controller(SystemConfigController),建立MenuService。
通過系統儲存的使用者對象的id來查詢,
(Hr)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId()
mapper進行查詢:
sql語句:
select
distinct m1.*,m2.id as id2,m2.component as component2,
m2.enabled as enabled2,
m2.iconCls as iconCls2,m2.keepAlive as keepAlive2,m2.name as name2,
m2.parentId as parentId2,m2.requireAuth as requireAuth2,m2.path as path2
from
hr_role hrr,
menu_role mr,
menu m2,
menu m1
where
hrr.hrid=10
and hrr.rid=mr.rid
and m2.id = mr.mid
and m1.id = m2.parentId
and m1.enabled=true
// 目的:查詢該使用者所擁有的菜單資訊
// 步驟:
1、查詢使用者為10的角色id
2、根據角色id查詢該角色擁有的菜單id
3、根據菜單id查詢具體的菜單資訊
4、根據菜單id連結菜單的父類菜單資訊
5、給子菜單的字段取個别名
重新定義一個resultMap:
<resultMap id="Menus2" type="org.javaboy.vhr.model.Menu" extends="BaseResultMap">
<collection property="children" ofType="org.javaboy.vhr.model.Menu">
<id column="id2" property="id" jdbcType="INTEGER" />
<result column="url2" property="url" jdbcType="VARCHAR" />
<result column="path2" property="path" jdbcType="VARCHAR" />
<result column="component2" property="component" jdbcType="VARCHAR" />
<result column="name2" property="name" jdbcType="VARCHAR" />
<result column="iconCls2" property="iconCls" jdbcType="VARCHAR" />
<result column="parentId2" property="parentId" jdbcType="INTEGER" />
<result column="enabled2" property="enabled" jdbcType="BIT" />
<association property="meta" javaType="org.javaboy.vhr.model.Meta">
<result column="keepAlive2" property="keepAlive" jdbcType="BIT" />
<result column="requireAuth2" property="requireAuth" jdbcType="BIT" />
</association>
</collection>
</resultMap>
5、菜單項資料加載成功之後,在前端有幾個可以存放的地方:
sessionStorage
localStorage
vuex
需要把加載下來的菜單項資料,需要放在一個公共的地方,讓大家都能通路
vuex:狀态管理。把資料放在一個公共的地方。可以用于資料共享,互相調用。
vuex的安裝:
npm install vuex
vuex的用法:
- 建立一個
包,建立store
檔案index.js
- 在
引入。 在new Vue裡加入main.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.user(Vuex)
export default new Vuex.Store({
state:{
routes:[] // 菜單項都放在這個資料裡
},
mutations:{
initRoutes(state,data){ // 這裡寫怎麼放
state.routes = data;
}
},
actions:{
}
})
// 引入
import store from './store'
new Vue({
// 加上一個
store,
})
6、寫一個專門的工具類,功能:1、從服務端請求資料。2、把服務端傳回的字元串轉成前端的對象;3、把服務端傳回的資料放到
router.js
中去
在utiles包中建立,
menus.js
在views包中建立元件的vue:有五個包:emp、per、sal、sta、sys。也拷貝一份到components
emp:EmpBasic.vue(基本資料)、EmpAdv.vue(進階資料)
per:PerEmp(員工資料)、PerEc(員工獎懲)、PerTrain(員工教育訓練)、PerSalary(員工調薪)、PerMv(員工調動)
sal:SalSob(工資賬套管理)、SalSobCfg(員工賬套設定)、SalTable(工資表管理)、SalMonth(月末處理)、SalSearch(工資表查詢)
sta:StaAll(綜合資訊統計)、StaScore(員工積分統計)、StaPers(人事資訊統計)、StaRecord(人事記錄統計)
sys:SysBasic(基礎資訊設定)、SysCfg(系統管理)、SysLog(記錄檔管理)、SysHr(操作員管理)、SysData(備份恢複資料庫)、SysInit(初始化資料庫)
import {getRequest} from "../../utils/api";
export const initMenu=(router,store) =>{ // 要存到router,要儲存到store
if(store.state.routes.length > 0){
// 說明有菜單資料,直接傳回
return;
}
getRequest("/system/config/menu").then(data =>{
if(data){
let fmtRoutes = formatRoutes(data);
router.addRoutes(fmtRoutes);
store.commit('initRoutes', fmtRoutes); // 調用initRoutes這個方法
}
})
}
// 把一些字元串字段變成前端對象
export const formatRoutes=(routes)=>{
let fmRoutes = [];
routes.forEach(router =>{
let{
path,
component,
name,
meta,
iconCls,
children
} = router // router.path, router.name, router.component....
if(children && children instanceof Array){
// 說明是一級菜單
// 遞歸調用
children = formatRoutes(children);
}
let fmRouter={
path:path,
name:name,
iconCls:iconCls,
meta:meta,
children:children,
component(resolve){ // 主要是處理這個component,componen就是傳回的字元串: component:"。。是這個。。"
// 動态導入(用的時候再導入這個檔案,類似于import...),導入這個vue。
if(component.startsWith("Home")){
require(['../views/' + component+'.vue'],resolve);
}else if (component.startsWith("Emp")){
require(['../views/emp/' + component+'.vue'],resolve);
}else if(component.startsWith("Per")) {
require(['../views/per/' + component+'.vue'],resolve);
}else if(component.startsWith("Sal")) {
require(['../views/sal/' + component+'.vue'],resolve);
}else if(component.startsWith("Sta")) {
require(['../views/sta/' + component+'.vue'],resolve);
}else if(component.startsWith("Sys")) {
require(['../views/sys/' + component+'.vue'],resolve);
}
}
}
fmRoutes.push(fmRouter);
})
return fmRoutes;
}
7、左邊導航欄菜單加載
通過路由導航守衛調用
initMenu()
方法來加載菜單項
路由導航守衛 :路由頁面在跳轉過程中,可以對頁面的跳轉進行監聽,可以過濾。
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
在main.js加載路由導航守衛
router.beforeEach(to, from, next) => {
// from:從哪來
// to:去哪裡
if (to.path == '/') { // 如果要去的頁面是登入頁,直接放行
next();
}else {
// 調用initMenu方法加載菜單項 ,記得導入 import {initMenu} from "@/store/menus"
initMenu(router, store);
next();
}
}
替換菜單項,把菜單項換成store裡存的資料。使用computed
在Home.vue中添加
computed: {
routes(){
return this.$store.state.routes;
}
}
然後導航頁的資料源代碼換成:routes,再加一個unique-opened..看别人的那個代碼
登出登入時,要清空store裡的資料
this.$store.commit('initRoutes',[])
安裝圖示庫:看網頁
完善首頁:看網頁
前後端分離權限管理思路探讨
目的:前端做菜單顯示隻是為了提高使用者體驗,而不是進行權限控制
權限:後端會提供很多接口,每個接口都需要使用者或者角色才能通路。沒有權限就通路不了接口。
後端處理的權限才是安全的,前端處理的不是安全的。
權限管理:後端接口設計
1、前端先發起一個http請求,拿到請求後,先分析位址和menu表的url字段哪一個是比對的。
2、然後去menu_role去檢視,哪些角色能通路這個資源。
3、檢視目前登入的使用者是否具備所需要的角色,如果具備那就沒問題,不具備就是越權通路。
1、寫一個類,來實作:通過請求位址來擷取請求位址所需要的角色
2、寫一個類,來判斷:目前使用者是否具備有上邊類傳回結果所需要的角色
3、在Security配置類中,設定這兩個類
4、給登入使用者設定角色資訊
接口設計
1、定義一個Fileter實作FilterInvocationSecurityMetadataSource接口(這個類的功能就是啟動權限通路)(根據使用者傳來的請求位址,分析出請求需要的角色)
2、主要寫
getAttributes
這個方法
3、Menu類加一個
List<Role> Roles
MenuService寫一個
getAllMenusWithRole
方法。Mapper寫一個sql:
在定義一個ResultMap
SELECT m.*,r.id as rid,r.name as rname,r.nameZh as rnameZh
FROM menu m,menu_role mr, role r
WHERE m.id=mr.mid and mr.rid=r.id
ORDER BY m.id
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException
//
Collection<ConfigAttribute>:目前請求需要的角色
第一步:通過分析目前請求位址,需要哪些角色才能通路
// 最終目的:通過分析目前請求位址,需要哪些角色才能通路
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) o).getRequestUrl(); // 目前請求的位址
List<Menu> menus = menuService.getAllMenusWithRole(); // 擷取所有的菜單所需要的角色。需要開緩存,不然每次都會去查資料庫
// 周遊menus比較目前請求路徑
// antPathMatcher需要new
for (Menu menu : menus) {
if(antPathMatcher.match(menu.getUrl(),requestUrl)){
// 擷取目前資源需要的角色
List<Role> roles = menu.getRoles();
String[] str = new String[roles.size()];
for (int i = 0; i < roles.size(); i++) {
str[i] = roles.get(i).getName();
}
return SecurityConfig.createList(str);
}
}
// 表示沒有比對上的,都是登入後通路
return SecurityConfig.createList("ROLE_LOGIN");
}
第二步:分析目前使用者是否具備有所通路資源的角色
寫一個類,實作
AccessDecisionManager
接口,用來判斷目前使用者是否具備所通路資源的角色,如果具有就過。
主要重寫:
public void decide(Authentication authentication, Object o,
Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException
// authentication:登入成功的使用者資訊。Collection<ConfigAttribute> collection:是上邊Fileter的傳回值,就是menu需要的角色資訊
@Override
public void decide(Authentication authentication, Object o,
Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute configAttribute : collection) {
String needRole = configAttribute.getAttribute(); // 需要的角色
// 判斷有沒有登入。如果目前使用者是匿名執行個體的話,就說明沒登入
if("ROLE_LOGIN".equals(needRole)){
if(authentication instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("尚未登入,請登入");
}else{
return;
}
}
// 擷取目前登入使用者的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 如果資源需要的角色有多個,隻需要判斷目前使用者的角色有一個在裡邊就可以
for (GrantedAuthority authority : authorities) {
if(authority.getAuthority().equals(needRole)){
return;
}
}
}
throw new AccessDeniedException("權限不足,請聯系管理者");
}
第三步:在
SecurityConfig
引進這兩個類就可以了
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O object) {
object.setAccessDecisionManager(customUrlDecisionManager);
object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
return object;
}
})
第四步:給使用者給角色
Hr類添加 List roles 屬性
這個操作就是給使用者賦角色的值
@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
在登入成功後,擷取登入使用者的角色,并給Hr的roles指派,在HrService裡:
添加:
hr.setRoles(hrMapper.getHrRolesById(hr.getId()));
寫sql:根據使用者id查詢使用者用擁有的角色
第四步:測試
如果通路
/login
不用經過
Spring Security
在
SecurityConfig
加上
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login");
}
完善權限管理:看網頁!
基礎資訊設定頁面
前端:複制别人的
vue的元件化開發,由小的頁面(元件)拼接成一個完整的頁面(元件)
1、在components包下建立sys包建立basic包,建立5個元件:DepMana、PosMana、JobLevelMana、EcMana、PermissMana
2、在SysBasic.vue引入這五個元件
import DepMana from '../../components/sys.......'
自己寫吧。。。。
并注冊元件
components:{
// 'DepMana':DepMana,前邊跟後邊一樣的可以簡寫成下邊這樣。
DepMana
}
在<template></template>内寫<DepMana></DepMana>
如:
<el-tab-pane label="部門管理" name="a"><DepMana></DepMana></el-tab-pane>