├── .gitattributes ├── QMPlusVuePage ├── .env.production ├── .env.development ├── babel.config.js ├── src │ ├── utils │ │ ├── _import.js │ │ ├── asyncRouter.js │ │ ├── bus.js │ │ ├── downloadImg.js │ │ ├── data.js │ │ └── request.js │ ├── assets │ │ ├── qm.png │ │ ├── logo.png │ │ └── notFound.png │ ├── view │ │ ├── example │ │ │ ├── index.vue │ │ │ ├── rte │ │ │ │ └── rte.vue │ │ │ ├── excel │ │ │ │ └── excel.vue │ │ │ ├── table │ │ │ │ └── table.vue │ │ │ ├── upload │ │ │ │ └── upload.vue │ │ │ └── form │ │ │ │ └── form.vue │ │ ├── workflow │ │ │ ├── index.vue │ │ │ └── workflowCreate │ │ │ │ └── workflowCreate.vue │ │ ├── superAdmin │ │ │ ├── index.vue │ │ │ ├── authority │ │ │ │ ├── components │ │ │ │ │ ├── menus.vue │ │ │ │ │ ├── apis.vue │ │ │ │ │ └── datas.vue │ │ │ │ └── authority.vue │ │ │ ├── system │ │ │ │ └── system.vue │ │ │ ├── user │ │ │ │ └── user.vue │ │ │ ├── menu │ │ │ │ └── menu.vue │ │ │ └── api │ │ │ │ └── api.vue │ │ ├── test │ │ │ └── index.vue │ │ ├── layout │ │ │ ├── aside │ │ │ │ ├── asideComponent │ │ │ │ │ ├── menuItem.vue │ │ │ │ │ ├── asyncSubmenu.vue │ │ │ │ │ └── index.vue │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ ├── error │ │ │ └── index.vue │ │ ├── dashbord │ │ │ ├── index.vue │ │ │ └── component │ │ │ │ └── animition.vue │ │ ├── person │ │ │ └── person.vue │ │ └── login │ │ │ ├── login.vue │ │ │ └── regist.vue │ ├── App.vue │ ├── api │ │ ├── jwt.js │ │ ├── workflow.js │ │ ├── system.js │ │ ├── fileUploadAndDownload.js │ │ ├── casbin.js │ │ ├── user.js │ │ ├── authority.js │ │ ├── menu.js │ │ └── api.js │ ├── store │ │ ├── index.js │ │ └── module │ │ │ ├── router.js │ │ │ └── user.js │ ├── router │ │ └── index.js │ ├── components │ │ └── mixins │ │ │ └── infoList.js │ ├── style │ │ └── base.scss │ ├── main.js │ └── permission.js ├── public │ ├── favicon.ico │ ├── qm-plus-img │ │ ├── 132604444.png │ │ └── 86891839.png │ └── index.html ├── README.md ├── package.json └── vue.config.js ├── tools ├── md5.go ├── array_to_string.go ├── directory.go ├── struct_to_map.go ├── hasGap.go ├── des.go └── str.go ├── static ├── rbacmodel │ └── rbac_model.conf └── config │ └── config.json ├── controller ├── servers │ ├── reportformat.go │ ├── paging.go │ ├── upload.go │ └── breakpoint_continue.go └── api │ ├── sys_workFlow.go │ ├── sys_jwt_blacklist.go │ ├── sys_casbin.go │ ├── sys_system.go │ ├── sys_authority.go │ ├── exa_fileUploadAndDownload.go │ ├── exa_breakpoint_continue.go │ ├── sys_api.go │ └── sys_menu.go ├── model ├── modelInterface │ └── interface.go ├── sysModel │ ├── sys_workFlowProcess.go │ ├── sys_system.go │ ├── sys_jwt_blacklist.go │ ├── sys_worfFlow.go │ ├── sys_menu_authority.go │ ├── sys_api.go │ ├── sys_authority.go │ ├── sys_casbin.go │ ├── sys_user.go │ └── sys_base_menu.go └── dbModel │ ├── exa_fileUploadAndDownload.go │ └── exa_breakpoint_continue.go ├── router ├── sys_base.go ├── sys_jwt.go ├── sys_workflow.go ├── sys_casbin.go ├── sys_system.go ├── sys_user.go ├── sys_authority.go ├── sys_api.go ├── sys_menu.go └── exp_fileUploadAndDownload.go ├── .gitignore ├── middleware ├── loadtls.go ├── casbin_rcba.go ├── cors.go ├── logger.go └── jwt.go ├── init ├── registTable │ └── regist_table.go ├── qmsql │ └── initMysql.go ├── initRedis │ └── init_redis.go ├── qmlog │ └── qmlog.go └── initRouter │ └── init_router.go ├── cmd └── windows.go ├── main.go ├── go.mod ├── config └── config.go └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sql linguist-language=GO -------------------------------------------------------------------------------- /QMPlusVuePage/.env.production: -------------------------------------------------------------------------------- 1 | ENV = 'production' 2 | VUE_APP_BASE_API = '/' -------------------------------------------------------------------------------- /QMPlusVuePage/.env.development: -------------------------------------------------------------------------------- 1 | ENV = 'development' 2 | VUE_APP_BASE_API = '/api' -------------------------------------------------------------------------------- /QMPlusVuePage/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/utils/_import.js: -------------------------------------------------------------------------------- 1 | module.exports = file => () => { 2 | return import ('@/' + file) 3 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/assets/qm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hairichuhe/nideshop-admin/HEAD/QMPlusVuePage/src/assets/qm.png -------------------------------------------------------------------------------- /QMPlusVuePage/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hairichuhe/nideshop-admin/HEAD/QMPlusVuePage/public/favicon.ico -------------------------------------------------------------------------------- /QMPlusVuePage/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hairichuhe/nideshop-admin/HEAD/QMPlusVuePage/src/assets/logo.png -------------------------------------------------------------------------------- /QMPlusVuePage/src/assets/notFound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hairichuhe/nideshop-admin/HEAD/QMPlusVuePage/src/assets/notFound.png -------------------------------------------------------------------------------- /QMPlusVuePage/public/qm-plus-img/132604444.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hairichuhe/nideshop-admin/HEAD/QMPlusVuePage/public/qm-plus-img/132604444.png -------------------------------------------------------------------------------- /QMPlusVuePage/public/qm-plus-img/86891839.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hairichuhe/nideshop-admin/HEAD/QMPlusVuePage/public/qm-plus-img/86891839.png -------------------------------------------------------------------------------- /tools/md5.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | func MD5V(str []byte) string { 9 | h := md5.New() 10 | h.Write(str) 11 | return hex.EncodeToString(h.Sum(nil)) 12 | } 13 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/example/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/workflow/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /tools/array_to_string.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func ArrayToString(array []interface{}) string { 9 | return strings.Replace(strings.Trim(fmt.Sprint(array), "[]"), " ", ",", -1) 10 | } 11 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/test/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /tools/directory.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "os" 4 | 5 | func PathExists(path string) (bool, error) { 6 | _, err := os.Stat(path) 7 | if err == nil { 8 | return true, nil 9 | } 10 | if os.IsNotExist(err) { 11 | return false, nil 12 | } 13 | return false, err 14 | } 15 | -------------------------------------------------------------------------------- /static/rbacmodel/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = r.sub == p.sub && ParamsMatch(r.obj, p.obj) && r.act == p.act 15 | -------------------------------------------------------------------------------- /controller/servers/reportformat.go: -------------------------------------------------------------------------------- 1 | package servers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func ReportFormat(c *gin.Context, success bool, msg string, json gin.H) { 10 | // 开始时间 11 | c.JSON(http.StatusOK, gin.H{ 12 | "success": success, 13 | "msg": msg, 14 | "data": json, 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /model/modelInterface/interface.go: -------------------------------------------------------------------------------- 1 | package modelInterface 2 | 3 | // 因为我也不确定项目要不要多人维护 所以定义了CURD接口 作为接口参考 4 | // 由于很多接口使用Restful模式 暂时不用泛型 有需要可以iss提供示例 5 | 6 | type PageInfo struct { 7 | Page int 8 | PageSize int 9 | } 10 | 11 | //分页接口 12 | type Paging interface { 13 | GetInfoList(PageInfo) (err error, list interface{}, total int) 14 | } 15 | -------------------------------------------------------------------------------- /router/sys_base.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) { 9 | BaseRouter := Router.Group("base") 10 | { 11 | BaseRouter.POST("regist", api.Regist) 12 | BaseRouter.POST("login", api.Login) 13 | } 14 | return BaseRouter 15 | } 16 | -------------------------------------------------------------------------------- /router/sys_jwt.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitJwtRouter(Router *gin.RouterGroup) { 10 | ApiRouter := Router.Group("jwt").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | ApiRouter.POST("jsonInBlacklist", api.JsonInBlacklist) //jwt加入黑名单 13 | } 14 | } -------------------------------------------------------------------------------- /tools/struct_to_map.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "reflect" 4 | 5 | // 利用反射将结构体转化为map 6 | func StructToMap(obj interface{}) map[string]interface{}{ 7 | obj1 := reflect.TypeOf(obj) 8 | obj2 := reflect.ValueOf(obj) 9 | 10 | var data = make(map[string]interface{}) 11 | for i := 0; i < obj1.NumField(); i++ { 12 | data[obj1.Field(i).Name] = obj2.Field(i).Interface() 13 | } 14 | return data 15 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /model/sysModel/sys_workFlowProcess.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import "github.com/jinzhu/gorm" 4 | 5 | // 工作流流转表 6 | type SysWorkFlowProcess struct { 7 | gorm.Model 8 | ApplicationID uint // 当前工作流所属申请的ID 9 | CurrentNode string // 当前进度节点 10 | HistoricalNode string //上一个进度节点 11 | CurrentUser string // 当前进度操作人 12 | HistoricalUser string // 上一个进度的操作人 13 | State bool // 状态 是否是正在进行的状态 14 | } 15 | -------------------------------------------------------------------------------- /router/sys_workflow.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitWorkflowRouter(Router *gin.RouterGroup) { 10 | WorkflowRouter := Router.Group("workflow").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | WorkflowRouter.POST("createWorkFlow", api.CreateWorkFlow) // 创建工作流 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/utils/asyncRouter.js: -------------------------------------------------------------------------------- 1 | const _import = require('./_import') //获取组件的方法 2 | export const asyncRouterHandle = (asyncRouter) => { 3 | asyncRouter.map(item => { 4 | if (item.component) { 5 | item.component = _import(item.component) 6 | } else { 7 | delete item['component'] 8 | } 9 | if (item.children) { 10 | asyncRouterHandle(item.children) 11 | } 12 | }) 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | go.sum 2 | .idea/ 3 | log/ 4 | *.exe 5 | /QMPlusVuePage/node_modules/ 6 | /QMPlusVuePage/package-lock.json 7 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 8 | *.o 9 | *.a 10 | *.so 11 | 12 | # Folders 13 | _obj 14 | _test 15 | 16 | # Architecture specific extensions/prefixes 17 | *.[568vq] 18 | [568vq].out 19 | 20 | *.cgo1.go 21 | *.cgo2.c 22 | _cgo_defun.c 23 | _cgo_gotypes.go 24 | _cgo_export.* 25 | 26 | _testmain.go 27 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/jwt.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | // @Tags jwt 4 | // @Summary jwt加入黑名单 5 | // @Security ApiKeyAuth 6 | // @accept application/json 7 | // @Produce application/json 8 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}" 9 | // @Router /jwt/jsonInBlacklist [post] 10 | 11 | export const jsonInBlacklist = () => { 12 | return service({ 13 | url: "/jwt/jsonInBlacklist", 14 | method: 'post', 15 | }) 16 | } -------------------------------------------------------------------------------- /router/sys_casbin.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitCasbinRouter(Router *gin.RouterGroup) { 10 | BaseRouter := Router.Group("casbin").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | BaseRouter.POST("casbinPUpdata", api.CasbinPUpdata) 13 | BaseRouter.POST("getPolicyPathByAuthorityId", api.GetPolicyPathByAuthorityId) 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/utils/bus.js: -------------------------------------------------------------------------------- 1 | const install = (Vue) => { 2 | const Bus = new Vue({ 3 | methods: { 4 | emit(event, ...args) { 5 | this.$emit(event, ...args) 6 | }, 7 | on(event, cb) { 8 | this.$on(event, cb) 9 | }, 10 | off(event, cb) { 11 | this.$off(event, cb) 12 | } 13 | }, 14 | }) 15 | Vue.prototype.$bus = Bus 16 | } 17 | 18 | export default install -------------------------------------------------------------------------------- /router/sys_system.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitSystemRouter(Router *gin.RouterGroup) { 10 | UserRouter := Router.Group("system").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | UserRouter.POST("getSystemConfig", api.GetSystemConfig) // 获取配置文件内容 13 | UserRouter.POST("setSystemConfig", api.SetSystemConfig) // 设置配置文件内容 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /QMPlusVuePage/README.md: -------------------------------------------------------------------------------- 1 | # qm-plus-vue-page 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /model/sysModel/sys_system.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "nideshop-admin/config" 5 | "nideshop-admin/tools" 6 | ) 7 | 8 | type System struct { 9 | Config config.Config 10 | } 11 | 12 | func (s *System)GetSystemConfig()(err error,conf config.Config){ 13 | return nil,config.GinVueAdminconfig 14 | } 15 | 16 | func (s *System)SetSystemConfig()(err error){ 17 | confs:= tools.StructToMap(s.Config) 18 | for k,v:= range confs { 19 | config.VTool.Set(k,v) 20 | } 21 | err = config.VTool.WriteConfig() 22 | return err 23 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/workflow.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Summary 删除角色 3 | // @Security ApiKeyAuth 4 | // @accept application/json 5 | // @Produce application/json 6 | // @Param data body {authorityId uint} true "删除角色" 7 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 8 | // @Router /authority/deleteAuthority [post] 9 | export const createWorkFlow = (data) => { 10 | return service({ 11 | url: "/workflow/createWorkFlow", 12 | method: 'post', 13 | data 14 | }) 15 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import VuexPersistence from 'vuex-persist' 4 | 5 | import { user } from "@/store/module/user" 6 | import { router } from "@/store/module/router" 7 | 8 | Vue.use(Vuex) 9 | 10 | 11 | 12 | const vuexLocal = new VuexPersistence({ 13 | storage: window.localStorage, 14 | modules: ['user'] 15 | }) 16 | export const store = new Vuex.Store({ 17 | modules: { 18 | user, 19 | router 20 | }, 21 | plugins: [vuexLocal.plugin] 22 | }) -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/layout/aside/asideComponent/menuItem.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | -------------------------------------------------------------------------------- /middleware/loadtls.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/unrolled/secure" 7 | ) 8 | 9 | // 用https把这个中间件在router里面use一下就好 10 | 11 | func LoadTls() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | middleware := secure.New(secure.Options{ 14 | SSLRedirect: true, 15 | SSLHost: "localhost:443", 16 | }) 17 | err := middleware.Process(c.Writer, c.Request) 18 | if err != nil { 19 | //如果出现错误,请不要继续。 20 | fmt.Println(err) 21 | return 22 | } 23 | // 继续往下处理 24 | c.Next() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /controller/servers/paging.go: -------------------------------------------------------------------------------- 1 | package servers 2 | 3 | import ( 4 | "nideshop-admin/init/qmsql" 5 | "nideshop-admin/model/modelInterface" 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | //获取分页功能 接收实现了分页接口的结构体 返回搜索完成的结果 许需要自行scan 或者find 10 | func PagingServer(paging modelInterface.Paging, info modelInterface.PageInfo) (err error, db *gorm.DB, total int) { 11 | limit := info.PageSize 12 | offset := info.PageSize * (info.Page - 1) 13 | err = qmsql.DEFAULTDB.Model(paging).Count(&total).Error 14 | db = qmsql.DEFAULTDB.Limit(limit).Offset(offset).Order("id desc") 15 | return err, db, total 16 | } 17 | -------------------------------------------------------------------------------- /init/registTable/regist_table.go: -------------------------------------------------------------------------------- 1 | package registTable 2 | 3 | import ( 4 | "nideshop-admin/model/dbModel" 5 | "nideshop-admin/model/sysModel" 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | //注册数据库表专用 10 | func RegistTable(db *gorm.DB) { 11 | db.AutoMigrate(sysModel.SysUser{}, 12 | sysModel.SysAuthority{}, 13 | sysModel.SysMenu{}, 14 | sysModel.SysApi{}, 15 | sysModel.SysBaseMenu{}, 16 | sysModel.JwtBlacklist{}, 17 | sysModel.SysWorkflow{}, 18 | sysModel.SysWorkflowStepInfo{}, 19 | dbModel.ExaFileUploadAndDownload{}, 20 | dbModel.ExaFile{}, 21 | dbModel.ExaFileChunk{}, 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /router/sys_user.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitUserRouter(Router *gin.RouterGroup) { 10 | UserRouter := Router.Group("user").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | UserRouter.POST("changePassword", api.ChangePassword) // 修改密码 13 | UserRouter.POST("uploadHeaderImg", api.UploadHeaderImg) //上传头像 14 | UserRouter.POST("getUserList", api.GetUserList) // 分页获取用户列表 15 | UserRouter.POST("setUserAuthority", api.SetUserAuthority) //设置用户权限 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /init/qmsql/initMysql.go: -------------------------------------------------------------------------------- 1 | package qmsql 2 | 3 | import ( 4 | "nideshop-admin/config" 5 | "github.com/jinzhu/gorm" 6 | _ "github.com/jinzhu/gorm/dialects/mysql" 7 | "log" 8 | ) 9 | 10 | var DEFAULTDB *gorm.DB 11 | 12 | //初始化数据库并产生数据库全局变量 13 | func InitMysql(admin config.MysqlAdmin) *gorm.DB { 14 | if db, err := gorm.Open("mysql", admin.Username+":"+admin.Password+"@("+admin.Path+")/"+admin.Dbname+"?"+admin.Config); err != nil { 15 | log.Printf("DEFAULTDB数据库启动异常%S", err) 16 | } else { 17 | DEFAULTDB = db 18 | DEFAULTDB.DB().SetMaxIdleConns(10) 19 | DEFAULTDB.DB().SetMaxOpenConns(100) 20 | } 21 | return DEFAULTDB 22 | } 23 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/layout/aside/asideComponent/asyncSubmenu.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | -------------------------------------------------------------------------------- /QMPlusVuePage/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | qm-plus-vue-page 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /router/sys_authority.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitAuthorityRouter(Router *gin.RouterGroup) { 10 | AuthorityRouter := Router.Group("authority").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | AuthorityRouter.POST("createAuthority", api.CreateAuthority) //创建角色 13 | AuthorityRouter.POST("deleteAuthority", api.DeleteAuthority) //删除角色 14 | AuthorityRouter.POST("getAuthorityList", api.GetAuthorityList) //获取角色列表 15 | AuthorityRouter.POST("setDataAuthority", api.SetDataAuthority) //设置角色资源权限 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /router/sys_api.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitApiRouter(Router *gin.RouterGroup) { 10 | ApiRouter := Router.Group("api").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | ApiRouter.POST("createApi", api.CreateApi) //创建Api 13 | ApiRouter.POST("deleteApi", api.DeleteApi) //删除Api 14 | ApiRouter.POST("getApiList", api.GetApiList) //获取Api列表 15 | ApiRouter.POST("getApiById", api.GetApiById) //获取单条Api消息 16 | ApiRouter.POST("updataApi", api.UpdataApi) //更新api 17 | ApiRouter.POST("getAllApis", api.GetAllApis) // 获取所有api 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /init/initRedis/init_redis.go: -------------------------------------------------------------------------------- 1 | package initRedis 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/config" 6 | "github.com/go-redis/redis" 7 | ) 8 | 9 | var DEFAULTREDIS *redis.Client 10 | 11 | func InitRedis() (client *redis.Client) { 12 | client = redis.NewClient(&redis.Options{ 13 | Addr: config.GinVueAdminconfig.RedisAdmin.Addr, 14 | Password: config.GinVueAdminconfig.RedisAdmin.Password, // no password set 15 | DB: config.GinVueAdminconfig.RedisAdmin.DB, // use default DB 16 | }) 17 | pong, err := client.Ping().Result() 18 | if err != nil { 19 | fmt.Println(pong, err) 20 | } else { 21 | fmt.Println(pong, err) 22 | DEFAULTREDIS = client 23 | } 24 | return client 25 | } 26 | -------------------------------------------------------------------------------- /cmd/windows.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/config" 6 | "github.com/gin-gonic/gin" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | func RunWindowsServer(Router *gin.Engine) { 12 | s := &http.Server{ 13 | Addr: fmt.Sprintf(":%d", config.GinVueAdminconfig.System.Addr), 14 | Handler: Router, 15 | ReadTimeout: 10 * time.Second, 16 | WriteTimeout: 10 * time.Second, 17 | MaxHeaderBytes: 1 << 20, 18 | } 19 | time.Sleep(10 * time.Microsecond) 20 | fmt.Printf(`欢迎使用 Gin-Vue-Admin 21 | 作者:奇淼 And Spike666 22 | 微信:shouzi_1994 23 | 默认自动化文档地址:http://127.0.0.1%s/swagger/index.html 24 | 默认前端文件运行地址:http://127.0.0.1:8080 25 | `, s.Addr) 26 | _ = s.ListenAndServe() 27 | } 28 | -------------------------------------------------------------------------------- /middleware/casbin_rcba.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/sysModel" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | //拦截器 11 | func CasbinHandler() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | claims, _ := c.Get("claims") 14 | waitUse := claims.(*CustomClaims) 15 | //获取请求的URI 16 | obj := c.Request.URL.RequestURI() 17 | //获取请求方法 18 | act := c.Request.Method 19 | //获取用户的角色 20 | sub := waitUse.AuthorityId 21 | e := sysModel.Casbin() 22 | //判断策略中是否存在 23 | if e.Enforce(sub, obj, act) { 24 | c.Next() 25 | } else { 26 | servers.ReportFormat(c, false, fmt.Sprintf("权限不足"), gin.H{}) 27 | c.Abort() 28 | return 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /controller/api/sys_workFlow.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/sysModel" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // @Tags workflow 11 | // @Summary 注册工作流 12 | // @Produce application/json 13 | // @Param data body sysModel.SysWorkflow true "注册工作流接口" 14 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"注册成功"}" 15 | // @Router /workflow/createWorkFlow [post] 16 | func CreateWorkFlow(c *gin.Context) { 17 | var wk sysModel.SysWorkflow 18 | _ = c.ShouldBind(&wk) 19 | err := wk.Create() 20 | if err != nil { 21 | servers.ReportFormat(c, false, fmt.Sprintf("获取失败:%v", err), gin.H{}) 22 | } else { 23 | servers.ReportFormat(c, true, "获取成功", gin.H{}) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /static/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "casbinconfig": { 3 | "modelPath": "./static/rbacmodel/rbac_model.conf" 4 | }, 5 | "jwt": { 6 | "signingKey": "nideshop" 7 | }, 8 | "mysqladmin": { 9 | "username": "root", 10 | "password": "xwkj123", 11 | "path": "127.0.0.1:3306", 12 | "dbname": "nideshop", 13 | "config": "charset=utf8mb4\u0026parseTime=True\u0026loc=Local" 14 | }, 15 | "upload": { 16 | "domain": "http://localhost:8080/", 17 | "path": "B:/gopath/src/nideshop-admin/QMPlusVuePage/public/" 18 | }, 19 | "redisadmin": { 20 | "addr": "127.0.0.1:6379", 21 | "password": "xwkj123", 22 | "db": 0 23 | }, 24 | "system": { 25 | "useMultipoint": false, 26 | "env": "develop", 27 | "addr": 8888 28 | } 29 | } -------------------------------------------------------------------------------- /middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | // 处理跨域请求,支持options访问 9 | func Cors() gin.HandlerFunc { 10 | return func(c *gin.Context) { 11 | method := c.Request.Method 12 | c.Header("Access-Control-Allow-Origin", "*") 13 | c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token") 14 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") 15 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") 16 | c.Header("Access-Control-Allow-Credentials", "true") 17 | 18 | //放行所有OPTIONS方法 19 | if method == "OPTIONS" { 20 | c.AbortWithStatus(http.StatusNoContent) 21 | } 22 | // 处理请求 23 | c.Next() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /controller/api/sys_jwt_blacklist.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/sysModel" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // @Tags jwt 11 | // @Summary jwt加入黑名单 12 | // @Security ApiKeyAuth 13 | // @accept application/json 14 | // @Produce application/json 15 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}" 16 | // @Router /jwt/jsonInBlacklist [post] 17 | func JsonInBlacklist(c *gin.Context){ 18 | token := c.Request.Header.Get("x-token") 19 | ModelJwt := sysModel.JwtBlacklist{ 20 | Jwt:token, 21 | } 22 | err := ModelJwt.JsonInBlacklist() 23 | if err != nil { 24 | servers.ReportFormat(c, false, fmt.Sprintf("jwt作废失败,%v", err), gin.H{}) 25 | } else { 26 | servers.ReportFormat(c, true, "jwt作废成功", gin.H{}) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/utils/downloadImg.js: -------------------------------------------------------------------------------- 1 | export const downloadImage = (imgsrc, name) => { //下载图片地址和图片名 2 | var image = new Image(); 3 | image.setAttribute("crossOrigin", "anonymous"); 4 | image.onload = function() { 5 | var canvas = document.createElement("canvas"); 6 | canvas.width = image.width; 7 | canvas.height = image.height; 8 | var context = canvas.getContext("2d"); 9 | context.drawImage(image, 0, 0, image.width, image.height); 10 | var url = canvas.toDataURL("image/png"); //得到图片的base64编码数据 11 | 12 | var a = document.createElement("a"); // 生成一个a元素 13 | var event = new MouseEvent("click"); // 创建一个单击事件 14 | a.download = name || "photo"; // 设置图片名称 15 | a.href = url; // 将生成的URL设置为a.href属性 16 | a.dispatchEvent(event); // 触发a的单击事件 17 | }; 18 | image.src = imgsrc; 19 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | const baseRouters = [{ 7 | path: '/', 8 | redirect: '/login' 9 | }, 10 | { 11 | path: '/login', 12 | name: 'login', 13 | component: () => 14 | import ('@/view/login/login.vue') 15 | }, 16 | { 17 | path: '/regist', 18 | name: 'regist', 19 | component: () => 20 | import ('@/view/login/regist.vue') 21 | }, 22 | { 23 | path: "/404", 24 | name: "404", 25 | component: () => 26 | import ('@/view/error/index.vue') 27 | } 28 | ] 29 | 30 | // 需要通过后台数据来生成的组件 31 | 32 | const createRouter = () => new Router({ 33 | routes: baseRouters 34 | }) 35 | 36 | const router = createRouter() 37 | 38 | export default router -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/system.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | // @Tags systrm 4 | // @Summary 获取配置文件内容 5 | // @Security ApiKeyAuth 6 | // @Produce application/json 7 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 8 | // @Router /system/getSystemConfig [post] 9 | export const getSystemConfig = () => { 10 | return service({ 11 | url: "/system/getSystemConfig", 12 | method: 'post', 13 | }) 14 | } 15 | 16 | // @Tags system 17 | // @Summary 设置配置文件内容 18 | // @Security ApiKeyAuth 19 | // @Produce application/json 20 | // @Param data body sysModel.System true 21 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 22 | // @Router /system/setSystemConfig [post] 23 | export const setSystemConfig = (data) => { 24 | return service({ 25 | url: "/system/setSystemConfig", 26 | method: 'post', 27 | data 28 | }) 29 | } -------------------------------------------------------------------------------- /tools/hasGap.go: -------------------------------------------------------------------------------- 1 | // 空值校验工具 仅用于检验空字符串 其余类型请勿使用 2 | 3 | package tools 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "reflect" 9 | ) 10 | 11 | func HasGap(input interface{}) error { 12 | getType := reflect.TypeOf(input) 13 | getValue := reflect.ValueOf(input) 14 | // 获取方法字段 15 | for i := 0; i < getType.NumField(); i++ { 16 | field := getType.Field(i) 17 | value := getValue.Field(i).Interface() 18 | switch value.(type) { 19 | case string: 20 | if value == "" { 21 | fmt.Printf("%s为空", field.Name) 22 | return errors.New(fmt.Sprintf("%s为空", field.Name)) 23 | } 24 | default: 25 | if value == nil { 26 | fmt.Printf("%s为空", field.Name) 27 | return errors.New(fmt.Sprintf("%s为空", field.Name)) 28 | } 29 | } 30 | } 31 | // 获取方法 32 | // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历 33 | //for i := 0; i < getType.NumMethod(); i++ { 34 | // m := getType.Method(i) 35 | // fmt.Printf("%s: %v\n", m.Name, m.Type) 36 | //} 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/components/mixins/infoList.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | page: 1, 5 | total: 10, 6 | pageSize: 10, 7 | tableData: [], 8 | searchInfo: {} 9 | } 10 | }, 11 | methods: { 12 | handleSizeChange(val) { 13 | this.pageSize = val 14 | this.getTableData() 15 | }, 16 | handleCurrentChange(val) { 17 | this.page = val 18 | this.getTableData() 19 | }, 20 | async getTableData(page = this.page, pageSize = this.pageSize) { 21 | const table = await this.listApi({ page, pageSize, ...this.searchInfo }) 22 | this.tableData = table.data[this.listKey] 23 | this.total = table.data.total 24 | this.page = table.data.page 25 | this.pageSize = table.data.pageSize 26 | } 27 | }, 28 | mounted() { 29 | this.getTableData() 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /model/sysModel/sys_jwt_blacklist.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "nideshop-admin/init/initRedis" 5 | "nideshop-admin/init/qmsql" 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | type JwtBlacklist struct { 10 | gorm.Model 11 | Jwt string `gorm:"type:text"` 12 | } 13 | 14 | func (j *JwtBlacklist) JsonInBlacklist() (err error) { 15 | err = qmsql.DEFAULTDB.Create(j).Error 16 | return 17 | } 18 | 19 | //判断JWT是否在黑名单内部 20 | func (j *JwtBlacklist) IsBlacklist(Jwt string) bool { 21 | isNotFound := qmsql.DEFAULTDB.Where("jwt = ?", Jwt).First(j).RecordNotFound() 22 | return !isNotFound 23 | } 24 | 25 | //判断当前用户是否在线 26 | func (j *JwtBlacklist) GetRedisJWT(userName string) (err error, RedisJWT string) { 27 | RedisJWT, err = initRedis.DEFAULTREDIS.Get(userName).Result() 28 | return err, RedisJWT 29 | } 30 | 31 | //设置当前用户在线 32 | func (j *JwtBlacklist) SetRedisJWT(userName string) (err error) { 33 | err = initRedis.DEFAULTREDIS.Set(userName, j.Jwt, 1000*1000*1000*60*60*24*7).Err() 34 | return err 35 | } 36 | -------------------------------------------------------------------------------- /tools/des.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "bytes" 5 | "crypto/cipher" 6 | "crypto/des" 7 | ) 8 | 9 | func padding(src []byte, blocksize int) []byte { 10 | n := len(src) 11 | padnum := blocksize - n%blocksize 12 | pad := bytes.Repeat([]byte{byte(padnum)}, padnum) 13 | dst := append(src, pad...) 14 | return dst 15 | } 16 | 17 | func unpadding(src []byte) []byte { 18 | n := len(src) 19 | unpadnum := int(src[n-1]) 20 | dst := src[:n-unpadnum] 21 | return dst 22 | } 23 | 24 | func EncryptDES(src []byte) []byte { 25 | key := []byte("qimiao66") 26 | block, _ := des.NewCipher(key) 27 | src = padding(src, block.BlockSize()) 28 | blockmode := cipher.NewCBCEncrypter(block, key) 29 | blockmode.CryptBlocks(src, src) 30 | return src 31 | } 32 | 33 | func DecryptDES(src []byte) []byte { 34 | key := []byte("qimiao66") 35 | block, _ := des.NewCipher(key) 36 | blockmode := cipher.NewCBCDecrypter(block, key) 37 | blockmode.CryptBlocks(src, src) 38 | src = unpadding(src) 39 | return src 40 | 41 | } 42 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/layout/aside/asideComponent/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 38 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/style/base.scss: -------------------------------------------------------------------------------- 1 | .clearflex { 2 | *zoom: 1; 3 | } 4 | 5 | .clearflex:after { 6 | content: ''; 7 | display: block; 8 | height: 0; 9 | visibility: hidden; 10 | clear: both; 11 | } 12 | 13 | .fl-left { 14 | float: left; 15 | } 16 | 17 | .fl-right { 18 | float: right; 19 | } 20 | 21 | .mg { 22 | margin: 10px !important; 23 | } 24 | 25 | .left-mg-xs { 26 | margin-left: 6px !important; 27 | } 28 | 29 | .left-mg-sm { 30 | margin-left: 10px !important; 31 | } 32 | 33 | .left-mg-md { 34 | margin-left: 14px !important; 35 | } 36 | 37 | .top-mg-lg { 38 | margin-top: 20px !important; 39 | } 40 | 41 | .tb-mg-lg { 42 | margin: 20px 0 !important; 43 | } 44 | 45 | .bottom-mg-lg { 46 | margin-bottom: 20px !important; 47 | } 48 | 49 | .left-mg-lg { 50 | margin-left: 18px !important; 51 | } 52 | 53 | .title-1 { 54 | text-align: center; 55 | font-size: 32px; 56 | margin-bottom: 32px; 57 | } 58 | 59 | .title-3 { 60 | text-align: center; 61 | } -------------------------------------------------------------------------------- /router/sys_menu.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "nideshop-admin/middleware" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) { 10 | MenuRouter := Router.Group("menu").Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | MenuRouter.POST("getMenu", api.GetMenu) //获取菜单树 13 | MenuRouter.POST("getMenuList", api.GetMenuList) // 分页获取基础menu列表 14 | MenuRouter.POST("addBaseMenu", api.AddBaseMenu) // 新增菜单 15 | MenuRouter.POST("getBaseMenuTree", api.GetBaseMenuTree) // 获取用户动态路由 16 | MenuRouter.POST("addMenuAuthority", api.AddMenuAuthority) // 增加menu和角色关联关系 17 | MenuRouter.POST("getMenuAuthority", api.GetMenuAuthority) // 获取指定角色menu 18 | MenuRouter.POST("deleteBaseMenu", api.DeleteBaseMenu) // 删除菜单 19 | MenuRouter.POST("updataBaseMenu", api.UpdataBaseMenu) // 更新菜单 20 | MenuRouter.POST("getBaseMenuById", api.GetBaseMenuById) //根据id获取菜单 21 | } 22 | return MenuRouter 23 | } 24 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | // 引入element 4 | import ElementUI from 'element-ui'; 5 | import 'element-ui/lib/theme-chalk/index.css'; 6 | Vue.use(ElementUI); 7 | // 引入封装的router 8 | import router from '@/router/index' 9 | 10 | // canvas背景插件 11 | import vueParticleLine from 'vue-particle-line' 12 | import 'vue-particle-line/dist/vue-particle-line.css' 13 | Vue.use(vueParticleLine) 14 | 15 | // 富文本插件 16 | import VueQuillEditor from 'vue-quill-editor' 17 | import 'quill/dist/quill.core.css' 18 | import 'quill/dist/quill.snow.css' 19 | import 'quill/dist/quill.bubble.css' 20 | 21 | Vue.use(VueQuillEditor) 22 | 23 | // markdown插件 24 | import mavonEditor from 'mavon-editor' 25 | import 'mavon-editor/dist/css/index.css' 26 | 27 | Vue.use(mavonEditor) 28 | 29 | import '@/permission' 30 | import { store } from '@/store/index' 31 | Vue.config.productionTip = false 32 | 33 | // 路由守卫 34 | import Bus from '@/utils/bus.js' 35 | Vue.use(Bus) 36 | new Vue({ 37 | render: h => h(App), 38 | router, 39 | store 40 | }).$mount('#app') -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/error/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/fileUploadAndDownload.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags FileUploadAndDownload 3 | // @Summary 分页文件列表 4 | // @Security ApiKeyAuth 5 | // @accept application/json 6 | // @Produce application/json 7 | // @Param data body modelInterface.PageInfo true "分页获取文件户列表" 8 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 9 | // @Router /fileUploadAndDownload/getFileList [post] 10 | export const getFileList = (data) => { 11 | return service({ 12 | url: "/fileUploadAndDownload/getFileList", 13 | method: "post", 14 | data 15 | }) 16 | } 17 | 18 | // @Tags FileUploadAndDownload 19 | // @Summary 删除文件 20 | // @Security ApiKeyAuth 21 | // @Produce application/json 22 | // @Param data body dbModel.FileUploadAndDownload true "传入文件里面id即可" 23 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"返回成功"}" 24 | // @Router /fileUploadAndDownload/deleteFile [post] 25 | export const deleteFile = (data) => { 26 | return service({ 27 | url: "/fileUploadAndDownload/deleteFile", 28 | method: "post", 29 | data 30 | }) 31 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/casbin.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | // @Tags authority 4 | // @Summary 更改角色api权限 5 | // @Security ApiKeyAuth 6 | // @accept application/json 7 | // @Produce application/json 8 | // @Param data body api.CreateAuthorityPatams true "更改角色api权限" 9 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 10 | // @Router /casbin/casbinPUpdata [post] 11 | 12 | export const casbinPUpdata = (data) => { 13 | return service({ 14 | url: "/casbin/casbinPUpdata", 15 | method: 'post', 16 | data 17 | }) 18 | } 19 | 20 | 21 | // @Tags casbin 22 | // @Summary 获取权限列表 23 | // @Security ApiKeyAuth 24 | // @accept application/json 25 | // @Produce application/json 26 | // @Param data body api.CreateAuthorityPatams true "获取权限列表" 27 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 28 | // @Router /casbin/getPolicyPathByAuthorityId [post] 29 | export const getPolicyPathByAuthorityId = (data) => { 30 | return service({ 31 | url: "/casbin/getPolicyPathByAuthorityId", 32 | method: 'post', 33 | data 34 | }) 35 | } -------------------------------------------------------------------------------- /router/exp_fileUploadAndDownload.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "nideshop-admin/controller/api" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func InitFileUploadAndDownloadRouter(Router *gin.RouterGroup) { 9 | FileUploadAndDownloadGroup := Router.Group("fileUploadAndDownload") 10 | //.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) 11 | { 12 | FileUploadAndDownloadGroup.POST("/upload", api.UploadFile) // 上传文件 13 | FileUploadAndDownloadGroup.POST("/getFileList", api.GetFileList) // 获取上传文件列表 14 | FileUploadAndDownloadGroup.POST("/deleteFile", api.DeleteFile) // 删除指定文件 15 | FileUploadAndDownloadGroup.POST("/breakpointContinue", api.BreakpointContinue) // 断点续传 16 | FileUploadAndDownloadGroup.GET("/findFile", api.FindFile) // 查询当前文件成功的切片 17 | FileUploadAndDownloadGroup.POST("/breakpointContinueFinish", api.BreakpointContinueFinish) // 查询当前文件成功的切片 18 | FileUploadAndDownloadGroup.POST("/removeChunk", api.RemoveChunk) // 查询当前文件成功的切片 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /model/sysModel/sys_worfFlow.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "nideshop-admin/init/qmsql" 5 | "github.com/jinzhu/gorm" 6 | ) 7 | 8 | //工作流属性表 9 | type SysWorkflow struct { 10 | gorm.Model 11 | WorkflowNickName string `json:"workflowNickName"` // 工作流名称 12 | WorkflowName string `json:"workflowName"` // 工作流英文id 13 | WorkflowDescription string `json:"workflowDescription"` // 工作流描述 14 | WorkflowStepInfo []SysWorkflowStepInfo `json:"workflowStep"` // 工作流步骤 15 | } 16 | 17 | // 工作流状态表 18 | type SysWorkflowStepInfo struct { 19 | gorm.Model 20 | SysWorkflowID uint `json:"workflowID"` // 所属工作流ID 21 | IsStrat bool `json:"isStrat"` // 是否是开始流节点 22 | StepName string `json:"stepName"` // 工作流名称 23 | StepNo float64 `json:"stepNo"` // 步骤id (第几步) 24 | StepAuthorityID string `json:"stepAuthorityID"` // 操作者级别id 25 | IsEnd bool `json:"isEnd"` // 是否是完结流节点 26 | } 27 | 28 | //创建工作流 29 | func (wk *SysWorkflow) Create() error { 30 | err := qmsql.DEFAULTDB.Create(&wk).Error 31 | return err 32 | } 33 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/utils/data.js: -------------------------------------------------------------------------------- 1 | // 对Date的扩展,将 Date 转化为指定格式的String 2 | // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 3 | // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 4 | // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 5 | // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 6 | Date.prototype.Format = function(fmt) { 7 | var o = { 8 | "M+": this.getMonth() + 1, //月份 9 | "d+": this.getDate(), //日 10 | "h+": this.getHours(), //小时 11 | "m+": this.getMinutes(), //分 12 | "s+": this.getSeconds(), //秒 13 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 14 | "S": this.getMilliseconds() //毫秒 15 | }; 16 | if (/(y+)/.test(fmt)) 17 | fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 18 | for (var k in o) 19 | if (new RegExp("(" + k + ")").test(fmt)) 20 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 21 | return fmt; 22 | } 23 | 24 | export function formatTimeToStr(times, pattern) { 25 | var d = new Date(times).Format("yyyy-MM-dd hh:mm:ss"); 26 | if (pattern) { 27 | d = new Date(times).Format(pattern); 28 | } 29 | return d.toLocaleString(); 30 | } -------------------------------------------------------------------------------- /init/qmlog/qmlog.go: -------------------------------------------------------------------------------- 1 | package qmlog 2 | 3 | // 日志初始化包 调用qmlog.QMLog.Info 记录日志 24小时切割 日志保存7天 可自行设置 4 | import ( 5 | "fmt" 6 | "nideshop-admin/tools" 7 | rotatelogs "github.com/lestrrat/go-file-rotatelogs" 8 | "github.com/rifflock/lfshook" 9 | "github.com/sirupsen/logrus" 10 | "os" 11 | "time" 12 | ) 13 | 14 | var QMLog = logrus.New() 15 | 16 | //禁止logrus的输出 17 | func InitLog() *logrus.Logger { 18 | src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend) 19 | if err != nil { 20 | fmt.Println("err", err) 21 | } 22 | QMLog.Out = src 23 | QMLog.SetLevel(logrus.DebugLevel) 24 | if ok, _ := tools.PathExists("./log"); !ok { 25 | // Directory not exist 26 | fmt.Println("Create log.") 27 | _ = os.Mkdir("log", os.ModePerm) 28 | } 29 | apiLogPath := "./log/api.log" 30 | logWriter, err := rotatelogs.New( 31 | apiLogPath+".%Y-%m-%d-%H-%M.log", 32 | rotatelogs.WithLinkName(apiLogPath), // 生成软链,指向最新日志文件 33 | rotatelogs.WithMaxAge(7*24*time.Hour), // 文件最大保存时间 34 | rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔 35 | ) 36 | writeMap := lfshook.WriterMap{ 37 | logrus.InfoLevel: logWriter, 38 | logrus.FatalLevel: logWriter, 39 | } 40 | lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{}) 41 | QMLog.AddHook(lfHook) 42 | return QMLog 43 | } 44 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "nideshop-admin/cmd" 5 | "nideshop-admin/config" 6 | "nideshop-admin/init/initRedis" 7 | "nideshop-admin/init/initRouter" 8 | "nideshop-admin/init/qmlog" 9 | "nideshop-admin/init/qmsql" 10 | "nideshop-admin/init/registTable" 11 | //"runtime" 12 | ) 13 | 14 | // @title Swagger Example API 15 | // @version 0.0.1 16 | // @description This is a sample Server pets 17 | // @securityDefinitions.apikey ApiKeyAuth 18 | // @in header 19 | // @name x-token 20 | // @BasePath / 21 | 22 | func main() { 23 | 24 | qmlog.InitLog() // 初始化日志 25 | db := qmsql.InitMysql(config.GinVueAdminconfig.MysqlAdmin) // 链接初始化数据库 26 | if config.GinVueAdminconfig.System.UseMultipoint { 27 | _ = initRedis.InitRedis() // 初始化redis服务 28 | } 29 | registTable.RegistTable(db) // 注册数据库表 30 | defer qmsql.DEFAULTDB.Close() // 程序结束前关闭数据库链接 31 | Router := initRouter.InitRouter() // 注册路由 32 | qmlog.QMLog.Info("服务器开启") // 日志测试代码 33 | //Router.RunTLS(":443","ssl.pem", "ssl.key") // https支持 需要添加中间件 34 | //sysType := runtime.GOOS 35 | // 36 | //if sysType == "linux" { 37 | // // LINUX系统 38 | // // 借助endless开发无感知重启后台 以及前端接口重启后台功能 39 | //} 40 | //if sysType == "windows" { 41 | // WIN系统 42 | cmd.RunWindowsServer(Router) 43 | //} 44 | } 45 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/dashbord/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 46 | 47 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/example/rte/rte.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 50 | -------------------------------------------------------------------------------- /init/initRouter/init_router.go: -------------------------------------------------------------------------------- 1 | package initRouter 2 | 3 | import ( 4 | _ "nideshop-admin/docs" 5 | "nideshop-admin/middleware" 6 | "nideshop-admin/router" 7 | "github.com/gin-gonic/gin" 8 | "github.com/swaggo/gin-swagger" 9 | "github.com/swaggo/gin-swagger/swaggerFiles" 10 | ) 11 | 12 | //初始化总路由 13 | func InitRouter() *gin.Engine { 14 | var Router = gin.Default() 15 | //Router.Use(middleware.LoadTls()) // 打开就能玩https了 16 | Router.Use(middleware.Logger()) // 如果不需要日志 请关闭这里 17 | Router.Use(middleware.Cors()) // 跨域 18 | Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 19 | ApiGroup := Router.Group("") // 方便统一添加路由组前缀 多服务器上线使用 20 | //Router.Use(middleware.Logger()) 21 | router.InitUserRouter(ApiGroup) // 注册用户路由 22 | router.InitBaseRouter(ApiGroup) // 注册基础功能路由 不做鉴权 23 | router.InitMenuRouter(ApiGroup) // 注册menu路由 24 | router.InitAuthorityRouter(ApiGroup) // 注册角色路由 25 | router.InitApiRouter(ApiGroup) // 注册功能api路由 26 | router.InitFileUploadAndDownloadRouter(ApiGroup) // 文件上传下载功能路由 27 | router.InitWorkflowRouter(ApiGroup) // 工作流相关路由 28 | router.InitCasbinRouter(ApiGroup) // 权限相关路由 29 | router.InitJwtRouter(ApiGroup) // jwt相关路由 30 | router.InitSystemRouter(ApiGroup) // system相关路由 31 | return Router 32 | } 33 | -------------------------------------------------------------------------------- /model/dbModel/exa_fileUploadAndDownload.go: -------------------------------------------------------------------------------- 1 | package dbModel 2 | 3 | import ( 4 | "nideshop-admin/controller/servers" 5 | "nideshop-admin/init/qmsql" 6 | "nideshop-admin/model/modelInterface" 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | type ExaFileUploadAndDownload struct { 11 | gorm.Model 12 | Name string `json:"name"` 13 | Url string `json:"url"` 14 | Tag string `json:"tag"` 15 | Key string `json:"key"` 16 | } 17 | 18 | func (f *ExaFileUploadAndDownload) Upload() error { 19 | err := qmsql.DEFAULTDB.Create(f).Error 20 | return err 21 | } 22 | 23 | func (f *ExaFileUploadAndDownload) DeleteFile() error { 24 | err := qmsql.DEFAULTDB.Where("id = ?", f.ID).Unscoped().Delete(f).Error 25 | return err 26 | } 27 | 28 | func (f *ExaFileUploadAndDownload) FindFile() (error, ExaFileUploadAndDownload) { 29 | var file ExaFileUploadAndDownload 30 | err := qmsql.DEFAULTDB.Where("id = ?", f.ID).First(&file).Error 31 | return err, file 32 | } 33 | 34 | // 分页获取数据 需要分页实现这个接口即可 35 | func (f *ExaFileUploadAndDownload) GetInfoList(info modelInterface.PageInfo) (err error, list interface{}, total int) { 36 | // 封装分页方法 调用即可 传入 当前的结构体和分页信息 37 | err, db, total := servers.PagingServer(f, info) 38 | if err != nil { 39 | return 40 | } else { 41 | var fileLists []ExaFileUploadAndDownload 42 | err = db.Order("updated_at desc").Find(&fileLists).Error 43 | return err, fileLists, total 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/store/module/router.js: -------------------------------------------------------------------------------- 1 | import { asyncRouterHandle } from '@/utils/asyncRouter'; 2 | 3 | import { asyncMenu } from '@/api/menu' 4 | 5 | export const router = { 6 | namespaced: true, 7 | state: { 8 | asyncRouters: [] 9 | }, 10 | mutations: { 11 | // 设置动态路由 12 | setAsyncRouter(state, asyncRouters) { 13 | state.asyncRouters = asyncRouters 14 | } 15 | }, 16 | actions: { 17 | // 从后台获取动态路由 18 | async SetAsyncRouter({ commit }) { 19 | const baseRouter = [{ 20 | path: '/layout', 21 | name: 'layout', 22 | component: "view/layout/index.vue", 23 | meta: { 24 | title: "底层layout" 25 | }, 26 | children: [] 27 | }] 28 | const asyncRouterRes = await asyncMenu() 29 | const asyncRouter = asyncRouterRes.data.menus 30 | baseRouter[0].children = asyncRouter 31 | baseRouter.push({ 32 | path: '*', 33 | redirect: '/404' 34 | 35 | }) 36 | asyncRouterHandle(baseRouter) 37 | commit('setAsyncRouter', baseRouter) 38 | return true 39 | } 40 | }, 41 | getters: { 42 | // 获取动态路由 43 | asyncRouters(state) { 44 | return state.asyncRouters 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module nideshop-admin 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc 7 | github.com/casbin/casbin v1.9.1 8 | github.com/casbin/gorm-adapter v1.0.0 9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 10 | github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect 11 | github.com/fsnotify/fsnotify v1.4.7 12 | github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 13 | github.com/gin-gonic/gin v1.4.0 14 | github.com/go-redis/redis v6.15.6+incompatible 15 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect 16 | github.com/jinzhu/gorm v1.9.10 17 | github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect 18 | github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f 19 | github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect 20 | github.com/pkg/errors v0.8.1 21 | github.com/qiniu/api.v7 v7.2.5+incompatible 22 | github.com/qiniu/x v7.0.8+incompatible // indirect 23 | github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 24 | github.com/satori/go.uuid v1.2.0 25 | github.com/sirupsen/logrus v1.2.0 26 | github.com/spf13/viper v1.4.0 27 | github.com/swaggo/gin-swagger v1.2.0 28 | github.com/swaggo/swag v1.5.1 29 | github.com/tebeka/strftime v0.1.3 // indirect 30 | github.com/unrolled/secure v1.0.6 31 | qiniupkg.com/x v7.0.8+incompatible // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /controller/api/sys_casbin.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/sysModel" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // @Tags casbin 11 | // @Summary 更改角色api权限 12 | // @Security ApiKeyAuth 13 | // @accept application/json 14 | // @Produce application/json 15 | // @Param data body sysModel.CasbinInReceive true "更改角色api权限" 16 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 17 | // @Router /casbin/casbinPUpdata [post] 18 | func CasbinPUpdata(c *gin.Context) { 19 | var cmr sysModel.CasbinInReceive 20 | _ = c.ShouldBind(&cmr) 21 | err := new(sysModel.CasbinModel).CasbinPUpdata(cmr.AuthorityId, cmr.CasbinInfos) 22 | if err != nil { 23 | servers.ReportFormat(c, false, fmt.Sprintf("添加规则失败,%v", err), gin.H{}) 24 | } else { 25 | servers.ReportFormat(c, true, "添加规则成功", gin.H{}) 26 | } 27 | } 28 | 29 | // @Tags casbin 30 | // @Summary 获取权限列表 31 | // @Security ApiKeyAuth 32 | // @accept application/json 33 | // @Produce application/json 34 | // @Param data body api.CreateAuthorityParams true "获取权限列表" 35 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 36 | // @Router /casbin/getPolicyPathByAuthorityId [post] 37 | func GetPolicyPathByAuthorityId(c *gin.Context) { 38 | var cmr sysModel.CasbinInReceive 39 | _ = c.ShouldBind(&cmr) 40 | paths := new(sysModel.CasbinModel).GetPolicyPathByAuthorityId(cmr.AuthorityId) 41 | servers.ReportFormat(c, true, "获取规则成功", gin.H{"paths": paths}) 42 | } 43 | -------------------------------------------------------------------------------- /controller/servers/upload.go: -------------------------------------------------------------------------------- 1 | package servers 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "mime/multipart" 7 | "nideshop-admin/config" 8 | "nideshop-admin/tools" 9 | "os" 10 | "time" 11 | ) 12 | 13 | var domain string = config.GinVueAdminconfig.UpLoad.Domain // 访问的域名设置,为了在数据库中储存使用 14 | var filepath string = config.GinVueAdminconfig.UpLoad.Path // 你在七牛云的secretKey 这里是我个人测试号的key 仅供测试使用 恳请大家不要乱传东西 15 | 16 | // 接收两个参数 一个文件流 一个 bucket 你的七牛云标准空间的名字 17 | func Upload(file *multipart.FileHeader, bucket string) (err error, path string, key string) { 18 | f, e := file.Open() 19 | if e != nil { 20 | fmt.Println(e) 21 | return e, "", "" 22 | } 23 | 24 | suffix := tools.GetSuffix(file.Filename) 25 | mscond := int(time.Now().UnixNano()/1000000) - 1581341955458 26 | fileKey := fmt.Sprintf("%d%s", mscond, suffix) // 文件名格式 自己可以改 建议保证唯一性 27 | dst := filepath + bucket + "/" + fileKey //本地服务器文件保存位置 28 | defer f.Close() 29 | //创建 dst 文件 30 | err = os.MkdirAll(filepath+bucket, 0755) 31 | if err != nil { 32 | return err, "", "" 33 | } 34 | out, err := os.Create(dst) 35 | if err != nil { 36 | return err, "", "" 37 | } 38 | defer out.Close() 39 | // 拷贝文件 40 | _, err = io.Copy(out, f) 41 | if err != nil { 42 | return err, "", "" 43 | } 44 | return err, domain + bucket + "/" + fileKey, fileKey 45 | } 46 | 47 | func DeleteFile(bucket string, key string) error { 48 | 49 | src := filepath + bucket + "/" + key 50 | err := os.Remove(src) 51 | if err != nil { 52 | fmt.Println(err) 53 | return err 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "nideshop-admin/init/qmlog" 6 | "net/http/httputil" 7 | "strings" 8 | "time" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func Logger() gin.HandlerFunc { 14 | log := qmlog.QMLog 15 | return func(c *gin.Context) { 16 | // request time 17 | start := time.Now() 18 | // request path 19 | path := c.Request.URL.Path 20 | logFlag := true 21 | if strings.Contains(path, "swagger") { 22 | logFlag = false 23 | } 24 | // request ip 25 | clientIP := c.ClientIP() 26 | // method 27 | method := c.Request.Method 28 | // copy request content 29 | req, _ := httputil.DumpRequest(c.Request, true) 30 | if logFlag { 31 | log.Infof(`| %s | %s | %s | %5s | %s\n`, 32 | `Request :`, method, clientIP, path, string(req)) 33 | } 34 | // replace writer 35 | cusWriter := &responseBodyWriter{ 36 | ResponseWriter: c.Writer, 37 | body: bytes.NewBufferString(""), 38 | } 39 | c.Writer = cusWriter 40 | // handle request 41 | c.Next() 42 | // ending time 43 | end := time.Now() 44 | //execute time 45 | latency := end.Sub(start) 46 | statusCode := c.Writer.Status() 47 | if logFlag { 48 | log.Infof(`| %s | %3d | %13v | %s \n`, 49 | `Response:`, 50 | statusCode, 51 | latency, 52 | cusWriter.body.String()) 53 | } 54 | } 55 | } 56 | 57 | type responseBodyWriter struct { 58 | gin.ResponseWriter 59 | body *bytes.Buffer 60 | } 61 | 62 | func (w responseBodyWriter) Write(b []byte) (int, error) { 63 | w.body.Write(b) 64 | return w.ResponseWriter.Write(b) 65 | } 66 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/permission.js: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import { store } from '@/store/index' 3 | 4 | let asyncRouterFlag = 0 5 | 6 | const whiteList = ['login', 'regist'] 7 | 8 | router.beforeEach(async(to, from, next) => { 9 | const token = store.getters['user/token'] 10 | // if (token) { 11 | // const expiresAt = store.getters['user/expiresAt'] 12 | // const nowUnix = new Date().getTime() 13 | // const hasExpires = (expiresAt - nowUnix) < 0 14 | // if (hasExpires) { 15 | // store.dispatch['user/claerAll'] 16 | // } 17 | // } 18 | // 在白名单中的判断情况 19 | if (whiteList.indexOf(to.name) > -1) { 20 | if (token) { 21 | next({ path: '/layout/dashbord' }) 22 | } else { 23 | next() 24 | } 25 | } else { 26 | // 不在白名单中并且已经登陆的时候 27 | if (token) { 28 | // 添加flag防止多次获取动态路由和栈溢出 29 | if (!asyncRouterFlag) { 30 | asyncRouterFlag++ 31 | await store.dispatch('router/SetAsyncRouter') 32 | const asyncRouters = store.getters['router/asyncRouters'] 33 | router.addRoutes(asyncRouters) 34 | next({...to, replace: true }) 35 | } else { 36 | next() 37 | } 38 | } 39 | // 不在白名单中并且未登陆的时候 40 | if (!token) { 41 | next({ 42 | name: "login", 43 | query: { 44 | redirect: document.location.hash 45 | } 46 | }) 47 | } 48 | } 49 | }) -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/user.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | // @Summary 用户登录 4 | // @Produce application/json 5 | // @Param data body {username:"string",password:"string"} 6 | // @Router /base/login [post] 7 | export const login = (data) => { 8 | return service({ 9 | url: "/base/login", 10 | method: 'post', 11 | data: data 12 | }) 13 | } 14 | 15 | // @Summary 用户注册 16 | // @Produce application/json 17 | // @Param data body {username:"string",password:"string"} 18 | // @Router /base/resige [post] 19 | export const regist = (data) => { 20 | return service({ 21 | url: "/base/regist", 22 | method: 'post', 23 | data: data 24 | }) 25 | } 26 | 27 | // @Tags User 28 | // @Summary 分页获取用户列表 29 | // @Security ApiKeyAuth 30 | // @accept application/json 31 | // @Produce application/json 32 | // @Param data body modelInterface.PageInfo true "分页获取用户列表" 33 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 34 | // @Router /user/getUserList [post] 35 | export const getUserList = (data) => { 36 | return service({ 37 | url: "/user/getUserList", 38 | method: 'post', 39 | data: data 40 | }) 41 | } 42 | 43 | 44 | // @Tags User 45 | // @Summary 设置用户权限 46 | // @Security ApiKeyAuth 47 | // @accept application/json 48 | // @Produce application/json 49 | // @Param data body api.SetUserAuth true "设置用户权限" 50 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"修改成功"}" 51 | // @Router /user/setUserAuthority [post] 52 | export const setUserAuthority = (data) => { 53 | return service({ 54 | url: "/user/setUserAuthority", 55 | method: 'post', 56 | data: data 57 | }) 58 | } -------------------------------------------------------------------------------- /QMPlusVuePage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qm-plus-vue-page", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.0", 12 | "core-js": "^2.6.5", 13 | "element-ui": "^2.12.0", 14 | "mavon-editor": "^2.7.7", 15 | "node-sass": "^4.12.0", 16 | "path": "^0.12.7", 17 | "qs": "^6.8.0", 18 | "quill": "^1.3.7", 19 | "sass-loader": "^8.0.0", 20 | "script-ext-html-webpack-plugin": "^2.1.4", 21 | "vue": "^2.6.10", 22 | "vue-particle-line": "^0.1.4", 23 | "vue-quill-editor": "^3.0.6", 24 | "vue-router": "^3.1.3", 25 | "vuescroll": "^4.14.4", 26 | "vuex": "^3.1.1", 27 | "vuex-persist": "^2.1.0" 28 | }, 29 | "devDependencies": { 30 | "@vue/cli-plugin-babel": "^3.11.0", 31 | "@vue/cli-plugin-eslint": "^3.11.0", 32 | "@vue/cli-service": "^3.11.0", 33 | "babel-eslint": "^10.0.1", 34 | "eslint": "^5.16.0", 35 | "eslint-plugin-vue": "^5.0.0", 36 | "vue-template-compiler": "^2.6.10" 37 | }, 38 | "eslintConfig": { 39 | "root": true, 40 | "env": { 41 | "node": true 42 | }, 43 | "extends": [ 44 | "plugin:vue/essential", 45 | "eslint:recommended" 46 | ], 47 | "rules": {}, 48 | "parserOptions": { 49 | "parser": "babel-eslint" 50 | } 51 | }, 52 | "postcss": { 53 | "plugins": { 54 | "autoprefixer": {} 55 | } 56 | }, 57 | "browserslist": [ 58 | "> 1%", 59 | "last 2 versions" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/layout/aside/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 56 | 57 | -------------------------------------------------------------------------------- /model/sysModel/sys_menu_authority.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/init/qmsql" 6 | ) 7 | 8 | // menu需要构建的点有点多 这里关联关系表直接把所有数据拿过来 用代码实现关联 后期实现主外键模式 9 | type SysMenu struct { 10 | SysBaseMenu 11 | MenuId string `json:"menuId"` 12 | AuthorityId string `json:"-"` 13 | Children []SysMenu `json:"children"` 14 | } 15 | 16 | type Meta struct { 17 | Title string `json:"title"` 18 | Icon string `json:"icon"` 19 | } 20 | 21 | // 为角色增加menu树 22 | func (m *SysMenu) AddMenuAuthority(menus []SysBaseMenu, authorityId string) (err error) { 23 | var menu SysMenu 24 | qmsql.DEFAULTDB.Where("authority_id = ? ", authorityId).Unscoped().Delete(&SysMenu{}) 25 | for _, v := range menus { 26 | menu.SysBaseMenu = v 27 | menu.AuthorityId = authorityId 28 | menu.MenuId = fmt.Sprintf("%v", v.ID) 29 | menu.ID = 0 30 | err = qmsql.DEFAULTDB.Create(&menu).Error 31 | if err != nil { 32 | return err 33 | } 34 | } 35 | return nil 36 | } 37 | 38 | // 查看当前角色树 39 | func (m *SysMenu) GetMenuAuthority(authorityId string) (err error, menus []SysMenu) { 40 | err = qmsql.DEFAULTDB.Where("authority_id = ?", authorityId).Find(&menus).Error 41 | return err, menus 42 | } 43 | 44 | //获取动态路由树 45 | func (m *SysMenu) GetMenuTree(authorityId string) (err error, menus []SysMenu) { 46 | err = qmsql.DEFAULTDB.Where("authority_id = ? AND parent_id = ?", authorityId, 0).Order("sort", true).Find(&menus).Error 47 | for i := 0; i < len(menus); i++ { 48 | err = getChildrenList(&menus[i]) 49 | } 50 | return err, menus 51 | } 52 | 53 | func getChildrenList(menu *SysMenu) (err error) { 54 | err = qmsql.DEFAULTDB.Where("authority_id = ? AND parent_id = ?", menu.AuthorityId, menu.MenuId).Order("sort", true).Find(&menu.Children).Error 55 | for i := 0; i < len(menu.Children); i++ { 56 | err = getChildrenList(&menu.Children[i]) 57 | } 58 | return err 59 | } 60 | -------------------------------------------------------------------------------- /controller/api/sys_system.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/sysModel" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // @Tags system 11 | // @Summary 获取配置文件内容 12 | // @Security ApiKeyAuth 13 | // @Produce application/json 14 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 15 | // @Router /system/getSystemConfig [post] 16 | func GetSystemConfig(c *gin.Context) { 17 | err, config := new(sysModel.System).GetSystemConfig() 18 | if err != nil { 19 | servers.ReportFormat(c, false, fmt.Sprintf("获取失败:%v", err), gin.H{}) 20 | } else { 21 | servers.ReportFormat(c, true, "获取成功", gin.H{"config": config}) 22 | } 23 | } 24 | 25 | // @Tags system 26 | // @Summary 设置配置文件内容 27 | // @Security ApiKeyAuth 28 | // @Produce application/json 29 | // @Param data body sysModel.System true 30 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 31 | // @Router /system/setSystemConfig [post] 32 | func SetSystemConfig(c *gin.Context) { 33 | var sys sysModel.System 34 | _ = c.ShouldBind(&sys) 35 | err := sys.SetSystemConfig() 36 | if err != nil { 37 | servers.ReportFormat(c, false, fmt.Sprintf("设置失败:%v", err), gin.H{}) 38 | } else { 39 | servers.ReportFormat(c, true, "设置成功", gin.H{}) 40 | } 41 | } 42 | 43 | // @Tags system 44 | // @Summary 设置配置文件内容 45 | // @Security ApiKeyAuth 46 | // @Produce application/json 47 | // @Param data body sysModel.System true 48 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 49 | // @Router /system/ReloadSystem [post] 50 | func ReloadSystem(c *gin.Context) { 51 | var sys sysModel.System 52 | _ = c.ShouldBind(&sys) 53 | err := sys.SetSystemConfig() 54 | if err != nil { 55 | servers.ReportFormat(c, false, fmt.Sprintf("设置失败:%v", err), gin.H{}) 56 | } else { 57 | servers.ReportFormat(c, true, "设置成功", gin.H{}) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/authority.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | // @Summary 用户登录 4 | // @Produce application/json 5 | // @Param { 6 | // page int 7 | // pageSize int 8 | // } 9 | // @Router /authority/getAuthorityList [post] 10 | export const getAuthorityList = (data) => { 11 | return service({ 12 | url: "/authority/getAuthorityList", 13 | method: 'post', 14 | data 15 | }) 16 | } 17 | 18 | 19 | // @Summary 删除角色 20 | // @Security ApiKeyAuth 21 | // @accept application/json 22 | // @Produce application/json 23 | // @Param data body {authorityId uint} true "删除角色" 24 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 25 | // @Router /authority/deleteAuthority [post] 26 | export const deleteAuthority = (data) => { 27 | return service({ 28 | url: "/authority/deleteAuthority", 29 | method: 'post', 30 | data 31 | }) 32 | } 33 | 34 | // @Summary 创建角色 35 | // @Security ApiKeyAuth 36 | // @accept application/json 37 | // @Produce application/json 38 | // @Param data body api.CreateAuthorityPatams true "创建角色" 39 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 40 | // @Router /authority/createAuthority [post] 41 | export const createAuthority = (data) => { 42 | return service({ 43 | url: "/authority/createAuthority", 44 | method: 'post', 45 | data 46 | }) 47 | } 48 | 49 | // @Summary 设置角色资源权限 50 | // @Security ApiKeyAuth 51 | // @accept application/json 52 | // @Produce application/json 53 | // @Param data body sysModel.SysAuthority true "设置角色资源权限" 54 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" 55 | // @Router /authority/setDataAuthority [post] 56 | export const setDataAuthority = (data) => { 57 | return service({ 58 | url: "/authority/setDataAuthority", 59 | method: 'post', 60 | data 61 | }) 62 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/person/person.vue: -------------------------------------------------------------------------------- 1 | 26 | 47 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/authority/components/menus.vue: -------------------------------------------------------------------------------- 1 | 18 | 75 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/example/excel/excel.vue: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /model/sysModel/sys_api.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "nideshop-admin/controller/servers" 5 | "nideshop-admin/init/qmsql" 6 | "nideshop-admin/model/modelInterface" 7 | "github.com/jinzhu/gorm" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | type SysApi struct { 12 | gorm.Model 13 | Path string `json:"path"` 14 | Description string `json:"description"` 15 | Group string `json:"group"` 16 | Method string `json:"method" gorm:"default:'POST'"` 17 | } 18 | 19 | func (a *SysApi) CreateApi() (err error) { 20 | findOne := qmsql.DEFAULTDB.Where("path = ?", a.Path).Find(&SysMenu{}).Error 21 | if findOne == nil { 22 | return errors.New("存在相同api") 23 | } else { 24 | err = qmsql.DEFAULTDB.Create(a).Error 25 | } 26 | return err 27 | } 28 | 29 | func (a *SysApi) DeleteApi() (err error) { 30 | err = qmsql.DEFAULTDB.Delete(a).Error 31 | new(CasbinModel).clearCasbin(1, a.Path) 32 | return err 33 | } 34 | 35 | func (a *SysApi) UpdataApi() (err error) { 36 | var oldA SysApi 37 | err = qmsql.DEFAULTDB.Where("id = ?", a.ID).First(&oldA).Error 38 | if err != nil { 39 | return err 40 | } else { 41 | err = new(CasbinModel).CasbinApiUpdata(oldA.Path, a.Path) 42 | if err != nil { 43 | return err 44 | } else { 45 | err = qmsql.DEFAULTDB.Save(a).Error 46 | } 47 | } 48 | return err 49 | } 50 | 51 | func (a *SysApi) GetApiById(id float64) (err error, api SysApi) { 52 | err = qmsql.DEFAULTDB.Where("id = ?", id).First(&api).Error 53 | return 54 | } 55 | 56 | // 获取所有api信息 57 | func (a *SysApi) GetAllApis() (err error, apis []SysApi) { 58 | err = qmsql.DEFAULTDB.Find(&apis).Error 59 | return 60 | } 61 | 62 | // 分页获取数据 需要分页实现这个接口即可 63 | func (a *SysApi) GetInfoList(info modelInterface.PageInfo) (err error, list interface{}, total int) { 64 | // 封装分页方法 调用即可 传入 当前的结构体和分页信息 65 | err, db, total := servers.PagingServer(a, info) 66 | if err != nil { 67 | return 68 | } else { 69 | var apiList []SysApi 70 | err = db.Order("group", true).Where("path LIKE ?", "%"+a.Path+"%").Find(&apiList).Count(&total).Error 71 | return err, apiList, total 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/fsnotify/fsnotify" 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | type Config struct { 11 | MysqlAdmin MysqlAdmin `json:"mysqlAdmin"` 12 | UpLoad UpLoad `json:"upload"` 13 | CasbinConfig CasbinConfig `json:"casbinConfig"` 14 | RedisAdmin RedisAdmin `json:"redisAdmin"` 15 | System System `json:"system"` 16 | JWT JWT `json:"jwt"` 17 | } 18 | 19 | type System struct { // 系统配置 20 | UseMultipoint bool `json:"useMultipoint"` 21 | Env string `json:"env"` 22 | Addr int `json:"addr"` 23 | } 24 | 25 | type JWT struct { // jwt签名 26 | SigningKey string `json:"signingKey"` 27 | } 28 | 29 | type CasbinConfig struct { //casbin配置 30 | ModelPath string `json:"modelPath"` // casbin model地址配置 31 | } 32 | 33 | type MysqlAdmin struct { // mysql admin 数据库配置 34 | Username string `json:"username"` 35 | Password string `json:"password"` 36 | Path string `json:"path"` 37 | Dbname string `json:"dbname"` 38 | Config string `json:"config"` 39 | } 40 | 41 | type RedisAdmin struct { // Redis admin 数据库配置 42 | Addr string `json:"addr"` 43 | Password string `json:"password"` 44 | DB int `json:"db"` 45 | } 46 | type UpLoad struct { // 文件上传配置 47 | Domain string `json:"domain"` 48 | Path string `json:"path"` 49 | } 50 | 51 | var GinVueAdminconfig Config 52 | var VTool *viper.Viper 53 | 54 | func init() { 55 | v := viper.New() 56 | v.SetConfigName("config") // 设置配置文件名 (不带后缀) 57 | v.AddConfigPath("./static/config/") // 第一个搜索路径 58 | v.SetConfigType("json") 59 | err := v.ReadInConfig() // 搜索路径,并读取配置数据 60 | if err != nil { 61 | panic(fmt.Errorf("Fatal error config file: %s \n", err)) 62 | } 63 | v.WatchConfig() 64 | v.OnConfigChange(func(e fsnotify.Event) { 65 | fmt.Println("Config file changed:", e.Name) 66 | if err := v.Unmarshal(&GinVueAdminconfig); err != nil { 67 | fmt.Println(err) 68 | } 69 | }) 70 | if err := v.Unmarshal(&GinVueAdminconfig); err != nil { 71 | fmt.Println(err) 72 | } 73 | VTool = v 74 | } 75 | -------------------------------------------------------------------------------- /tools/str.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func GetSuffix(s string) string { 8 | start := strings.LastIndex(s, ".") 9 | end := len(s) 10 | return SubStr(s, start, end) 11 | } 12 | 13 | func GetFileName(s string) string { 14 | start := strings.LastIndex(s, "/") + 1 15 | end := len(s) 16 | return SubStr(s, start, end) 17 | } 18 | 19 | func SubStr(s string, start, end int) string { 20 | rs := []byte(s) 21 | rl := len(rs) 22 | 23 | if start < 0 { 24 | start = 0 25 | } 26 | 27 | if start > end { 28 | start, end = end, start 29 | } 30 | 31 | if end > rl { 32 | end = rl 33 | } 34 | 35 | if start > rl { 36 | start = rl 37 | } 38 | 39 | if end < 0 { 40 | end = 0 41 | } 42 | 43 | if end > rl { 44 | end = rl 45 | } 46 | 47 | return string(rs[start:end]) 48 | } 49 | 50 | func NTos(n string) string { 51 | result := "" 52 | for i := 0; i < len(n); i++ { 53 | result += nTs(string(n[i])) 54 | } 55 | return result 56 | } 57 | 58 | func STon(n string) string { 59 | result := "" 60 | for i := 0; i < len(n); i++ { 61 | result += sTn(string(n[i])) 62 | } 63 | return result 64 | } 65 | 66 | func nTs(n string) string { 67 | switch n { 68 | case "0": 69 | return "f" 70 | case "1": 71 | return "b" 72 | case "2": 73 | return "h" 74 | case "3": 75 | return "w" 76 | case "4": 77 | return "k" 78 | case "5": 79 | return "n" 80 | case "6": 81 | return "a" 82 | case "7": 83 | return "p" 84 | case "8": 85 | return "u" 86 | case "9": 87 | return "s" 88 | default: 89 | return "" 90 | } 91 | } 92 | 93 | func sTn(n string) string { 94 | switch n { 95 | case "f": 96 | return "0" 97 | case "b": 98 | return "1" 99 | case "h": 100 | return "2" 101 | case "w": 102 | return "3" 103 | case "k": 104 | return "4" 105 | case "n": 106 | return "5" 107 | case "a": 108 | return "6" 109 | case "p": 110 | return "7" 111 | case "u": 112 | return "8" 113 | case "s": 114 | return "9" 115 | default: 116 | return "" 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /controller/servers/breakpoint_continue.go: -------------------------------------------------------------------------------- 1 | package servers 2 | 3 | import ( 4 | "nideshop-admin/tools" 5 | "io/ioutil" 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | // 前端传来文件片与当前片为什么文件的第几片 11 | // 后端拿到以后比较次分片是否上传 或者是否为不完全片 12 | // 前端发送每片多大 13 | // 前端告知是否为最后一片且是否完成 14 | 15 | const breakpointDir = "./breakpointDir/" 16 | const finishDir = "./fileDir/" 17 | 18 | func BreakPointContinue(content []byte, fileName string, contentNumber int, contentTotal int, fileMd5 string) (error, string) { 19 | path := breakpointDir + fileMd5 + "/" 20 | err := os.MkdirAll(path, os.ModePerm) 21 | if err != nil { 22 | return err, path 23 | } 24 | err, pathc := makeFileContent(content, fileName, path, contentNumber) 25 | return err, pathc 26 | 27 | } 28 | 29 | func CheckMd5(content []byte, chunkMd5 string) (CanUpload bool) { 30 | fileMd5 := tools.MD5V(content) 31 | if fileMd5 == chunkMd5 { 32 | return true // "可以继续上传" 33 | } else { 34 | return false // "切片不完整,废弃" 35 | } 36 | } 37 | 38 | func makeFileContent(content []byte, fileName string, FileDir string, contentNumber int) (error, string) { 39 | path := FileDir + fileName + "_" + strconv.Itoa(contentNumber) 40 | f, err := os.Create(path) 41 | defer f.Close() 42 | if err != nil { 43 | return err, path 44 | } else { 45 | _, err = f.Write(content) 46 | if err != nil { 47 | return err, path 48 | } 49 | } 50 | return nil, path 51 | } 52 | 53 | func MakeFile(fileName string, FileMd5 string) (error, string) { 54 | rd, err := ioutil.ReadDir(breakpointDir + FileMd5) 55 | if err != nil { 56 | return err, finishDir + fileName 57 | } 58 | _ = os.MkdirAll(finishDir, os.ModePerm) 59 | fd, _ := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) 60 | for k, _ := range rd { 61 | content, _ := ioutil.ReadFile(breakpointDir + FileMd5 + "/" + fileName + "_" + strconv.Itoa(k)) 62 | _, err = fd.Write(content) 63 | if err != nil { 64 | _ = os.Remove(finishDir + fileName) 65 | return err, finishDir + fileName 66 | } 67 | } 68 | defer fd.Close() 69 | return nil, finishDir + fileName 70 | } 71 | 72 | func RemoveChunk(FileMd5 string) error { 73 | err := os.RemoveAll(breakpointDir + FileMd5) 74 | return err 75 | } 76 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/store/module/user.js: -------------------------------------------------------------------------------- 1 | import { login } from '@/api/user' 2 | import { jsonInBlacklist } from '@/api/jwt' 3 | import router from '@/router/index' 4 | export const user = { 5 | namespaced: true, 6 | state: { 7 | userInfo: { 8 | uuid: "", 9 | nickName: "", 10 | headerImg: "", 11 | authority: "", 12 | }, 13 | token: "", 14 | expiresAt: "" 15 | }, 16 | mutations: { 17 | setUserInfo(state, userInfo) { 18 | // 这里的 `state` 对象是模块的局部状态 19 | state.userInfo = userInfo 20 | }, 21 | setToken(state, token) { 22 | // 这里的 `state` 对象是模块的局部状态 23 | state.token = token 24 | }, 25 | setExpiresAt(state, expiresAt) { 26 | // 这里的 `state` 对象是模块的局部状态 27 | state.expiresAt = expiresAt 28 | }, 29 | LoginOut(state) { 30 | state.userInfo = {} 31 | state.token = "" 32 | state.expiresAt = "" 33 | router.push({ name: 'login', replace: true }) 34 | window.location.reload() 35 | }, 36 | ResetUserInfo(state, userInfo = {}) { 37 | state.userInfo = {...state.userInfo, 38 | ...userInfo 39 | } 40 | } 41 | }, 42 | actions: { 43 | async LoginIn({ commit }, loginInfo) { 44 | const res = await login(loginInfo) 45 | commit('setUserInfo', res.data.user) 46 | commit('setToken', res.data.token) 47 | commit('setExpiresAt', res.data.expiresAt) 48 | if (res.success) { 49 | const redirect = router.history.current.query.redirect 50 | if (redirect) { 51 | router.push({ path: redirect }) 52 | } else { 53 | router.push({ path: '/layout/dashbord' }) 54 | } 55 | } 56 | }, 57 | async LoginOut({ commit }) { 58 | const res = await jsonInBlacklist() 59 | if (res.success) { 60 | commit("LoginOut") 61 | } 62 | } 63 | }, 64 | getters: { 65 | userInfo(state) { 66 | return state.userInfo 67 | }, 68 | token(state) { 69 | return state.token 70 | }, 71 | expiresAt(state) { 72 | return state.expiresAt 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; // 引入axios 2 | import { Message, Loading } from 'element-ui'; 3 | import { store } from '@/store/index' 4 | const service = axios.create({ 5 | baseURL: process.env.VUE_APP_BASE_API, 6 | timeout: 99999 7 | }) 8 | let acitveAxios = 0 9 | let loadingInstance 10 | let timer 11 | const showLoading = () => { 12 | acitveAxios++ 13 | if (timer) { 14 | clearTimeout(timer) 15 | } 16 | timer = setTimeout(() => { 17 | if (acitveAxios > 0) { 18 | loadingInstance = Loading.service({ fullscreen: true }) 19 | } 20 | }, 400); 21 | } 22 | 23 | const closeLoading = () => { 24 | acitveAxios-- 25 | if (acitveAxios <= 0) { 26 | clearTimeout(timer) 27 | loadingInstance && loadingInstance.close() 28 | } 29 | } 30 | //http request 拦截器 31 | service.interceptors.request.use( 32 | config => { 33 | showLoading() 34 | const token = store.getters['user/token'] 35 | config.data = JSON.stringify(config.data); 36 | config.headers = { 37 | 'Content-Type': 'application/json', 38 | 'x-token': token 39 | } 40 | return config; 41 | }, 42 | error => { 43 | closeLoading() 44 | Message({ 45 | showClose: true, 46 | message: error, 47 | type: 'error' 48 | }) 49 | return Promise.reject(error); 50 | } 51 | ); 52 | 53 | 54 | //http response 拦截器 55 | service.interceptors.response.use( 56 | response => { 57 | closeLoading() 58 | if (response.data.success) { 59 | return response.data 60 | } else { 61 | Message({ 62 | showClose: true, 63 | message: response.data.msg, 64 | type: 'error', 65 | onClose: () => { 66 | if (response.data.data && response.data.data.reload) { 67 | store.commit('user/LoginOut') 68 | } 69 | } 70 | }) 71 | return Promise.reject(response.data.msg) 72 | } 73 | }, 74 | error => { 75 | closeLoading() 76 | Message({ 77 | showClose: true, 78 | message: error, 79 | type: 'error' 80 | }) 81 | return Promise.reject(error) 82 | } 83 | ) 84 | 85 | export default service -------------------------------------------------------------------------------- /model/dbModel/exa_breakpoint_continue.go: -------------------------------------------------------------------------------- 1 | package dbModel 2 | 3 | import ( 4 | "nideshop-admin/init/qmsql" 5 | "github.com/jinzhu/gorm" 6 | ) 7 | 8 | //文件结构体 9 | type ExaFile struct { 10 | gorm.Model 11 | FileName string 12 | FileMd5 string 13 | FilePath string 14 | ExaFileChunk []ExaFileChunk 15 | ChunkTotal int 16 | IsFinish bool 17 | } 18 | 19 | //切片结构体 20 | type ExaFileChunk struct { 21 | gorm.Model 22 | ExaFileId uint 23 | FileChunkNumber int 24 | FileChunkPath string 25 | } 26 | 27 | //文件合成完成 28 | func (f *ExaFile) FileCreateComplete(FileMd5 string, FileName string, FilePath string) error { 29 | var file ExaFile 30 | upDateFile := make(map[string]interface{}) 31 | upDateFile["FilePath"] = FilePath 32 | upDateFile["IsFinish"] = true 33 | err := qmsql.DEFAULTDB.Where("file_md5 = ? AND file_name = ?", FileMd5, FileName).First(&file).Updates(upDateFile).Error 34 | return err 35 | } 36 | 37 | //第一次上传或者断点续传时候检测当前文件属性,没有则创建,有则返回文件的当前切片 38 | func (f *ExaFile) FindOrCreateFile(FileMd5 string, FileName string, ChunkTotal int) (err error, file ExaFile) { 39 | var cfile ExaFile 40 | cfile.FileMd5 = FileMd5 41 | cfile.FileName = FileName 42 | cfile.ChunkTotal = ChunkTotal 43 | notHaveSameMd5Finish := qmsql.DEFAULTDB.Where("file_md5 = ? AND is_finish = ?", FileMd5, true).First(&file).RecordNotFound() 44 | if notHaveSameMd5Finish { 45 | err = qmsql.DEFAULTDB.Where("file_md5 = ? AND file_name = ?", FileMd5, FileName).Preload("ExaFileChunk").FirstOrCreate(&file, cfile).Error 46 | return err, file 47 | } else { 48 | cfile.IsFinish = true 49 | cfile.FilePath = file.FilePath 50 | err = qmsql.DEFAULTDB.Create(&cfile).Error 51 | return err, cfile 52 | } 53 | } 54 | 55 | // 创建文件切片记录 56 | func (f *ExaFile) CreateFileChunk(FileChunkPath string, FileChunkNumber int) error { 57 | var chunk ExaFileChunk 58 | chunk.FileChunkPath = FileChunkPath 59 | chunk.ExaFileId = f.ID 60 | chunk.FileChunkNumber = FileChunkNumber 61 | err := qmsql.DEFAULTDB.Create(&chunk).Error 62 | return err 63 | } 64 | 65 | // 删除文件切片记录 66 | func (f *ExaFile) DeleteFileChunk(fileMd5 string, fileName string, filePath string) error { 67 | var chunks []ExaFileChunk 68 | var file ExaFile 69 | err := qmsql.DEFAULTDB.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).First(&file).Update("IsFinish", true).Update("file_path", filePath).Error 70 | err = qmsql.DEFAULTDB.Where("exa_file_id = ?", file.ID).Delete(&chunks).Unscoped().Error 71 | return err 72 | } 73 | -------------------------------------------------------------------------------- /model/sysModel/sys_authority.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "nideshop-admin/controller/servers" 5 | "nideshop-admin/init/qmsql" 6 | "nideshop-admin/model/modelInterface" 7 | "github.com/jinzhu/gorm" 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | type SysAuthority struct { 12 | gorm.Model 13 | AuthorityId string `json:"authorityId" gorm:"not null;unique"` 14 | AuthorityName string `json:"authorityName"` 15 | ParentId string `json:"parentId"` 16 | DataAuthorityId []SysAuthority `json:"dataAuthorityId" gorm:"many2many:sys_data_authority_id;association_jointable_foreignkey:data_id"` 17 | Children []SysAuthority `json:"children"` 18 | } 19 | 20 | // 创建角色 21 | func (a *SysAuthority) CreateAuthority() (err error, authority *SysAuthority) { 22 | err = qmsql.DEFAULTDB.Create(a).Error 23 | return err, a 24 | } 25 | 26 | // 删除角色 27 | func (a *SysAuthority) DeleteAuthority() (err error) { 28 | err = qmsql.DEFAULTDB.Where("authority_id = ?", a.AuthorityId).Find(&SysUser{}).Error 29 | if err != nil { 30 | err = qmsql.DEFAULTDB.Where("parentId = ?", a.AuthorityId).Find(&SysAuthority{}).Error 31 | if err != nil { 32 | err = qmsql.DEFAULTDB.Where("authority_id = ?", a.AuthorityId).First(a).Unscoped().Delete(a).Error 33 | new(CasbinModel).clearCasbin(0, a.AuthorityId) 34 | } else { 35 | err = errors.New("此角色存在子角色不允许删除") 36 | } 37 | } else { 38 | err = errors.New("此角色有用户正在使用禁止删除") 39 | } 40 | return err 41 | } 42 | 43 | // 分页获取数据 需要分页实现这个接口即可 44 | func (a *SysAuthority) GetInfoList(info modelInterface.PageInfo) (err error, list interface{}, total int) { 45 | // 封装分页方法 调用即可 传入 当前的结构体和分页信息 46 | err, db, total := servers.PagingServer(a, info) 47 | if err != nil { 48 | return 49 | } else { 50 | var authority []SysAuthority 51 | err = db.Preload("DataAuthorityId").Where("parent_id = 0").Find(&authority).Error 52 | if len(authority) > 0 { 53 | for k, _ := range authority { 54 | err = findChildrenAuthority(&authority[k]) 55 | } 56 | } 57 | return err, authority, total 58 | } 59 | } 60 | 61 | func findChildrenAuthority(authority *SysAuthority) (err error) { 62 | err = qmsql.DEFAULTDB.Preload("DataAuthorityId").Where("parent_id = ?", authority.AuthorityId).Find(&authority.Children).Error 63 | if len(authority.Children) > 0 { 64 | for k, _ := range authority.Children { 65 | err = findChildrenAuthority(&authority.Children[k]) 66 | } 67 | } 68 | return err 69 | } 70 | 71 | func (a *SysAuthority) SetDataAuthority() error { 72 | var s SysAuthority 73 | qmsql.DEFAULTDB.Preload("DataAuthorityId").First(&s, "id = ?", a.ID) 74 | err := qmsql.DEFAULTDB.Model(&s).Association("DataAuthorityId").Replace(&a.DataAuthorityId).Error 75 | return err 76 | } 77 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/authority/components/apis.vue: -------------------------------------------------------------------------------- 1 | 18 | 98 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/login/login.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 85 | 86 | -------------------------------------------------------------------------------- /model/sysModel/sys_casbin.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "errors" 5 | "nideshop-admin/config" 6 | "nideshop-admin/init/qmsql" 7 | "github.com/casbin/casbin" 8 | gormadapter "github.com/casbin/gorm-adapter" 9 | "strings" 10 | ) 11 | 12 | type CasbinModel struct { 13 | ID uint `json:"id" gorm:"column:_id"` 14 | Ptype string `json:"ptype" gorm:"column:ptype"` 15 | AuthorityId string `json:"rolename" gorm:"column:v0"` 16 | Path string `json:"path" gorm:"column:v1"` 17 | Method string `json:"method" gorm:"column:v2"` 18 | } 19 | 20 | // 供入参使用 21 | type CasbinInfo struct { 22 | Path string `json:"path"` 23 | Method string `json:"method"` 24 | } 25 | 26 | // 供入参使用 27 | type CasbinInReceive struct { 28 | AuthorityId string `json:"authorityId"` 29 | CasbinInfos []CasbinInfo `json:"casbinInfos"` 30 | } 31 | 32 | // 更新权限 33 | func (c *CasbinModel) CasbinPUpdata(AuthorityId string, casbinInfos []CasbinInfo) error { 34 | c.clearCasbin(0, AuthorityId) 35 | for _, v := range casbinInfos { 36 | cm := CasbinModel{ 37 | ID: 0, 38 | Ptype: "p", 39 | AuthorityId: AuthorityId, 40 | Path: v.Path, 41 | Method: v.Method, 42 | } 43 | addflag := c.AddCasbin(cm) 44 | if addflag == false { 45 | return errors.New("存在相同api,添加失败,请联系管理员") 46 | } 47 | } 48 | return nil 49 | } 50 | 51 | // API更新随动 52 | func (c *CasbinModel) CasbinApiUpdata(oldPath string, newPath string) error { 53 | var cs []CasbinModel 54 | err := qmsql.DEFAULTDB.Table("casbin_rule").Where("v1 = ?", oldPath).Find(&cs).Update("v1", newPath).Error 55 | return err 56 | } 57 | 58 | //添加权限 59 | func (c *CasbinModel) AddCasbin(cm CasbinModel) bool { 60 | e := Casbin() 61 | return e.AddPolicy(cm.AuthorityId, cm.Path, cm.Method) 62 | } 63 | 64 | //获取权限列表 65 | func (c *CasbinModel) GetPolicyPathByAuthorityId(AuthorityId string) []string { 66 | e := Casbin() 67 | var pathList []string 68 | list := e.GetFilteredPolicy(0, AuthorityId) 69 | for _, v := range list { 70 | pathList = append(pathList, v[1]) 71 | } 72 | return pathList 73 | } 74 | 75 | //清除匹配的权限 76 | func (c *CasbinModel) clearCasbin(v int, p string) bool { 77 | e := Casbin() 78 | return e.RemoveFilteredPolicy(v, p) 79 | 80 | } 81 | 82 | // 自定义规则函数 83 | func ParamsMatch(key1 string, key2 string) bool { 84 | k1arr := strings.Split(key1, "?") 85 | return k1arr[0] == key2 86 | } 87 | 88 | // 自定义规则函数 89 | func ParamsMatchFunc(args ...interface{}) (interface{}, error) { 90 | name1 := args[0].(string) 91 | name2 := args[1].(string) 92 | 93 | return (bool)(ParamsMatch(name1, name2)), nil 94 | } 95 | 96 | //持久化到数据库 引入自定义规则 97 | func Casbin() *casbin.Enforcer { 98 | a := gormadapter.NewAdapterByDB(qmsql.DEFAULTDB) 99 | e := casbin.NewEnforcer(config.GinVueAdminconfig.CasbinConfig.ModelPath, a) 100 | e.AddFunction("ParamsMatch", ParamsMatchFunc) 101 | e.LoadPolicy() 102 | return e 103 | } 104 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/menu.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | // @Summary 用户登录 获取动态路由 4 | // @Produce application/json 5 | // @Param 可以什么都不填 调一下即可 6 | // @Router /menu/getMenu [post] 7 | export const asyncMenu = () => { 8 | return service({ 9 | url: "/menu/getMenu", 10 | method: 'post', 11 | }) 12 | } 13 | 14 | // @Summary 获取menu列表 15 | // @Produce application/json 16 | // @Param { 17 | // page int 18 | // pageSize int 19 | // } 20 | // @Router /menu/getMenuList [post] 21 | export const getMenuList = (data) => { 22 | return service({ 23 | url: "/menu/getMenuList", 24 | method: 'post', 25 | data 26 | }) 27 | } 28 | 29 | 30 | // @Summary 新增基础menu 31 | // @Produce application/json 32 | // @Param menu Object 33 | // @Router /menu/getMenuList [post] 34 | export const addBaseMenu = (data) => { 35 | return service({ 36 | url: "/menu/addBaseMenu", 37 | method: 'post', 38 | data 39 | }) 40 | } 41 | 42 | // @Summary 获取基础路由列表 43 | // @Produce application/json 44 | // @Param 可以什么都不填 调一下即可 45 | // @Router /menu/getBaseMenuTree [post] 46 | export const getBaseMenuTree = () => { 47 | return service({ 48 | url: "/menu/getBaseMenuTree", 49 | method: 'post', 50 | }) 51 | } 52 | 53 | // @Summary 添加用户menu关联关系 54 | // @Produce application/json 55 | // @Param menus Object authorityId string 56 | // @Router /menu/getMenuList [post] 57 | export const addMenuAuthority = (data) => { 58 | return service({ 59 | url: "/menu/addMenuAuthority", 60 | method: 'post', 61 | data 62 | }) 63 | } 64 | 65 | // @Summary 获取用户menu关联关系 66 | // @Produce application/json 67 | // @Param authorityId string 68 | // @Router /menu/getMenuAuthority [post] 69 | export const getMenuAuthority = (data) => { 70 | return service({ 71 | url: "/menu/getMenuAuthority", 72 | method: 'post', 73 | data 74 | }) 75 | } 76 | 77 | // @Summary 获取用户menu关联关系 78 | // @Produce application/json 79 | // @Param ID float64 80 | // @Router /menu/deleteBaseMenu [post] 81 | export const deleteBaseMenu = (data) => { 82 | return service({ 83 | url: "/menu/deleteBaseMenu", 84 | method: 'post', 85 | data 86 | }) 87 | } 88 | 89 | 90 | // @Summary 修改menu列表 91 | // @Produce application/json 92 | // @Param menu Object 93 | // @Router /menu/updataBaseMenu [post] 94 | export const updataBaseMenu = (data) => { 95 | return service({ 96 | url: "/menu/updataBaseMenu", 97 | method: 'post', 98 | data 99 | }) 100 | } 101 | 102 | 103 | // @Tags menu 104 | // @Summary 根据id获取菜单 105 | // @Security ApiKeyAuth 106 | // @accept application/json 107 | // @Produce application/json 108 | // @Param data body api.GetById true "根据id获取菜单" 109 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 110 | // @Router /menu/getBaseMenuById [post] 111 | export const getBaseMenuById = (data) => { 112 | return service({ 113 | url: "/menu/getBaseMenuById", 114 | method: 'post', 115 | data 116 | }) 117 | } -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/dashbord/component/animition.vue: -------------------------------------------------------------------------------- 1 | 85 | 90 | -------------------------------------------------------------------------------- /model/sysModel/sys_user.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "nideshop-admin/controller/servers" 5 | "nideshop-admin/init/qmsql" 6 | "nideshop-admin/model/modelInterface" 7 | "nideshop-admin/tools" 8 | "github.com/jinzhu/gorm" 9 | "github.com/pkg/errors" 10 | uuid "github.com/satori/go.uuid" 11 | ) 12 | 13 | type SysUser struct { 14 | gorm.Model 15 | UUID uuid.UUID `json:"uuid"` 16 | Username string `json:"userName"` 17 | Password string `json:"-"` 18 | NickName string `json:"nickName" gorm:"default:'QMPlusUser'"` 19 | HeaderImg string `json:"headerImg" gorm:"default:'http://www.henrongyi.top/avatar/lufu.jpg'"` 20 | Authority SysAuthority `json:"authority" gorm:"ForeignKey:AuthorityId;AssociationForeignKey:AuthorityId"` 21 | AuthorityId string `json:"authorityId" gorm:"default:888"` 22 | } 23 | 24 | //type Propertie struct { 25 | // gorm.Model 26 | //} 27 | 28 | //注册接口model方法 29 | func (u *SysUser) Regist() (err error, userInter *SysUser) { 30 | var user SysUser 31 | //判断用户名是否注册 32 | notResigt := qmsql.DEFAULTDB.Where("username = ?", u.Username).First(&user).RecordNotFound() 33 | //notResigt为false表明读取到了 不能注册 34 | if !notResigt { 35 | return errors.New("用户名已注册"), nil 36 | } else { 37 | // 否则 附加uuid 密码md5简单加密 注册 38 | u.Password = tools.MD5V([]byte(u.Password)) 39 | u.UUID = uuid.NewV4() 40 | err = qmsql.DEFAULTDB.Create(u).Error 41 | } 42 | return err, u 43 | } 44 | 45 | //修改用户密码 46 | func (u *SysUser) ChangePassword(newPassword string) (err error, userInter *SysUser) { 47 | var user SysUser 48 | //后期修改jwt+password模式 49 | u.Password = tools.MD5V([]byte(u.Password)) 50 | err = qmsql.DEFAULTDB.Where("username = ? AND password = ?", u.Username, u.Password).First(&user).Update("password", tools.MD5V([]byte(newPassword))).Error 51 | return err, u 52 | } 53 | 54 | //用户更新接口 55 | func (u *SysUser) SetUserAuthority(uuid uuid.UUID, AuthorityId string) (err error) { 56 | err = qmsql.DEFAULTDB.Where("uuid = ?", uuid).First(&SysUser{}).Update("authority_id", AuthorityId).Error 57 | return err 58 | } 59 | 60 | //用户登录 61 | func (u *SysUser) Login() (err error, userInter *SysUser) { 62 | var user SysUser 63 | u.Password = tools.MD5V([]byte(u.Password)) 64 | err = qmsql.DEFAULTDB.Where("username = ? AND password = ?", u.Username, u.Password).First(&user).Error 65 | if err != nil { 66 | return err, &user 67 | } 68 | err = qmsql.DEFAULTDB.Where("authority_id = ?", user.AuthorityId).First(&user.Authority).Error 69 | return err, &user 70 | } 71 | 72 | // 用户头像上传更新地址 73 | func (u *SysUser) UploadHeaderImg(uuid uuid.UUID, filePath string) (err error, userInter *SysUser) { 74 | var user SysUser 75 | err = qmsql.DEFAULTDB.Where("uuid = ?", uuid).First(&user).Update("header_img", filePath).First(&user).Error 76 | return err, &user 77 | } 78 | 79 | // 分页获取数据 需要分页实现这个接口即可 80 | func (u *SysUser) GetInfoList(info modelInterface.PageInfo) (err error, list interface{}, total int) { 81 | // 封装分页方法 调用即可 传入 当前的结构体和分页信息 82 | err, db, total := servers.PagingServer(u, info) 83 | if err != nil { 84 | return 85 | } else { 86 | var userList []SysUser 87 | err = db.Preload("Authority").Find(&userList).Error 88 | return err, userList, total 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/authority/components/datas.vue: -------------------------------------------------------------------------------- 1 | 14 | 95 | -------------------------------------------------------------------------------- /controller/api/sys_authority.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/modelInterface" 7 | "nideshop-admin/model/sysModel" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | type CreateAuthorityParams struct { 12 | AuthorityId string `json:"authorityId"` 13 | AuthorityName string `json:"authorityName"` 14 | } 15 | 16 | // @Tags authority 17 | // @Summary 创建角色 18 | // @Security ApiKeyAuth 19 | // @accept application/json 20 | // @Produce application/json 21 | // @Param data body api.CreateAuthorityParams true "创建角色" 22 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 23 | // @Router /authority/createAuthority [post] 24 | func CreateAuthority(c *gin.Context) { 25 | var auth sysModel.SysAuthority 26 | _ = c.ShouldBind(&auth) 27 | err, authBack := auth.CreateAuthority() 28 | if err != nil { 29 | servers.ReportFormat(c, false, fmt.Sprintf("创建失败:%v", err), gin.H{ 30 | "authority": authBack, 31 | }) 32 | } else { 33 | servers.ReportFormat(c, true, "创建成功", gin.H{ 34 | "authority": authBack, 35 | }) 36 | } 37 | } 38 | 39 | type DeleteAuthorityPatams struct { 40 | AuthorityId uint `json:"authorityId"` 41 | } 42 | 43 | // @Tags authority 44 | // @Summary 删除角色 45 | // @Security ApiKeyAuth 46 | // @accept application/json 47 | // @Produce application/json 48 | // @Param data body api.DeleteAuthorityPatams true "删除角色" 49 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 50 | // @Router /authority/deleteAuthority [post] 51 | func DeleteAuthority(c *gin.Context) { 52 | var a sysModel.SysAuthority 53 | _ = c.BindJSON(&a) 54 | //删除角色之前需要判断是否有用户正在使用此角色 55 | err := a.DeleteAuthority() 56 | if err != nil { 57 | servers.ReportFormat(c, false, fmt.Sprintf("删除失败:%v", err), gin.H{}) 58 | } else { 59 | servers.ReportFormat(c, true, "删除成功", gin.H{}) 60 | } 61 | } 62 | 63 | // @Tags authority 64 | // @Summary 分页获取角色列表 65 | // @Security ApiKeyAuth 66 | // @accept application/json 67 | // @Produce application/json 68 | // @Param data body modelInterface.PageInfo true "分页获取用户列表" 69 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 70 | // @Router /authority/getAuthorityList [post] 71 | func GetAuthorityList(c *gin.Context) { 72 | var pageInfo modelInterface.PageInfo 73 | _ = c.BindJSON(&pageInfo) 74 | err, list, total := new(sysModel.SysAuthority).GetInfoList(pageInfo) 75 | if err != nil { 76 | servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{}) 77 | } else { 78 | servers.ReportFormat(c, true, "获取数据成功", gin.H{ 79 | "list": list, 80 | "total": total, 81 | "page": pageInfo.Page, 82 | "pageSize": pageInfo.PageSize, 83 | }) 84 | } 85 | } 86 | 87 | // @Tags authority 88 | // @Summary 设置角色资源权限 89 | // @Security ApiKeyAuth 90 | // @accept application/json 91 | // @Produce application/json 92 | // @Param data body sysModel.SysAuthority true "设置角色资源权限" 93 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" 94 | // @Router /authority/setDataAuthority [post] 95 | func SetDataAuthority(c *gin.Context) { 96 | var auth sysModel.SysAuthority 97 | _ = c.ShouldBind(&auth) 98 | err := auth.SetDataAuthority() 99 | if err != nil { 100 | servers.ReportFormat(c, false, fmt.Sprintf("设置关联失败,%v", err), gin.H{}) 101 | } else { 102 | servers.ReportFormat(c, true, "设置关联成功", gin.H{}) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /model/sysModel/sys_base_menu.go: -------------------------------------------------------------------------------- 1 | package sysModel 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/init/qmsql" 7 | "nideshop-admin/model/modelInterface" 8 | "github.com/jinzhu/gorm" 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | type SysBaseMenu struct { 13 | gorm.Model 14 | MenuLevel uint `json:"-"` 15 | ParentId string `json:"parentId"` 16 | Path string `json:"path"` 17 | Name string `json:"name"` 18 | Hidden bool `json:"hidden"` 19 | Component string `json:"component"` 20 | Sort string `json:"sort"` 21 | Meta `json:"meta"` 22 | NickName string `json:"nickName"` 23 | Children []SysBaseMenu `json:"children"` 24 | } 25 | 26 | func (b *SysBaseMenu) AddBaseMenu() (err error) { 27 | findOne := qmsql.DEFAULTDB.Where("name = ?", b.Name).Find(&SysBaseMenu{}).Error 28 | if findOne != nil { 29 | b.NickName = b.Title 30 | err = qmsql.DEFAULTDB.Create(b).Error 31 | } else { 32 | err = errors.New("存在重复name,请修改name") 33 | } 34 | return err 35 | } 36 | 37 | func (b *SysBaseMenu) DeleteBaseMenu(id float64) (err error) { 38 | err = qmsql.DEFAULTDB.Where("parent_id = ?", id).First(&SysBaseMenu{}).Error 39 | if err != nil { 40 | err = qmsql.DEFAULTDB.Where("id = ?", id).Delete(&b).Error 41 | err = qmsql.DEFAULTDB.Where("menu_id = ?", id).Unscoped().Delete(&SysMenu{}).Error 42 | } else { 43 | return errors.New("此菜单存在子菜单不可删除") 44 | } 45 | return err 46 | } 47 | 48 | func (b *SysBaseMenu) UpdataBaseMenu() (err error) { 49 | upDataMap := make(map[string]interface{}) 50 | upDataMap["parent_id"] = b.ParentId 51 | upDataMap["path"] = b.Path 52 | upDataMap["name"] = b.Name 53 | upDataMap["hidden"] = b.Hidden 54 | upDataMap["component"] = b.Component 55 | upDataMap["title"] = b.Title 56 | upDataMap["icon"] = b.Icon 57 | upDataMap["sort"] = b.Sort 58 | err = qmsql.DEFAULTDB.Where("id = ?", b.ID).Find(&SysBaseMenu{}).Updates(upDataMap).Error 59 | err1 := qmsql.DEFAULTDB.Where("menu_id = ?", b.ID).Find(&[]SysMenu{}).Updates(upDataMap).Error 60 | fmt.Printf("菜单修改时候,关联菜单err1:%v,err:%v", err1, err) 61 | return err 62 | } 63 | 64 | func (b *SysBaseMenu) GetBaseMenuById(id float64) (err error, menu SysBaseMenu) { 65 | err = qmsql.DEFAULTDB.Where("id = ?", id).First(&menu).Error 66 | return 67 | } 68 | 69 | func (b *SysBaseMenu) GetInfoList(info modelInterface.PageInfo) (err error, list interface{}, total int) { 70 | // 封装分页方法 调用即可 传入 当前的结构体和分页信息 71 | err, db, total := servers.PagingServer(b, info) 72 | if err != nil { 73 | return 74 | } else { 75 | var menuList []SysBaseMenu 76 | err = db.Where("parent_id = 0").Order("sort", true).Find(&menuList).Error 77 | for i := 0; i < len(menuList); i++ { 78 | err = getBaseChildrenList(&menuList[i]) 79 | } 80 | return err, menuList, total 81 | } 82 | } 83 | 84 | //获取基础路由树 85 | func (m *SysBaseMenu) GetBaseMenuTree() (err error, menus []SysBaseMenu) { 86 | err = qmsql.DEFAULTDB.Where(" parent_id = ?", 0).Order("sort", true).Find(&menus).Error 87 | for i := 0; i < len(menus); i++ { 88 | err = getBaseChildrenList(&menus[i]) 89 | } 90 | return err, menus 91 | } 92 | 93 | func getBaseChildrenList(menu *SysBaseMenu) (err error) { 94 | err = qmsql.DEFAULTDB.Where("parent_id = ?", menu.ID).Order("sort", true).Find(&menu.Children).Error 95 | for i := 0; i < len(menu.Children); i++ { 96 | err = getBaseChildrenList(&menu.Children[i]) 97 | } 98 | return err 99 | } 100 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/api/api.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags api 3 | // @Summary 分页获取角色列表 4 | // @Security ApiKeyAuth 5 | // @accept application/json 6 | // @Produce application/json 7 | // @Param data body modelInterface.PageInfo true "分页获取用户列表" 8 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 9 | // @Router /api/getApiList [post] 10 | // { 11 | // page int 12 | // pageSize int 13 | // } 14 | export const getApiList = (data) => { 15 | return service({ 16 | url: "/api/getApiList", 17 | method: 'post', 18 | data 19 | }) 20 | } 21 | 22 | 23 | // @Tags Api 24 | // @Summary 创建基础api 25 | // @Security ApiKeyAuth 26 | // @accept application/json 27 | // @Produce application/json 28 | // @Param data body api.CreateApiParams true "创建api" 29 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 30 | // @Router /api/createApi [post] 31 | export const createApi = (data) => { 32 | return service({ 33 | url: "/api/createApi", 34 | method: 'post', 35 | data 36 | }) 37 | } 38 | 39 | // @Tags menu 40 | // @Summary 根据id获取菜单 41 | // @Security ApiKeyAuth 42 | // @accept application/json 43 | // @Produce application/json 44 | // @Param data body api.GetById true "根据id获取菜单" 45 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 46 | // @Router /menu/getApiById [post] 47 | export const getApiById = (data) => { 48 | return service({ 49 | url: "/api/getApiById", 50 | method: 'post', 51 | data 52 | }) 53 | } 54 | 55 | 56 | 57 | // @Tags Api 58 | // @Summary 更新api 59 | // @Security ApiKeyAuth 60 | // @accept application/json 61 | // @Produce application/json 62 | // @Param data body api.CreateApiParams true "更新api" 63 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}" 64 | // @Router /api/updataApi [post] 65 | export const updataApi = (data) => { 66 | return service({ 67 | url: "/api/updataApi", 68 | method: 'post', 69 | data 70 | }) 71 | } 72 | 73 | // @Tags Api 74 | // @Summary 更新api 75 | // @Security ApiKeyAuth 76 | // @accept application/json 77 | // @Produce application/json 78 | // @Param data body api.CreateApiParams true "更新api" 79 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}" 80 | // @Router /api/setAuthApi [post] 81 | export const setAuthApi = (data) => { 82 | return service({ 83 | url: "/api/setAuthApi", 84 | method: 'post', 85 | data 86 | }) 87 | } 88 | 89 | // @Tags Api 90 | // @Summary 获取所有的Api 不分页 91 | // @Security ApiKeyAuth 92 | // @accept application/json 93 | // @Produce application/json 94 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 95 | // @Router /api/getAllApis [post] 96 | export const getAllApis = (data) => { 97 | return service({ 98 | url: "/api/getAllApis", 99 | method: 'post', 100 | data 101 | }) 102 | } 103 | 104 | // @Tags Api 105 | // @Summary 删除指定api 106 | // @Security ApiKeyAuth 107 | // @accept application/json 108 | // @Produce application/json 109 | // @Param data body dbModel.Api true "删除api" 110 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 111 | // @Router /api/deleteApi [post] 112 | export const deleteApi = (data) => { 113 | return service({ 114 | url: "/api/deleteApi", 115 | method: 'post', 116 | data 117 | }) 118 | } -------------------------------------------------------------------------------- /controller/api/exa_fileUploadAndDownload.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/dbModel" 7 | "nideshop-admin/model/modelInterface" 8 | "strings" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // @Tags ExaFileUploadAndDownload 14 | // @Summary 上传文件示例 15 | // @Security ApiKeyAuth 16 | // @accept multipart/form-data 17 | // @Produce application/json 18 | // @Param file formData file true "上传文件示例" 19 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"上传成功"}" 20 | // @Router /fileUploadAndDownload/upload [post] 21 | func UploadFile(c *gin.Context) { 22 | noSave := c.DefaultQuery("noSave", "0") 23 | _, header, err := c.Request.FormFile("file") 24 | if err != nil { 25 | servers.ReportFormat(c, false, fmt.Sprintf("上传文件失败,%v", err), gin.H{}) 26 | } else { 27 | //文件上传后拿到文件路径 28 | err, filePath, key := servers.Upload(header, USER_HEADER_BUCKET) 29 | fmt.Println(filePath) 30 | fmt.Println(key) 31 | if err != nil { 32 | servers.ReportFormat(c, false, fmt.Sprintf("接收返回值失败,%v", err), gin.H{}) 33 | } else { 34 | //修改数据库后得到修改后的user并且返回供前端使用 35 | var file dbModel.ExaFileUploadAndDownload 36 | file.Url = filePath 37 | file.Name = header.Filename 38 | s := strings.Split(file.Name, ".") 39 | file.Tag = s[len(s)-1] 40 | file.Key = key 41 | if noSave == "0" { 42 | err = file.Upload() 43 | } 44 | if err != nil { 45 | servers.ReportFormat(c, false, fmt.Sprintf("修改数据库链接失败,%v", err), gin.H{}) 46 | } else { 47 | servers.ReportFormat(c, true, "上传成功", gin.H{"file": file}) 48 | } 49 | } 50 | } 51 | } 52 | 53 | // @Tags ExaFileUploadAndDownload 54 | // @Summary 删除文件 55 | // @Security ApiKeyAuth 56 | // @Produce application/json 57 | // @Param data body dbModel.ExaFileUploadAndDownload true "传入文件里面id即可" 58 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 59 | // @Router /fileUploadAndDownload/deleteFile [post] 60 | func DeleteFile(c *gin.Context) { 61 | var file dbModel.ExaFileUploadAndDownload 62 | _ = c.ShouldBind(&file) 63 | err, f := file.FindFile() 64 | if err != nil { 65 | servers.ReportFormat(c, false, fmt.Sprintf("删除失败,%v", err), gin.H{}) 66 | } else { 67 | err = servers.DeleteFile(USER_HEADER_BUCKET, f.Key) 68 | if err != nil { 69 | servers.ReportFormat(c, false, fmt.Sprintf("删除失败,%v", err), gin.H{}) 70 | } else { 71 | err = f.DeleteFile() 72 | if err != nil { 73 | servers.ReportFormat(c, false, fmt.Sprintf("删除失败,%v", err), gin.H{}) 74 | } else { 75 | servers.ReportFormat(c, true, fmt.Sprintf("删除成功,%v", err), gin.H{}) 76 | } 77 | } 78 | } 79 | } 80 | 81 | // @Tags ExaFileUploadAndDownload 82 | // @Summary 分页文件列表 83 | // @Security ApiKeyAuth 84 | // @accept application/json 85 | // @Produce application/json 86 | // @Param data body modelInterface.PageInfo true "分页获取文件户列表" 87 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 88 | // @Router /fileUploadAndDownload/getFileList [post] 89 | func GetFileList(c *gin.Context) { 90 | var pageInfo modelInterface.PageInfo 91 | _ = c.BindJSON(&pageInfo) 92 | err, list, total := new(dbModel.ExaFileUploadAndDownload).GetInfoList(pageInfo) 93 | if err != nil { 94 | servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{}) 95 | } else { 96 | servers.ReportFormat(c, true, "获取数据成功", gin.H{ 97 | "list": list, 98 | "total": total, 99 | "page": pageInfo.Page, 100 | "pageSize": pageInfo.PageSize, 101 | }) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/example/table/table.vue: -------------------------------------------------------------------------------- 1 | // table 纯前端示例 2 | 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | nideshop-admin 4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | # nideshop-admin gin+vue开源快速项目模板 15 | 本模板使用前端ui框架为 element-ui https://element.eleme.cn/#/zh-CN 前端组件可查看elementUi文档使用 16 | ## 写在前面 17 | 非常感谢 [@piexlmax](https://github.com/piexlmax)提供项目脚手架,脚手架地址 [gin-vue-admin](https://github.com/piexlmax/gin-vue-admin) 18 | 19 | 20 | ## 环境搭建教学视频 21 | 22 | 腾讯视频:https://v.qq.com/x/page/e3008xjxqtu.html (等待最新视频录制) 23 | 24 | ## 模板使用教学及展示视频 25 | 26 | 腾讯视频:https://v.qq.com/x/page/c3008y2ukba.html (等待最新视频录制) 27 | 28 | ## 技术选型 29 | 1.后端采用golang框架gin,快速搭建基础restful风格API 30 | 2.前端项目采用VUE框架,构建基础页面 31 | 3.数据库采用Mysql(5.6.44)版本不同可能会导致SQL导入失败 32 | 4.使用redis实现记录当前活跃用户的jwt令牌并实现多点登录限制 33 | 5.使用swagger构建自动化文档 34 | 6.使用fsnotify和viper实现json格式配置文件 35 | 7.使用logrus实现日志记录 36 | 8.使用gorm实现对数据库的基本操作 37 | 38 | ## 项目说明 39 | static/config存放mysql相关配置。可以根据自己的mysql数据库名 用户名 密码修改对应配置 40 | vue项目存放于QMPlusVuePage文件夹下 41 | 开源不易,感谢各位支持,错误指出即刻改正,改写纠错,感谢star支持 42 | ## TODO 43 | ###脚手架 44 | 1.基本用户注册登录 √ 45 | 2.用户等基础数据CURD √ 46 | 3.调用des实现数据加密 √ 47 | 4.实现基于jwt的权限管理 √ 48 | 6.封装了分页方法,实现分页接口并且复制粘贴就可使用分页 √ 49 | 7.前端分页mixin封装 分页方法调用mixins即可 √ 50 | 8.图片上传前端下载功能 √ 51 | 9.富文本编辑器,MarkDown编辑器功能嵌入 √ 52 | 10.增加条件搜索示例 前端文件参考src\view\superAdmin\api\api.vue 后台文件参考 model\dnModel\api.go √ 53 | 11.增加了多点登录限制 体验需要再 static\config中 把 system中的useMultipoint 修改为 true(需要自行配置redis和config中的redis参数)(测试阶段,有bug请及时反馈)√ 54 | 12.增加了配置文件管理功能 √ 55 | ###nideshop-admin 56 | 1.商品管理 57 | 2.商品分类管理 58 | ## 使用说明 59 | 1.golang api server 基于go.mod 如果golang版本低于1.11 请自行升级golang版本 60 | 2.支持go.mod的golang版本在运行go list 和 编译之前都会自动下载所需要的依赖包 61 | 3.前端项目node建议高于V8.6.0 62 | 4.到前端项目目录下运行 npm i 安装所需依赖 63 | 5.依赖安装完成直接运行 npm run serve即可启动项目 64 | 6.如果要使用swagger自动化文档 首先需要安装 swagger 65 | ```` 66 | go get -u github.com/swaggo/swag/cmd/swag 67 | ```` 68 | 由于国内没法安装到X包下面的东西 如果可以翻墙 上面的命令就可以让你安心使用swagger了 69 | 如果没有翻墙的办法那就先装一下 gopm 70 | 71 | ```` 72 | go get -v -u github.com/gpmgo/gopm 73 | ```` 74 | 此时你就可以使用 gopm了 75 | 这时候执行 76 | ```` 77 | gopm get -g -v github.com/swaggo/swag/cmd/swag 78 | ```` 79 | 等待安装完成以后 80 | 到我们GOPATH下面的/src/github.com/swaggo/swag/cmd/swag路径 81 | 执行 82 | ```` 83 | go install 84 | ```` 85 | 安装完成过后在项目目录下运行 86 | ```` 87 | swag init 88 | ```` 89 | 项目文件夹下面会有 doc文件夹出现 90 | 这时候登录 localhost:8888/swagger/index.html 91 | 就可以看到 swagger文档啦 92 | 93 | ## docker镜像 94 | 感谢 [@chenlinzhong](https://github.com/chenlinzhong)提供docker镜像 95 | 96 | #启动容器 97 | docker run -itd --net=host --name=go_container shareclz/go_node /bin/bash; 98 | 99 | #进入容器 100 | docker exec -it go_container /bin/bash; 101 | git clone https://github.com/piexlmax/nideshop-admin.git /data1/www/htdocs/go/admin; 102 | 103 | #启动前端 104 | cd /data1/www/htdocs/go/admin/QMPlusVuePage; 105 | cnpm i ; 106 | npm run serve; 107 | 108 | #修改数据库配置 109 | vi /data1/www/htdocs/go/admin/QMPlusServer/static/dbconfig/config.json; 110 | 111 | #启动后端 112 | cd /data1/www/htdocs/go/admin/QMPlusServer; 113 | go run main.go; 114 | 115 | 116 | ## 联系方式 117 | 118 |
119 |

qq交流群:234706951

120 |

微信交流群加微信:14320794 备注"加入nideshop-admin交流群"

121 |
122 | 123 | 124 | ## 更新日志 125 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/system/system.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 103 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/login/regist.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 108 | 109 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/workflow/workflowCreate/workflowCreate.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 114 | 116 | -------------------------------------------------------------------------------- /middleware/jwt.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "nideshop-admin/config" 6 | "nideshop-admin/controller/servers" 7 | "nideshop-admin/model/sysModel" 8 | "github.com/dgrijalva/jwt-go" 9 | "github.com/gin-gonic/gin" 10 | uuid "github.com/satori/go.uuid" 11 | "time" 12 | ) 13 | 14 | func JWTAuth() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | // 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localSstorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录 17 | token := c.Request.Header.Get("x-token") 18 | ModelToken := sysModel.JwtBlacklist{ 19 | Jwt: token, 20 | } 21 | if token == "" { 22 | servers.ReportFormat(c, false, "未登录或非法访问", gin.H{ 23 | "reload": true, 24 | }) 25 | c.Abort() 26 | return 27 | } 28 | if ModelToken.IsBlacklist(token) { 29 | servers.ReportFormat(c, false, "您的帐户异地登陆或令牌失效", gin.H{ 30 | "reload": true, 31 | }) 32 | c.Abort() 33 | return 34 | } 35 | j := NewJWT() 36 | // parseToken 解析token包含的信息 37 | claims, err := j.ParseToken(token) 38 | if err != nil { 39 | if err == TokenExpired { 40 | servers.ReportFormat(c, false, "授权已过期", gin.H{ 41 | "reload": true, 42 | }) 43 | c.Abort() 44 | return 45 | } 46 | servers.ReportFormat(c, false, err.Error(), gin.H{ 47 | "reload": true, 48 | }) 49 | c.Abort() 50 | return 51 | } 52 | c.Set("claims", claims) 53 | c.Next() 54 | } 55 | } 56 | 57 | type JWT struct { 58 | SigningKey []byte 59 | } 60 | 61 | var ( 62 | TokenExpired error = errors.New("Token is expired") 63 | TokenNotValidYet error = errors.New("Token not active yet") 64 | TokenMalformed error = errors.New("That's not even a token") 65 | TokenInvalid error = errors.New("Couldn't handle this token:") 66 | ) 67 | 68 | type CustomClaims struct { 69 | UUID uuid.UUID 70 | ID uint 71 | NickName string 72 | AuthorityId string 73 | jwt.StandardClaims 74 | } 75 | 76 | func NewJWT() *JWT { 77 | return &JWT{ 78 | []byte(config.GinVueAdminconfig.JWT.SigningKey), 79 | } 80 | } 81 | 82 | //创建一个token 83 | func (j *JWT) CreateToken(claims CustomClaims) (string, error) { 84 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 85 | return token.SignedString(j.SigningKey) 86 | } 87 | 88 | //解析 token 89 | func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { 90 | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { 91 | return j.SigningKey, nil 92 | }) 93 | if err != nil { 94 | if ve, ok := err.(*jwt.ValidationError); ok { 95 | if ve.Errors&jwt.ValidationErrorMalformed != 0 { 96 | return nil, TokenMalformed 97 | } else if ve.Errors&jwt.ValidationErrorExpired != 0 { 98 | // Token is expired 99 | return nil, TokenExpired 100 | } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { 101 | return nil, TokenNotValidYet 102 | } else { 103 | return nil, TokenInvalid 104 | } 105 | } 106 | } 107 | if token != nil { 108 | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { 109 | return claims, nil 110 | } 111 | return nil, TokenInvalid 112 | 113 | } else { 114 | return nil, TokenInvalid 115 | 116 | } 117 | 118 | } 119 | 120 | // 更新token 121 | func (j *JWT) RefreshToken(tokenString string) (string, error) { 122 | jwt.TimeFunc = func() time.Time { 123 | return time.Unix(0, 0) 124 | } 125 | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { 126 | return j.SigningKey, nil 127 | }) 128 | if err != nil { 129 | return "", err 130 | } 131 | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { 132 | jwt.TimeFunc = time.Now 133 | claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix() 134 | return j.CreateToken(*claims) 135 | } 136 | return "", TokenInvalid 137 | } 138 | -------------------------------------------------------------------------------- /QMPlusVuePage/vue.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | 5 | function resolve(dir) { 6 | return path.join(__dirname, dir) 7 | } 8 | module.exports = { 9 | // 基础配置 详情看文档 10 | publicPath: './', 11 | outputDir: 'dist', 12 | assetsDir: 'static', 13 | lintOnSave: process.env.NODE_ENV === 'development', 14 | productionSourceMap: false, 15 | devServer: { 16 | port: 8080, 17 | open: true, 18 | overlay: { 19 | warnings: false, 20 | errors: true 21 | }, 22 | proxy: { 23 | // 把key的路径代理到target位置 24 | // detail: https://cli.vuejs.org/config/#devserver-proxy 25 | [process.env.VUE_APP_BASE_API]: { //需要代理的路径 例如 '/api' 26 | target: `http://127.0.0.1:8888`, //代理到 目标路径 27 | changeOrigin: true, 28 | pathRewrite: { // 修改路径数据 29 | ['^' + process.env.VUE_APP_BASE_API]: '' // 举例 '^/api:""' 把路径中的/api字符串删除 30 | } 31 | } 32 | }, 33 | }, 34 | configureWebpack: { 35 | // @路径走src文件夹 36 | resolve: { 37 | alias: { 38 | '@': resolve('src') 39 | } 40 | } 41 | }, 42 | chainWebpack(config) { 43 | // set preserveWhitespace 44 | config.module 45 | .rule('vue') 46 | .use('vue-loader') 47 | .loader('vue-loader') 48 | .tap(options => { 49 | options.compilerOptions.preserveWhitespace = true 50 | return options 51 | }) 52 | .end() 53 | config 54 | // https://webpack.js.org/configuration/devtool/#development 55 | .when(process.env.NODE_ENV === 'development', 56 | config => config.devtool('cheap-source-map') 57 | ) 58 | 59 | config 60 | .when(process.env.NODE_ENV !== 'development', 61 | config => { 62 | config 63 | .plugin('ScriptExtHtmlWebpackPlugin') 64 | .after('html') 65 | .use('script-ext-html-webpack-plugin', [{ 66 | // `runtime` must same as runtimeChunk name. default is `runtime` 67 | inline: /runtime\..*\.js$/ 68 | }]) 69 | .end() 70 | config 71 | .optimization.splitChunks({ 72 | chunks: 'all', 73 | cacheGroups: { 74 | libs: { 75 | name: 'chunk-libs', 76 | test: /[\\/]node_modules[\\/]/, 77 | priority: 10, 78 | chunks: 'initial' // only package third parties that are initially dependent 79 | }, 80 | elementUI: { 81 | name: 'chunk-elementUI', // split elementUI into a single package 82 | priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app 83 | test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm 84 | }, 85 | commons: { 86 | name: 'chunk-commons', 87 | test: resolve('src/components'), // can customize your rules 88 | minChunks: 3, // minimum common number 89 | priority: 5, 90 | reuseExistingChunk: true 91 | } 92 | } 93 | }) 94 | config.optimization.runtimeChunk('single') 95 | } 96 | ) 97 | } 98 | } -------------------------------------------------------------------------------- /controller/api/exa_breakpoint_continue.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/dbModel" 7 | "github.com/gin-gonic/gin" 8 | "io/ioutil" 9 | "strconv" 10 | ) 11 | 12 | // @Tags ExaFileUploadAndDownload 13 | // @Summary 断点续传到服务器 14 | // @Security ApiKeyAuth 15 | // @accept multipart/form-data 16 | // @Produce application/json 17 | // @Param file formData file true "断点续传示例" 18 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"上传成功"}" 19 | // @Router /fileUploadAndDownload/breakpointContinue [post] 20 | 21 | func BreakpointContinue(c *gin.Context) { 22 | fileMd5 := c.Request.FormValue("fileMd5") 23 | fileName := c.Request.FormValue("fileName") 24 | chunkMd5 := c.Request.FormValue("chunkMd5") 25 | chunkNumber, _ := strconv.Atoi(c.Request.FormValue("chunkNumber")) 26 | chunkTotal, _ := strconv.Atoi(c.Request.FormValue("chunkTotal")) 27 | _, FileHeader, err := c.Request.FormFile("file") 28 | if err != nil { 29 | servers.ReportFormat(c, false, fmt.Sprintf("%v", err), gin.H{}) 30 | } else { 31 | f, err := FileHeader.Open() 32 | if err != nil { 33 | servers.ReportFormat(c, false, fmt.Sprintf("%v", err), gin.H{}) 34 | } else { 35 | cen, _ := ioutil.ReadAll(f) 36 | defer f.Close() 37 | if flag := servers.CheckMd5(cen, chunkMd5); flag { 38 | err, file := new(dbModel.ExaFile).FindOrCreateFile(fileMd5, fileName, chunkTotal) 39 | if err != nil { 40 | servers.ReportFormat(c, false, fmt.Sprintf("%v", err), gin.H{}) 41 | } else { 42 | err, pathc := servers.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5) 43 | if err != nil { 44 | servers.ReportFormat(c, false, fmt.Sprintf("%v", err), gin.H{}) 45 | } else { 46 | err = file.CreateFileChunk(pathc, chunkNumber) 47 | if err != nil { 48 | servers.ReportFormat(c, false, fmt.Sprintf("%v", err), gin.H{}) 49 | } else { 50 | servers.ReportFormat(c, true, "切片创建成功", gin.H{}) 51 | } 52 | } 53 | } 54 | } else { 55 | } 56 | } 57 | } 58 | } 59 | 60 | // @Tags ExaFileUploadAndDownload 61 | // @Summary 查找文件 62 | // @Security ApiKeyAuth 63 | // @accept multipart/form-data 64 | // @Produce application/json 65 | // @Param file params file true "查找文件" 66 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"查找成功"}" 67 | // @Router /fileUploadAndDownload/findFile [post] 68 | func FindFile(c *gin.Context) { 69 | fileMd5 := c.Query("fileMd5") 70 | fileName := c.Query("fileName") 71 | chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal")) 72 | err, file := new(dbModel.ExaFile).FindOrCreateFile(fileMd5, fileName, chunkTotal) 73 | if err != nil { 74 | servers.ReportFormat(c, false, fmt.Sprintf("查找失败:%v", err), gin.H{}) 75 | } else { 76 | servers.ReportFormat(c, true, "查找成功", gin.H{"file": file}) 77 | } 78 | } 79 | 80 | // @Tags ExaFileUploadAndDownload 81 | // @Summary 查找文件 82 | // @Security ApiKeyAuth 83 | // @accept multipart/form-data 84 | // @Produce application/json 85 | // @Param file params file true "查找文件" 86 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"查找成功"}" 87 | // @Router /fileUploadAndDownload/findFile [post] 88 | func BreakpointContinueFinish(c *gin.Context) { 89 | fileMd5 := c.Query("fileMd5") 90 | fileName := c.Query("fileName") 91 | err, filePath := servers.MakeFile(fileName, fileMd5) 92 | if err != nil { 93 | servers.ReportFormat(c, true, fmt.Sprintf("文件创建失败:%v", err), gin.H{}) 94 | } else { 95 | servers.ReportFormat(c, true, "文件创建成功", gin.H{"filePath": filePath}) 96 | } 97 | } 98 | 99 | // @Tags ExaFileUploadAndDownload 100 | // @Summary 删除切片 101 | // @Security ApiKeyAuth 102 | // @accept multipart/form-data 103 | // @Produce application/json 104 | // @Param file params file true "查找文件" 105 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"查找成功"}" 106 | // @Router /fileUploadAndDownload/removeChunk [post] 107 | func RemoveChunk(c *gin.Context) { 108 | fileMd5 := c.Query("fileMd5") 109 | fileName := c.Query("fileName") 110 | filePath := c.Query("filePath") 111 | err := servers.RemoveChunk(fileMd5) 112 | err = new(dbModel.ExaFile).DeleteFileChunk(fileMd5, fileName, filePath) 113 | if err != nil { 114 | servers.ReportFormat(c, true, fmt.Sprintf("缓存切片删除失败:%v", err), gin.H{}) 115 | } else { 116 | servers.ReportFormat(c, true, "缓存切片删除成功", gin.H{}) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /controller/api/sys_api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/model/modelInterface" 7 | "nideshop-admin/model/sysModel" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | type CreateApiParams struct { 12 | Path string `json:"path"` 13 | Description string `json:"description"` 14 | } 15 | 16 | type DeleteApiParams struct { 17 | ID uint `json:"id"` 18 | } 19 | 20 | // @Tags SysApi 21 | // @Summary 创建基础api 22 | // @Security ApiKeyAuth 23 | // @accept application/json 24 | // @Produce application/json 25 | // @Param data body api.CreateApiParams true "创建api" 26 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 27 | // @Router /api/createApi [post] 28 | func CreateApi(c *gin.Context) { 29 | var api sysModel.SysApi 30 | _ = c.BindJSON(&api) 31 | err := api.CreateApi() 32 | if err != nil { 33 | servers.ReportFormat(c, false, fmt.Sprintf("创建失败:%v", err), gin.H{}) 34 | } else { 35 | servers.ReportFormat(c, true, "创建成功", gin.H{}) 36 | } 37 | } 38 | 39 | // @Tags SysApi 40 | // @Summary 删除指定api 41 | // @Security ApiKeyAuth 42 | // @accept application/json 43 | // @Produce application/json 44 | // @Param data body sysModel.SysApi true "删除api" 45 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 46 | // @Router /api/deleteApi [post] 47 | func DeleteApi(c *gin.Context) { 48 | var a sysModel.SysApi 49 | _ = c.BindJSON(&a) 50 | err := a.DeleteApi() 51 | if err != nil { 52 | servers.ReportFormat(c, false, fmt.Sprintf("删除失败:%v", err), gin.H{}) 53 | } else { 54 | servers.ReportFormat(c, true, "删除成功", gin.H{}) 55 | } 56 | } 57 | 58 | type AuthAndPathIn struct { 59 | AuthorityId string `json:"authorityId"` 60 | ApiIds []uint `json:"apiIds"` 61 | } 62 | 63 | //条件搜索后端看此api 64 | 65 | // @Tags SysApi 66 | // @Summary 分页获取API列表 67 | // @Security ApiKeyAuth 68 | // @accept application/json 69 | // @Produce application/json 70 | // @Param data body modelInterface.PageInfo true "分页获取API列表" 71 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 72 | // @Router /api/getApiList [post] 73 | func GetApiList(c *gin.Context) { 74 | // 此结构体仅本方法使用 75 | type searchParams struct { 76 | sysModel.SysApi 77 | modelInterface.PageInfo 78 | } 79 | var sp searchParams 80 | _ = c.ShouldBindJSON(&sp) 81 | err, list, total := sp.SysApi.GetInfoList(sp.PageInfo) 82 | if err != nil { 83 | servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{}) 84 | } else { 85 | servers.ReportFormat(c, true, "获取数据成功", gin.H{ 86 | "list": list, 87 | "total": total, 88 | "page": sp.PageInfo.Page, 89 | "pageSize": sp.PageInfo.PageSize, 90 | }) 91 | 92 | } 93 | } 94 | 95 | // @Tags SysApi 96 | // @Summary 根据id获取api 97 | // @Security ApiKeyAuth 98 | // @accept application/json 99 | // @Produce application/json 100 | // @Param data body modelInterface.PageInfo true "分页获取用户列表" 101 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 102 | // @Router /api/getApiById [post] 103 | func GetApiById(c *gin.Context) { 104 | var idInfo GetById 105 | _ = c.BindJSON(&idInfo) 106 | err, api := new(sysModel.SysApi).GetApiById(idInfo.Id) 107 | if err != nil { 108 | servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{}) 109 | } else { 110 | servers.ReportFormat(c, true, "获取数据成功", gin.H{ 111 | "api": api, 112 | }) 113 | 114 | } 115 | } 116 | 117 | // @Tags SysApi 118 | // @Summary 创建基础api 119 | // @Security ApiKeyAuth 120 | // @accept application/json 121 | // @Produce application/json 122 | // @Param data body api.CreateApiParams true "创建api" 123 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 124 | // @Router /api/updataApi [post] 125 | func UpdataApi(c *gin.Context) { 126 | var api sysModel.SysApi 127 | _ = c.BindJSON(&api) 128 | err := api.UpdataApi() 129 | if err != nil { 130 | servers.ReportFormat(c, false, fmt.Sprintf("修改数据失败,%v", err), gin.H{}) 131 | } else { 132 | servers.ReportFormat(c, true, "修改数据成功", gin.H{}) 133 | } 134 | } 135 | 136 | // @Tags SysApi 137 | // @Summary 获取所有的Api 不分页 138 | // @Security ApiKeyAuth 139 | // @accept application/json 140 | // @Produce application/json 141 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 142 | // @Router /api/getAllApis [post] 143 | func GetAllApis(c *gin.Context) { 144 | err, apis := new(sysModel.SysApi).GetAllApis() 145 | if err != nil { 146 | servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{}) 147 | } else { 148 | servers.ReportFormat(c, true, "获取数据成功", gin.H{ 149 | "apis": apis, 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/example/upload/upload.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/layout/index.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 86 | 87 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/authority/authority.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 166 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/user/user.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 89 | 159 | -------------------------------------------------------------------------------- /controller/api/sys_menu.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "nideshop-admin/controller/servers" 6 | "nideshop-admin/middleware" 7 | "nideshop-admin/model/modelInterface" 8 | "nideshop-admin/model/sysModel" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // @Tags authorityAndMenu 13 | // @Summary 获取用户动态路由 14 | // @Security ApiKeyAuth 15 | // @Produce application/json 16 | // @Param data body api.RegistAndLoginStuct true "可以什么都不填" 17 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 18 | // @Router /menu/getMenu [post] 19 | func GetMenu(c *gin.Context) { 20 | claims, _ := c.Get("claims") 21 | waitUse := claims.(*middleware.CustomClaims) 22 | err, menus := new(sysModel.SysMenu).GetMenuTree(waitUse.AuthorityId) 23 | if err != nil { 24 | servers.ReportFormat(c, false, fmt.Sprintf("获取失败:%v", err), gin.H{"menus": menus}) 25 | } else { 26 | servers.ReportFormat(c, true, "获取成功", gin.H{"menus": menus}) 27 | } 28 | } 29 | 30 | // @Tags menu 31 | // @Summary 分页获取基础menu列表 32 | // @Security ApiKeyAuth 33 | // @accept application/json 34 | // @Produce application/json 35 | // @Param data body modelInterface.PageInfo true "分页获取基础menu列表" 36 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 37 | // @Router /menu/getMenuList [post] 38 | func GetMenuList(c *gin.Context) { 39 | var pageInfo modelInterface.PageInfo 40 | _ = c.BindJSON(&pageInfo) 41 | err, menuList, total := new(sysModel.SysBaseMenu).GetInfoList(pageInfo) 42 | if err != nil { 43 | servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{}) 44 | } else { 45 | servers.ReportFormat(c, true, "获取数据成功", gin.H{ 46 | "list": menuList, 47 | "total": total, 48 | "page": pageInfo.Page, 49 | "pageSize": pageInfo.PageSize, 50 | }) 51 | } 52 | } 53 | 54 | // @Tags menu 55 | // @Summary 新增菜单 56 | // @Security ApiKeyAuth 57 | // @accept application/json 58 | // @Produce application/json 59 | // @Param data body sysModel.SysBaseMenu true "新增菜单" 60 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 61 | // @Router /menu/addBaseMenu [post] 62 | func AddBaseMenu(c *gin.Context) { 63 | var addMenu sysModel.SysBaseMenu 64 | _ = c.BindJSON(&addMenu) 65 | err := addMenu.AddBaseMenu() 66 | if err != nil { 67 | servers.ReportFormat(c, false, fmt.Sprintf("添加失败,%v", err), gin.H{}) 68 | } else { 69 | servers.ReportFormat(c, true, fmt.Sprintf("添加成功,%v", err), gin.H{}) 70 | } 71 | } 72 | 73 | // @Tags authorityAndMenu 74 | // @Summary 获取用户动态路由 75 | // @Security ApiKeyAuth 76 | // @Produce application/json 77 | // @Param data body api.RegistAndLoginStuct true "可以什么都不填" 78 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 79 | // @Router /menu/getBaseMenuTree [post] 80 | func GetBaseMenuTree(c *gin.Context) { 81 | err, menus := new(sysModel.SysBaseMenu).GetBaseMenuTree() 82 | if err != nil { 83 | servers.ReportFormat(c, false, fmt.Sprintf("获取失败:%v", err), gin.H{"menus": menus}) 84 | } else { 85 | servers.ReportFormat(c, true, "获取成功", gin.H{"menus": menus}) 86 | } 87 | } 88 | 89 | type AddMenuAuthorityInfo struct { 90 | Menus []sysModel.SysBaseMenu 91 | AuthorityId string 92 | } 93 | 94 | // @Tags authorityAndMenu 95 | // @Summary 增加menu和角色关联关系 96 | // @Security ApiKeyAuth 97 | // @accept application/json 98 | // @Produce application/json 99 | // @Param data body api.AddMenuAuthorityInfo true "增加menu和角色关联关系" 100 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 101 | // @Router /menu/addMenuAuthority [post] 102 | func AddMenuAuthority(c *gin.Context) { 103 | var addMenuAuthorityInfo AddMenuAuthorityInfo 104 | _ = c.BindJSON(&addMenuAuthorityInfo) 105 | err := new(sysModel.SysMenu).AddMenuAuthority(addMenuAuthorityInfo.Menus, addMenuAuthorityInfo.AuthorityId) 106 | if err != nil { 107 | servers.ReportFormat(c, false, fmt.Sprintf("添加失败,%v", err), gin.H{}) 108 | } else { 109 | servers.ReportFormat(c, true, fmt.Sprintf("添加成功,%v", err), gin.H{}) 110 | } 111 | } 112 | 113 | type AuthorityIdInfo struct { 114 | AuthorityId string 115 | } 116 | 117 | // @Tags authorityAndMenu 118 | // @Summary 获取指定角色menu 119 | // @Security ApiKeyAuth 120 | // @accept application/json 121 | // @Produce application/json 122 | // @Param data body api.AuthorityIdInfo true "增加menu和角色关联关系" 123 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 124 | // @Router /menu/addMenuAuthority [post] 125 | func GetMenuAuthority(c *gin.Context) { 126 | var authorityIdInfo AuthorityIdInfo 127 | _ = c.BindJSON(&authorityIdInfo) 128 | err, menus := new(sysModel.SysMenu).GetMenuAuthority(authorityIdInfo.AuthorityId) 129 | if err != nil { 130 | servers.ReportFormat(c, false, fmt.Sprintf("获取失败:%v", err), gin.H{"menus": menus}) 131 | } else { 132 | servers.ReportFormat(c, true, "获取成功", gin.H{"menus": menus}) 133 | } 134 | } 135 | 136 | type IdInfo struct { 137 | Id float64 138 | } 139 | 140 | // @Tags menu 141 | // @Summary 删除菜单 142 | // @Security ApiKeyAuth 143 | // @accept application/json 144 | // @Produce application/json 145 | // @Param data body api.IdInfo true "删除菜单" 146 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 147 | // @Router /menu/deleteBaseMenu [post] 148 | func DeleteBaseMenu(c *gin.Context) { 149 | var idInfo IdInfo 150 | _ = c.BindJSON(&idInfo) 151 | err := new(sysModel.SysBaseMenu).DeleteBaseMenu(idInfo.Id) 152 | if err != nil { 153 | servers.ReportFormat(c, false, fmt.Sprintf("删除失败:%v", err), gin.H{}) 154 | } else { 155 | servers.ReportFormat(c, true, "删除成功", gin.H{}) 156 | } 157 | } 158 | 159 | // @Tags menu 160 | // @Summary 更新菜单 161 | // @Security ApiKeyAuth 162 | // @accept application/json 163 | // @Produce application/json 164 | // @Param data body sysModel.SysBaseMenu true "更新菜单" 165 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 166 | // @Router /menu/updataBaseMen [post] 167 | func UpdataBaseMenu(c *gin.Context) { 168 | var menu sysModel.SysBaseMenu 169 | _ = c.BindJSON(&menu) 170 | err := menu.UpdataBaseMenu() 171 | if err != nil { 172 | servers.ReportFormat(c, false, fmt.Sprintf("修改失败:%v", err), gin.H{}) 173 | } else { 174 | servers.ReportFormat(c, true, "修改成功", gin.H{}) 175 | } 176 | } 177 | 178 | type GetById struct { 179 | Id float64 `json:"id"` 180 | } 181 | 182 | // @Tags menu 183 | // @Summary 根据id获取菜单 184 | // @Security ApiKeyAuth 185 | // @accept application/json 186 | // @Produce application/json 187 | // @Param data body api.GetById true "根据id获取菜单" 188 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" 189 | // @Router /menu/getBaseMenuById [post] 190 | func GetBaseMenuById(c *gin.Context) { 191 | var idInfo GetById 192 | _ = c.BindJSON(&idInfo) 193 | err, menu := new(sysModel.SysBaseMenu).GetBaseMenuById(idInfo.Id) 194 | if err != nil { 195 | servers.ReportFormat(c, false, fmt.Sprintf("查询失败:%v", err), gin.H{}) 196 | } else { 197 | servers.ReportFormat(c, true, "查询成功", gin.H{"menu": menu}) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/example/form/form.vue: -------------------------------------------------------------------------------- 1 | 135 | 188 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/menu/menu.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 212 | -------------------------------------------------------------------------------- /QMPlusVuePage/src/view/superAdmin/api/api.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 79 | 229 | --------------------------------------------------------------------------------