698 lines
18 KiB
Go
698 lines
18 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/casbin/casbin/v2"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
"net/http"
|
|
v1 "nunu-layout-admin/api/v1"
|
|
"nunu-layout-admin/internal/model"
|
|
"nunu-layout-admin/pkg/log"
|
|
"nunu-layout-admin/pkg/sid"
|
|
"os"
|
|
)
|
|
|
|
type MigrateServer struct {
|
|
db *gorm.DB
|
|
log *log.Logger
|
|
sid *sid.Sid
|
|
e *casbin.SyncedEnforcer
|
|
}
|
|
|
|
func NewMigrateServer(
|
|
db *gorm.DB,
|
|
log *log.Logger,
|
|
sid *sid.Sid,
|
|
e *casbin.SyncedEnforcer,
|
|
) *MigrateServer {
|
|
return &MigrateServer{
|
|
e: e,
|
|
db: db,
|
|
log: log,
|
|
sid: sid,
|
|
}
|
|
}
|
|
func (m *MigrateServer) Start(ctx context.Context) error {
|
|
m.db.Migrator().DropTable(
|
|
&model.AdminUser{},
|
|
&model.Menu{},
|
|
&model.Role{},
|
|
&model.Api{},
|
|
)
|
|
if err := m.db.AutoMigrate(
|
|
&model.AdminUser{},
|
|
&model.Menu{},
|
|
&model.Role{},
|
|
&model.Api{},
|
|
); err != nil {
|
|
m.log.Error("user migrate error", zap.Error(err))
|
|
return err
|
|
}
|
|
err := m.initialAdminUser(ctx)
|
|
if err != nil {
|
|
m.log.Error("initialAdminUser error", zap.Error(err))
|
|
}
|
|
|
|
err = m.initialMenuData(ctx)
|
|
if err != nil {
|
|
m.log.Error("initialMenuData error", zap.Error(err))
|
|
}
|
|
|
|
err = m.initialApisData(ctx)
|
|
if err != nil {
|
|
m.log.Error("initialApisData error", zap.Error(err))
|
|
}
|
|
|
|
err = m.initialRBAC(ctx)
|
|
if err != nil {
|
|
m.log.Error("initialRBAC error", zap.Error(err))
|
|
}
|
|
|
|
m.log.Info("AutoMigrate success")
|
|
os.Exit(0)
|
|
return nil
|
|
}
|
|
func (m *MigrateServer) Stop(ctx context.Context) error {
|
|
m.log.Info("AutoMigrate stop")
|
|
return nil
|
|
}
|
|
func (m *MigrateServer) initialAdminUser(ctx context.Context) error {
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = m.db.Create(&model.AdminUser{
|
|
Model: gorm.Model{ID: 1},
|
|
Username: "admin",
|
|
Password: string(hashedPassword),
|
|
Nickname: "Admin",
|
|
}).Error
|
|
return m.db.Create(&model.AdminUser{
|
|
Model: gorm.Model{ID: 2},
|
|
Username: "user",
|
|
Password: string(hashedPassword),
|
|
Nickname: "运营人员",
|
|
}).Error
|
|
|
|
}
|
|
func (m *MigrateServer) initialRBAC(ctx context.Context) error {
|
|
roles := []model.Role{
|
|
{Sid: model.AdminRole, Name: "超级管理员"},
|
|
{Sid: "1000", Name: "运营人员"},
|
|
{Sid: "1001", Name: "访客"},
|
|
}
|
|
if err := m.db.Create(&roles).Error; err != nil {
|
|
return err
|
|
}
|
|
m.e.ClearPolicy()
|
|
err := m.e.SavePolicy()
|
|
if err != nil {
|
|
m.log.Error("m.e.SavePolicy error", zap.Error(err))
|
|
return err
|
|
}
|
|
_, err = m.e.AddRoleForUser(model.AdminUserID, model.AdminRole)
|
|
if err != nil {
|
|
m.log.Error("m.e.AddRoleForUser error", zap.Error(err))
|
|
return err
|
|
}
|
|
menuList := make([]v1.MenuDataItem, 0)
|
|
err = json.Unmarshal([]byte(menuData), &menuList)
|
|
if err != nil {
|
|
m.log.Error("json.Unmarshal error", zap.Error(err))
|
|
return err
|
|
}
|
|
for _, item := range menuList {
|
|
m.addPermissionForRole(model.AdminRole, model.MenuResourcePrefix+item.Path, "read")
|
|
}
|
|
apiList := make([]model.Api, 0)
|
|
err = m.db.Find(&apiList).Error
|
|
if err != nil {
|
|
m.log.Error("m.db.Find(&apiList).Error error", zap.Error(err))
|
|
return err
|
|
}
|
|
for _, api := range apiList {
|
|
m.addPermissionForRole(model.AdminRole, model.ApiResourcePrefix+api.Path, api.Method)
|
|
}
|
|
|
|
// 添加运营人员权限
|
|
_, err = m.e.AddRoleForUser("2", "1000")
|
|
if err != nil {
|
|
m.log.Error("m.e.AddRoleForUser error", zap.Error(err))
|
|
return err
|
|
}
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/profile/basic", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/profile/advanced", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/profile", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/dashboard", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/dashboard/workplace", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/dashboard/analysis", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/account/settings", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/account/center", "read")
|
|
m.addPermissionForRole("1000", model.MenuResourcePrefix+"/account", "read")
|
|
m.addPermissionForRole("1000", model.ApiResourcePrefix+"/v1/menus", http.MethodGet)
|
|
m.addPermissionForRole("1000", model.ApiResourcePrefix+"/v1/admin/user", http.MethodGet)
|
|
|
|
return nil
|
|
}
|
|
func (m *MigrateServer) addPermissionForRole(role, resource, action string) {
|
|
_, err := m.e.AddPermissionForUser(role, resource, action)
|
|
if err != nil {
|
|
m.log.Sugar().Info("为角色 %s 添加权限 %s:%s 失败: %v", role, resource, action, err)
|
|
return
|
|
}
|
|
fmt.Printf("为角色 %s 添加权限: %s %s\n", role, resource, action)
|
|
}
|
|
func (m *MigrateServer) initialApisData(ctx context.Context) error {
|
|
initialApis := []model.Api{
|
|
|
|
{Group: "基础API", Name: "获取用户菜单列表", Path: "/v1/menus", Method: http.MethodGet},
|
|
{Group: "基础API", Name: "获取管理员信息", Path: "/v1/admin/user", Method: http.MethodGet},
|
|
|
|
{Group: "菜单管理", Name: "获取管理菜单", Path: "/v1/admin/menus", Method: http.MethodGet},
|
|
{Group: "菜单管理", Name: "创建菜单", Path: "/v1/admin/menu", Method: http.MethodPost},
|
|
{Group: "菜单管理", Name: "更新菜单", Path: "/v1/admin/menu", Method: http.MethodPut},
|
|
{Group: "菜单管理", Name: "删除菜单", Path: "/v1/admin/menu", Method: http.MethodDelete},
|
|
|
|
{Group: "权限模块", Name: "获取用户权限", Path: "/v1/admin/user/permissions", Method: http.MethodGet},
|
|
{Group: "权限模块", Name: "获取角色权限", Path: "/v1/admin/role/permissions", Method: http.MethodGet},
|
|
{Group: "权限模块", Name: "更新角色权限", Path: "/v1/admin/role/permission", Method: http.MethodPut},
|
|
{Group: "权限模块", Name: "获取角色列表", Path: "/v1/admin/roles", Method: http.MethodGet},
|
|
{Group: "权限模块", Name: "创建角色", Path: "/v1/admin/role", Method: http.MethodPost},
|
|
{Group: "权限模块", Name: "更新角色", Path: "/v1/admin/role", Method: http.MethodPut},
|
|
{Group: "权限模块", Name: "删除角色", Path: "/v1/admin/role", Method: http.MethodDelete},
|
|
|
|
{Group: "权限模块", Name: "获取管理员列表", Path: "/v1/admin/users", Method: http.MethodGet},
|
|
{Group: "权限模块", Name: "更新管理员信息", Path: "/v1/admin/user", Method: http.MethodPut},
|
|
{Group: "权限模块", Name: "创建管理员账号", Path: "/v1/admin/user", Method: http.MethodPost},
|
|
{Group: "权限模块", Name: "删除管理员", Path: "/v1/admin/user", Method: http.MethodDelete},
|
|
|
|
{Group: "权限模块", Name: "获取API列表", Path: "/v1/admin/apis", Method: http.MethodGet},
|
|
{Group: "权限模块", Name: "创建API", Path: "/v1/admin/api", Method: http.MethodPost},
|
|
{Group: "权限模块", Name: "更新API", Path: "/v1/admin/api", Method: http.MethodPut},
|
|
{Group: "权限模块", Name: "删除API", Path: "/v1/admin/api", Method: http.MethodDelete},
|
|
}
|
|
|
|
return m.db.Create(&initialApis).Error
|
|
}
|
|
func (m *MigrateServer) initialMenuData(ctx context.Context) error {
|
|
menuList := make([]v1.MenuDataItem, 0)
|
|
err := json.Unmarshal([]byte(menuData), &menuList)
|
|
if err != nil {
|
|
m.log.Error("json.Unmarshal error", zap.Error(err))
|
|
return err
|
|
}
|
|
menuListDb := make([]model.Menu, 0)
|
|
for _, item := range menuList {
|
|
menuListDb = append(menuListDb, model.Menu{
|
|
Model: gorm.Model{
|
|
ID: item.ID,
|
|
},
|
|
ParentID: item.ParentID,
|
|
Path: item.Path,
|
|
Title: item.Title,
|
|
Name: item.Name,
|
|
Component: item.Component,
|
|
Locale: item.Locale,
|
|
Weight: item.Weight,
|
|
Icon: item.Icon,
|
|
Redirect: item.Redirect,
|
|
URL: item.URL,
|
|
KeepAlive: item.KeepAlive,
|
|
HideInMenu: item.HideInMenu,
|
|
})
|
|
}
|
|
return m.db.Create(&menuListDb).Error
|
|
}
|
|
|
|
var menuData = `[
|
|
{
|
|
"id": 18,
|
|
"parentId": 15,
|
|
"path": "/access/admin",
|
|
"title": "管理员账号",
|
|
"name": "accessAdmin",
|
|
"component": "/access/admin",
|
|
"locale": "menu.access.admin"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"parentId": 0,
|
|
"title": "分析页",
|
|
"icon": "DashboardOutlined",
|
|
"component": "/dashboard/analysis",
|
|
"path": "/dashboard/analysis",
|
|
"name": "DashboardAnalysis",
|
|
"keepAlive": true,
|
|
"locale": "menu.dashboard.analysis",
|
|
"weight": 2
|
|
},
|
|
{
|
|
"id": 1,
|
|
"parentId": 0,
|
|
"title": "仪表盘",
|
|
"icon": "DashboardOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/dashboard/analysis",
|
|
"path": "/dashboard",
|
|
"name": "Dashboard",
|
|
"locale": "menu.dashboard"
|
|
},
|
|
{
|
|
"id": 3,
|
|
"parentId": 0,
|
|
"title": "表单页",
|
|
"icon": "FormOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/form/basic",
|
|
"path": "/form",
|
|
"name": "Form",
|
|
"locale": "menu.form"
|
|
},
|
|
{
|
|
"id": 5,
|
|
"parentId": 0,
|
|
"title": "链接",
|
|
"icon": "LinkOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/link/iframe",
|
|
"path": "/link",
|
|
"name": "Link",
|
|
"locale": "menu.link"
|
|
},
|
|
{
|
|
"id": 6,
|
|
"parentId": 5,
|
|
"title": "AntDesign",
|
|
"url": "https://ant.design/",
|
|
"component": "Iframe",
|
|
"path": "/link/iframe",
|
|
"name": "LinkIframe",
|
|
"keepAlive": true,
|
|
"locale": "menu.link.iframe"
|
|
},
|
|
{
|
|
"id": 7,
|
|
"parentId": 5,
|
|
"title": "AntDesignVue",
|
|
"url": "https://antdv.com/",
|
|
"component": "Iframe",
|
|
"path": "/link/antdv",
|
|
"name": "LinkAntdv",
|
|
"keepAlive": true,
|
|
"locale": "menu.link.antdv"
|
|
},
|
|
{
|
|
"id": 8,
|
|
"parentId": 5,
|
|
"path": "https://www.baidu.com",
|
|
"name": "LinkExternal",
|
|
"title": "跳转百度",
|
|
"locale": "menu.link.external"
|
|
},
|
|
{
|
|
"id": 9,
|
|
"parentId": 0,
|
|
"title": "菜单",
|
|
"icon": "BarsOutlined",
|
|
"component": "RouteView",
|
|
"path": "/menu",
|
|
"redirect": "/menu/menu1",
|
|
"name": "Menu",
|
|
"locale": "menu.menu"
|
|
},
|
|
{
|
|
"id": 10,
|
|
"parentId": 9,
|
|
"title": "菜单1",
|
|
"component": "/menu/menu1",
|
|
"path": "/menu/menu1",
|
|
"name": "MenuMenu11",
|
|
"keepAlive": true,
|
|
"locale": "menu.menu.menu1"
|
|
},
|
|
{
|
|
"id": 11,
|
|
"parentId": 9,
|
|
"title": "菜单2",
|
|
"component": "/menu/menu2",
|
|
"path": "/menu/menu2",
|
|
"keepAlive": true,
|
|
"locale": "menu.menu.menu2"
|
|
},
|
|
{
|
|
"id": 12,
|
|
"parentId": 9,
|
|
"path": "/menu/menu3",
|
|
"redirect": "/menu/menu3/menu1",
|
|
"title": "菜单1-1",
|
|
"component": "RouteView",
|
|
"locale": "menu.menu.menu3"
|
|
},
|
|
{
|
|
"id": 13,
|
|
"parentId": 12,
|
|
"path": "/menu/menu3/menu1",
|
|
"component": "/menu/menu-1-1/menu1",
|
|
"title": "菜单1-1-1",
|
|
"keepAlive": true,
|
|
"locale": "menu.menu3.menu1"
|
|
},
|
|
{
|
|
"id": 14,
|
|
"parentId": 12,
|
|
"path": "/menu/menu3/menu2",
|
|
"component": "/menu/menu-1-1/menu2",
|
|
"title": "菜单1-1-2",
|
|
"keepAlive": true,
|
|
"locale": "menu.menu3.menu2"
|
|
},
|
|
{
|
|
"id": 15,
|
|
"path": "/access",
|
|
"component": "RouteView",
|
|
"redirect": "/access/common",
|
|
"title": "权限模块",
|
|
"name": "Access",
|
|
"parentId": 0,
|
|
"icon": "ClusterOutlined",
|
|
"locale": "menu.access",
|
|
"weight": 1
|
|
},
|
|
{
|
|
"id": 51,
|
|
"parentId": 15,
|
|
"path": "/access/role",
|
|
"title": "角色管理",
|
|
"name": "AccessRoles",
|
|
"component": "/access/role",
|
|
"locale": "menu.access.roles"
|
|
},
|
|
{
|
|
"id": 52,
|
|
"parentId": 15,
|
|
"path": "/access/menu",
|
|
"title": "菜单管理",
|
|
"name": "AccessMenu",
|
|
"component": "/access/menu",
|
|
"locale": "menu.access.menus"
|
|
},
|
|
{
|
|
"id": 53,
|
|
"parentId": 15,
|
|
"path": "/access/api",
|
|
"title": "API管理",
|
|
"name": "AccessAPI",
|
|
"component": "/access/api",
|
|
"locale": "menu.access.api"
|
|
},
|
|
{
|
|
"id": 19,
|
|
"parentId": 0,
|
|
"title": "异常页",
|
|
"icon": "WarningOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/exception/403",
|
|
"path": "/exception",
|
|
"name": "Exception",
|
|
"locale": "menu.exception"
|
|
},
|
|
{
|
|
"id": 20,
|
|
"parentId": 19,
|
|
"path": "/exception/403",
|
|
"title": "403",
|
|
"name": "403",
|
|
"component": "/exception/403",
|
|
"locale": "menu.exception.not-permission"
|
|
},
|
|
{
|
|
"id": 21,
|
|
"parentId": 19,
|
|
"path": "/exception/404",
|
|
"title": "404",
|
|
"name": "404",
|
|
"component": "/exception/404",
|
|
"locale": "menu.exception.not-find"
|
|
},
|
|
{
|
|
"id": 22,
|
|
"parentId": 19,
|
|
"path": "/exception/500",
|
|
"title": "500",
|
|
"name": "500",
|
|
"component": "/exception/500",
|
|
"locale": "menu.exception.server-error"
|
|
},
|
|
{
|
|
"id": 23,
|
|
"parentId": 0,
|
|
"title": "结果页",
|
|
"icon": "CheckCircleOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/result/success",
|
|
"path": "/result",
|
|
"name": "Result",
|
|
"locale": "menu.result"
|
|
},
|
|
{
|
|
"id": 24,
|
|
"parentId": 23,
|
|
"path": "/result/success",
|
|
"title": "成功页",
|
|
"name": "ResultSuccess",
|
|
"component": "/result/success",
|
|
"locale": "menu.result.success"
|
|
},
|
|
{
|
|
"id": 25,
|
|
"parentId": 23,
|
|
"path": "/result/fail",
|
|
"title": "失败页",
|
|
"name": "ResultFail",
|
|
"component": "/result/fail",
|
|
"locale": "menu.result.fail"
|
|
},
|
|
{
|
|
"id": 26,
|
|
"parentId": 0,
|
|
"title": "列表页",
|
|
"icon": "TableOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/list/card-list",
|
|
"path": "/list",
|
|
"name": "List",
|
|
"locale": "menu.list"
|
|
},
|
|
{
|
|
"id": 27,
|
|
"parentId": 26,
|
|
"path": "/list/card-list",
|
|
"title": "卡片列表",
|
|
"name": "ListCard",
|
|
"component": "/list/card-list",
|
|
"locale": "menu.list.card-list"
|
|
},
|
|
{
|
|
"id": 28,
|
|
"parentId": 0,
|
|
"title": "详情页",
|
|
"icon": "ProfileOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/profile/basic",
|
|
"path": "/profile",
|
|
"name": "Profile",
|
|
"locale": "menu.profile"
|
|
},
|
|
{
|
|
"id": 29,
|
|
"parentId": 28,
|
|
"path": "/profile/basic",
|
|
"title": "基础详情页",
|
|
"name": "ProfileBasic",
|
|
"component": "/profile/basic/index",
|
|
"locale": "menu.profile.basic"
|
|
},
|
|
{
|
|
"id": 30,
|
|
"parentId": 26,
|
|
"path": "/list/search-list",
|
|
"title": "搜索列表",
|
|
"name": "SearchList",
|
|
"component": "/list/search-list",
|
|
"locale": "menu.list.search-list"
|
|
},
|
|
{
|
|
"id": 31,
|
|
"parentId": 30,
|
|
"path": "/list/search-list/articles",
|
|
"title": "搜索列表(文章)",
|
|
"name": "SearchListArticles",
|
|
"component": "/list/search-list/articles",
|
|
"locale": "menu.list.search-list.articles"
|
|
},
|
|
{
|
|
"id": 32,
|
|
"parentId": 30,
|
|
"path": "/list/search-list/projects",
|
|
"title": "搜索列表(项目)",
|
|
"name": "SearchListProjects",
|
|
"component": "/list/search-list/projects",
|
|
"locale": "menu.list.search-list.projects"
|
|
},
|
|
{
|
|
"id": 33,
|
|
"parentId": 30,
|
|
"path": "/list/search-list/applications",
|
|
"title": "搜索列表(应用)",
|
|
"name": "SearchListApplications",
|
|
"component": "/list/search-list/applications",
|
|
"locale": "menu.list.search-list.applications"
|
|
},
|
|
{
|
|
"id": 34,
|
|
"parentId": 26,
|
|
"path": "/list/basic-list",
|
|
"title": "标准列表",
|
|
"name": "BasicCard",
|
|
"component": "/list/basic-list",
|
|
"locale": "menu.list.basic-list"
|
|
},
|
|
{
|
|
"id": 35,
|
|
"parentId": 28,
|
|
"path": "/profile/advanced",
|
|
"title": "高级详细页",
|
|
"name": "ProfileAdvanced",
|
|
"component": "/profile/advanced/index",
|
|
"locale": "menu.profile.advanced"
|
|
},
|
|
{
|
|
"id": 4,
|
|
"parentId": 3,
|
|
"title": "基础表单",
|
|
"component": "/form/basic-form/index",
|
|
"path": "/form/basic-form",
|
|
"name": "FormBasic",
|
|
"keepAlive": false,
|
|
"locale": "menu.form.basic-form"
|
|
},
|
|
{
|
|
"id": 36,
|
|
"parentId": 0,
|
|
"title": "个人页",
|
|
"icon": "UserOutlined",
|
|
"component": "RouteView",
|
|
"redirect": "/account/center",
|
|
"path": "/account",
|
|
"name": "Account",
|
|
"locale": "menu.account"
|
|
},
|
|
{
|
|
"id": 37,
|
|
"parentId": 36,
|
|
"path": "/account/center",
|
|
"title": "个人中心",
|
|
"name": "AccountCenter",
|
|
"component": "/account/center",
|
|
"locale": "menu.account.center"
|
|
},
|
|
{
|
|
"id": 38,
|
|
"parentId": 36,
|
|
"path": "/account/settings",
|
|
"title": "个人设置",
|
|
"name": "AccountSettings",
|
|
"component": "/account/settings",
|
|
"locale": "menu.account.settings"
|
|
},
|
|
{
|
|
"id": 39,
|
|
"parentId": 3,
|
|
"title": "分步表单",
|
|
"component": "/form/step-form/index",
|
|
"path": "/form/step-form",
|
|
"name": "FormStep",
|
|
"keepAlive": false,
|
|
"locale": "menu.form.step-form"
|
|
},
|
|
{
|
|
"id": 40,
|
|
"parentId": 3,
|
|
"title": "高级表单",
|
|
"component": "/form/advanced-form/index",
|
|
"path": "/form/advanced-form",
|
|
"name": "FormAdvanced",
|
|
"keepAlive": false,
|
|
"locale": "menu.form.advanced-form"
|
|
},
|
|
{
|
|
"id": 41,
|
|
"parentId": 26,
|
|
"path": "/list/table-list",
|
|
"title": "查询表格",
|
|
"name": "ConsultTable",
|
|
"component": "/list/table-list",
|
|
"locale": "menu.list.consult-table"
|
|
},
|
|
{
|
|
"id": 42,
|
|
"parentId": 1,
|
|
"title": "监控页",
|
|
"component": "/dashboard/monitor",
|
|
"path": "/dashboard/monitor",
|
|
"name": "DashboardMonitor",
|
|
"keepAlive": true,
|
|
"locale": "menu.dashboard.monitor"
|
|
},
|
|
{
|
|
"id": 43,
|
|
"parentId": 1,
|
|
"title": "工作台",
|
|
"component": "/dashboard/workplace",
|
|
"path": "/dashboard/workplace",
|
|
"name": "DashboardWorkplace",
|
|
"keepAlive": true,
|
|
"locale": "menu.dashboard.workplace"
|
|
},
|
|
{
|
|
"id": 44,
|
|
"parentId": 26,
|
|
"path": "/list/crud-table",
|
|
"title": "增删改查表格",
|
|
"name": "CrudTable",
|
|
"component": "/list/crud-table",
|
|
"locale": "menu.list.crud-table"
|
|
},
|
|
{
|
|
"id": 45,
|
|
"parentId": 9,
|
|
"path": "/menu/menu4",
|
|
"redirect": "/menu/menu4/menu1",
|
|
"title": "菜单2-1",
|
|
"component": "RouteView",
|
|
"locale": "menu.menu.menu4"
|
|
},
|
|
{
|
|
"id": 46,
|
|
"parentId": 45,
|
|
"path": "/menu/menu4/menu1",
|
|
"component": "/menu/menu-2-1/menu1",
|
|
"title": "菜单2-1-1",
|
|
"keepAlive": true,
|
|
"locale": "menu.menu4.menu1"
|
|
},
|
|
{
|
|
"id": 47,
|
|
"parentId": 45,
|
|
"path": "/menu/menu4/menu2",
|
|
"component": "/menu/menu-2-1/menu2",
|
|
"title": "菜单2-1-2",
|
|
"keepAlive": true,
|
|
"locale": "menu.menu4.menu2"
|
|
}
|
|
]`
|