Prechádzať zdrojové kódy

Merge pull request #65 from flipped-aurora/gin-vue-admin_v2_dev

Gin vue admin v2 dev
rainyan 4 rokov pred
rodič
commit
2527251b3b

+ 119 - 0
.dockerignore

@@ -0,0 +1,119 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Node template
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.pnp.*
+
+**/node_modules

+ 41 - 0
Dockerfile

@@ -0,0 +1,41 @@
+FROM golang:alpine as builder
+RUN  apk add --update --no-cache yarn make g++
+RUN yarn global add cross-env node-sass
+
+ENV GOPROXY=https://goproxy.cn,https://goproxy.io,direct \
+    GO111MODULE=on \
+    CGO_ENABLED=1
+WORKDIR /go/src/gin-vue-admin
+RUN go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct
+COPY server/ ./
+RUN go env && go list && go build -v -a -ldflags "-extldflags \"-static\" " -o gvadmin .
+
+WORKDIR /web
+COPY web/ ./
+RUN yarn install && yarn run build
+
+
+FROM nginx:alpine
+LABEL MAINTAINER="rikugun"
+
+RUN apk add --no-cache  gettext tzdata   && \
+    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
+    echo "Asia/Shanghai" >  /etc/timezone && \
+    date && \
+    apk del tzdata
+
+COPY docker/etc/nginx/nginx.conf.tpl /etc/nginx/nginx.conf.tpl
+WORKDIR /app
+#copy web
+COPY --from=builder /web/dist/ /var/www/
+#copy go app
+COPY --from=builder /go/src/gin-vue-admin/gvadmin ./
+COPY --from=builder /go/src/gin-vue-admin/db.db ./
+COPY --from=builder /go/src/gin-vue-admin/config.yaml ./
+COPY --from=builder /go/src/gin-vue-admin/resource ./resource
+COPY docker/docker-start.sh ./
+
+ENV API_SERVER="http://localhost:8888/"
+EXPOSE 80
+
+ENTRYPOINT ["./docker-start.sh"]

+ 1 - 1
README-zh_CN.md

@@ -68,7 +68,7 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm
 ```
 - node版本 > v8.6.0
 - golang版本 >= v1.11
-- IDE推荐:Golang
+- IDE推荐:Goland
 - 各位在clone项目以后,把db文件导入自己创建的库后,最好前往七牛云申请自己的空间地址。
 - 替换掉项目中的七牛云公钥,私钥,仓名和默认url地址,以免发生测试文件数据错乱
 ```

+ 4 - 0
docker/docker-start.sh

@@ -0,0 +1,4 @@
+#!/bin/sh
+envsubst '$API_SERVER' < /etc/nginx/nginx.conf.tpl > /etc/nginx/nginx.conf
+env nginx
+./gvadmin

+ 56 - 0
docker/etc/nginx/nginx.conf.tpl

@@ -0,0 +1,56 @@
+daemon on;
+worker_processes  50;
+#error_log /dev/stdout warn;
+error_log  /var/log/nginx/error.log error;
+
+
+events {
+    worker_connections 1024;
+}
+
+
+http {
+    include       mime.types;
+    default_type  application/octet-stream;
+    # See http://licson.net/post/optimizing-nginx-for-large-file-delivery/ for more detail
+        # This optimizes the server for HLS fragment delivery
+    sendfile off;
+    #tcp_nopush on;
+    keepalive_timeout  65;
+    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+                      '$status $body_bytes_sent "$http_referer" '
+                      '"$http_user_agent" "$http_x_forwarded_for"';
+    #access_log /dev/stdout combined;
+
+#     ssl_ciphers         HIGH:!aNULL:!MD5;
+#     ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
+#     ssl_session_cache   shared:SSL:10m;
+#     ssl_session_timeout 10m;
+
+server {
+        listen 80;
+
+        # Uncomment these lines to enable SSL.
+        # Update the ssl paths with your own certificate and private key.
+        # listen 443 ssl;
+        # ssl_certificate     /opt/certs/example.com.crt;
+        # ssl_certificate_key /opt/certs/example.com.key;
+        location / {
+          root /var/www;
+          try_files $uri $uri/ /index.html;
+          index  index.html;
+        }
+
+        location /v1/ {
+          proxy_set_header X-Forwarded-Proto $scheme;
+          proxy_set_header X-Forwarded-Port $server_port;
+          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+          proxy_set_header Upgrade $http_upgrade;
+          proxy_set_header Connection "upgrade";
+          proxy_set_header  Host  $host;
+          proxy_pass ${API_SERVER} ;
+        }
+
+}
+
+}

+ 24 - 0
server/Dockerfile

@@ -0,0 +1,24 @@
+FROM centos:7.6.1810
+
+# 设置go mod proxy 国内代理
+# 设置golang path
+ENV GOPROXY=https://goproxy.io GOPATH=/gopath PATH="${PATH}:/usr/local/go/bin"
+# 定义使用的Golang 版本
+ARG GO_VERSION=1.13.3
+
+# 安装 golang 1.13.3
+RUN wget "https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz" && \
+    rm -rf /usr/local/go && \
+    tar -C /usr/local -xzf "go$GO_VERSION.linux-amd64.tar.gz" && \
+    rm -rf *.tar.gz && \
+    go version && go env;
+
+
+WORKDIR $GOPATH
+COPY . gin-vue
+
+RUN cd server && go build -o app;
+
+EXPOSE 8888
+
+CMD ["gin-vue/app"]

+ 19 - 0
server/api/v1/sys_authority.go

@@ -29,6 +29,25 @@ func CreateAuthority(c *gin.Context) {
 	}
 }
 
+// @Tags authority
+// @Summary 拷贝角色
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body response.SysAuthorityCopyResponse true "拷贝角色"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"拷贝成功"}"
+// @Router /authority/copyAuthority [post]
+func CopyAuthority(c *gin.Context) {
+	var copyInfo resp.SysAuthorityCopyResponse
+	_ = c.ShouldBindJSON(&copyInfo)
+	err, authBack := service.CopyAuthority(copyInfo)
+	if err != nil {
+		response.FailWithMessage(fmt.Sprintf("创建失败,%v", err), c)
+	} else {
+		response.OkWithData(resp.SysAuthorityResponse{Authority: authBack}, c)
+	}
+}
+
 // @Tags authority
 // @Summary 删除角色
 // @Security ApiKeyAuth

+ 4 - 4
server/config.yaml

@@ -13,14 +13,14 @@ mysql:
     username: root
     password: 'Aa@6447985'
     path: '127.0.0.1:3306'
-    db-name: 'qmplus'
+    db-name: 'qmPlus'
     config: 'charset=utf8&parseTime=True&loc=Local'
     max-idle-conns: 10
     max-open-conns: 10
     log-mode: true
 #sqlite 配置
 sqlite:
-    path: db/db.db
+    path: db.db
     log-mode: true
     config: 'loc=Asia/Shanghai'
 # oss configuration
@@ -31,7 +31,7 @@ qiniu:
 # redis configuration
 redis:
     addr: '127.0.0.1:6379'
-    passwprd: ''
+    password: ''
     db: 0
 
 # system configuration
@@ -39,7 +39,7 @@ system:
     use-multipoint: false
     env: 'public'  # Change to "develop" to skip authentication for development mode
     addr: 8888
-    db-type: "mysql"
+    db-type: "mysql"  # support mysql/sqlite
 
 # captcha configuration
 captcha:

+ 40 - 40
server/config/config.go

@@ -1,70 +1,70 @@
 package config
 
 type Server struct {
-	Mysql   Mysql   `mapstructure:"mysql" json:"mysql"`
-	Sqlite  Sqlite  `mapstructure:"sqlite" json:"sqlite"`
-	Qiniu   Qiniu   `mapstructure:"qiniu" json:"qiniu"`
-	Casbin  Casbin  `mapstructure:"casbin" json:"casbin"`
-	Redis   Redis   `mapstructure:"redis" json:"redis"`
-	System  System  `mapstructure:"system" json:"system"`
-	JWT     JWT     `mapstructure:"jwt" json:"jwt"`
-	Captcha Captcha `mapstructure:"captcha" json:"captcha"`
-	Log     Log     `mapstructure:"log" json:"log"`
+	Mysql   Mysql   `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
+	Sqlite  Sqlite  `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"`
+	Qiniu   Qiniu   `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
+	Casbin  Casbin  `mapstructure:"casbin" json:"casbin" yaml:"casbin"`
+	Redis   Redis   `mapstructure:"redis" json:"redis" yaml:"redis"`
+	System  System  `mapstructure:"system" json:"system" yaml:"system"`
+	JWT     JWT     `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
+	Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
+	Log     Log     `mapstructure:"log" json:"log" yaml:"log"`
 }
 
 type System struct {
-	UseMultipoint bool   `mapstructure:"use-multipoint" json:"useMultipoint"`
-	Env           string `mapstructure:"env" json:"env"`
-	Addr          int    `mapstructure:"addr" json:"addr"`
-	DbType        string `mapstructure:"db-type" json:"dbType"`
+	UseMultipoint bool   `mapstructure:"use-multipoint" json:"useMultipoint" yaml:"use-multipoint"`
+	Env           string `mapstructure:"env" json:"env" yaml:"env"`
+	Addr          int    `mapstructure:"addr" json:"addr" yaml:"addr"`
+	DbType        string `mapstructure:"db-type" json:"dbType" yaml:"db-type"`
 }
 
 type JWT struct {
-	SigningKey string `mapstructure:"signing-key" json:"signingKey"`
+	SigningKey string `mapstructure:"signing-key" json:"signingKey" yaml:"signing-key"`
 }
 
 type Casbin struct {
-	ModelPath string `mapstructure:"model-path" json:"modelPath"`
+	ModelPath string `mapstructure:"model-path" json:"modelPath" yaml:"model-path"`
 }
 
 type Mysql struct {
-	Username     string `mapstructure:"username" json:"username"`
-	Password     string `mapstructure:"password" json:"password"`
-	Path         string `mapstructure:"path" json:"path"`
-	Dbname       string `mapstructure:"db-name" json:"dbname"`
-	Config       string `mapstructure:"config" json:"config"`
-	MaxIdleConns int    `mapstructure:"max-idle-conns" json:"maxIdleConns"`
-	MaxOpenConns int    `mapstructure:"max-open-conns" json:"maxOpenConns"`
-	LogMode      bool   `mapstructure:"log-mode" json:"logMode"`
+	Username     string `mapstructure:"username" json:"username" yaml:"username"`
+	Password     string `mapstructure:"password" json:"password" yaml:"password"`
+	Path         string `mapstructure:"path" json:"path" yaml:"path"`
+	Dbname       string `mapstructure:"db-name" json:"dbname" yaml:"db-name"`
+	Config       string `mapstructure:"config" json:"config" yaml:"config"`
+	MaxIdleConns int    `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"`
+	MaxOpenConns int    `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"`
+	LogMode      bool   `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`
 }
 
 type Redis struct {
-	Addr     string `mapstructure:"addr" json:"addr"`
-	Password string `mapstructure:"password" json:"password"`
-	DB       int    `mapstructure:"db" json:"db"`
+	Addr     string `mapstructure:"addr" json:"addr" yaml:"addr"`
+	Password string `mapstructure:"password" json:"password" yaml:"password"`
+	DB       int    `mapstructure:"db" json:"db" yaml:"db"`
 }
 type Qiniu struct {
-	AccessKey string `mapstructure:"access-key" json:"accessKey"`
-	SecretKey string `mapstructure:"secret-key" json:"secretKey"`
+	AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"`
+	SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`
 }
 
 type Captcha struct {
-	KeyLong   int `mapstructure:"key-long" json:"keyLong"`
-	ImgWidth  int `mapstructure:"img-width" json:"imgWidth"`
-	ImgHeight int `mapstructure:"img-height" json:"imgHeight"`
+	KeyLong   int `mapstructure:"key-long" json:"keyLong" yaml:"key-long"`
+	ImgWidth  int `mapstructure:"img-width" json:"imgWidth" yaml:"img-width"`
+	ImgHeight int `mapstructure:"img-height" json:"imgHeight" yaml:"img-height"`
 }
 
 type Log struct {
-	Prefix  string `mapstructure:"prefix" json:"prefix"`
-	LogFile bool   `mapstructure:"log-file" json:"logFile"`
-	Stdout  string `mapstructure:"stdout" json:"stdout"`
-	File    string `mapstructure:"file" json:"file"`
+	Prefix  string `mapstructure:"prefix" json:"prefix" yaml:"prefix"`
+	LogFile bool   `mapstructure:"log-file" json:"logFile" yaml:"log-file"`
+	Stdout  string `mapstructure:"stdout" json:"stdout" yaml:"stdout"`
+	File    string `mapstructure:"file" json:"file" yaml:"file"`
 }
 
 type Sqlite struct {
-	Username string `mapstructure:"username" json:"username"`
-	Password string `mapstructure:"password" json:"password"`
-	Path     string `mapstructure:"path" json:"path"`
-	Config   string `mapstructure:"config" json:"config"`
-	LogMode  bool   `mapstructure:"log-mode" json:"logMode"`
+	Username string `mapstructure:"username" json:"username" yaml:"username"`
+	Password string `mapstructure:"password" json:"password" yaml:"password"`
+	Path     string `mapstructure:"path" json:"path" yaml:"path"`
+	Config   string `mapstructure:"config" json:"config" yaml:"config"`
+	LogMode  bool   `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`
 }

BIN
server/db/db.db → server/db.db


+ 147 - 5
server/docs/docs.go

@@ -1,6 +1,6 @@
 // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 // This file was generated by swaggo/swag at
-// 2020-04-15 09:37:13.5084762 +0800 CST m=+0.126566601
+// 2020-04-20 14:50:58.4141704 +0800 CST m=+0.881639701
 
 package docs
 
@@ -248,6 +248,45 @@ var doc = `{
                 }
             }
         },
+        "/authority/copyAuthority": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "authority"
+                ],
+                "summary": "拷贝角色",
+                "parameters": [
+                    {
+                        "description": "拷贝角色",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "object",
+                            "$ref": "#/definitions/response.SysAuthorityCopyResponse"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"success\":true,\"data\":{},\"msg\":\"拷贝成功\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
         "/authority/createAuthority": {
             "post": {
                 "security": [
@@ -404,6 +443,45 @@ var doc = `{
                 }
             }
         },
+        "/authority/updateAuthority": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "authority"
+                ],
+                "summary": "设置角色资源权限",
+                "parameters": [
+                    {
+                        "description": "设置角色资源权限",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "object",
+                            "$ref": "#/definitions/model.SysAuthority"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"success\":true,\"data\":{},\"msg\":\"设置成功\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
         "/autoCode/createTemp": {
             "post": {
                 "security": [
@@ -1588,6 +1666,45 @@ var doc = `{
                 }
             }
         },
+        "/user/deleteUser": {
+            "delete": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "SysUser"
+                ],
+                "summary": "删除用户",
+                "parameters": [
+                    {
+                        "description": "删除用户",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "object",
+                            "$ref": "#/definitions/request.SetUserAuth"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
         "/user/getUserList": {
             "post": {
                 "security": [
@@ -2072,12 +2189,18 @@ var doc = `{
                 "component": {
                     "type": "string"
                 },
+                "defaultMenu": {
+                    "type": "boolean"
+                },
                 "hidden": {
                     "type": "boolean"
                 },
                 "icon": {
                     "type": "string"
                 },
+                "keepAlive": {
+                    "type": "boolean"
+                },
                 "name": {
                     "type": "string"
                 },
@@ -2317,6 +2440,25 @@ var doc = `{
                     "type": "string"
                 }
             }
+        },
+        "response.SysAuthorityCopyResponse": {
+            "type": "object",
+            "properties": {
+                "authority": {
+                    "type": "object",
+                    "$ref": "#/definitions/model.SysAuthority"
+                },
+                "oldAuthorityId": {
+                    "type": "integer"
+                }
+            }
+        }
+    },
+    "securityDefinitions": {
+        "ApiKeyAuth": {
+            "type": "apiKey",
+            "name": "x-token",
+            "in": "header"
         }
     }
 }`
@@ -2332,12 +2474,12 @@ type swaggerInfo struct {
 
 // SwaggerInfo holds exported Swagger Info so clients can modify it
 var SwaggerInfo = swaggerInfo{
-	Version:     "",
+	Version:     "0.0.1",
 	Host:        "",
-	BasePath:    "",
+	BasePath:    "/",
 	Schemes:     []string{},
-	Title:       "",
-	Description: "",
+	Title:       "Swagger Example API",
+	Description: "This is a sample Server pets",
 }
 
 type s struct{}

+ 147 - 1
server/docs/swagger.json

@@ -1,9 +1,13 @@
 {
     "swagger": "2.0",
     "info": {
+        "description": "This is a sample Server pets",
+        "title": "Swagger Example API",
         "contact": {},
-        "license": {}
+        "license": {},
+        "version": "0.0.1"
     },
+    "basePath": "/",
     "paths": {
         "/api/createApi": {
             "post": {
@@ -227,6 +231,45 @@
                 }
             }
         },
+        "/authority/copyAuthority": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "authority"
+                ],
+                "summary": "拷贝角色",
+                "parameters": [
+                    {
+                        "description": "拷贝角色",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "object",
+                            "$ref": "#/definitions/response.SysAuthorityCopyResponse"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"success\":true,\"data\":{},\"msg\":\"拷贝成功\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
         "/authority/createAuthority": {
             "post": {
                 "security": [
@@ -383,6 +426,45 @@
                 }
             }
         },
+        "/authority/updateAuthority": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "authority"
+                ],
+                "summary": "设置角色资源权限",
+                "parameters": [
+                    {
+                        "description": "设置角色资源权限",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "object",
+                            "$ref": "#/definitions/model.SysAuthority"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"success\":true,\"data\":{},\"msg\":\"设置成功\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
         "/autoCode/createTemp": {
             "post": {
                 "security": [
@@ -1567,6 +1649,45 @@
                 }
             }
         },
+        "/user/deleteUser": {
+            "delete": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "SysUser"
+                ],
+                "summary": "删除用户",
+                "parameters": [
+                    {
+                        "description": "删除用户",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "object",
+                            "$ref": "#/definitions/request.SetUserAuth"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
         "/user/getUserList": {
             "post": {
                 "security": [
@@ -2051,12 +2172,18 @@
                 "component": {
                     "type": "string"
                 },
+                "defaultMenu": {
+                    "type": "boolean"
+                },
                 "hidden": {
                     "type": "boolean"
                 },
                 "icon": {
                     "type": "string"
                 },
+                "keepAlive": {
+                    "type": "boolean"
+                },
                 "name": {
                     "type": "string"
                 },
@@ -2296,6 +2423,25 @@
                     "type": "string"
                 }
             }
+        },
+        "response.SysAuthorityCopyResponse": {
+            "type": "object",
+            "properties": {
+                "authority": {
+                    "type": "object",
+                    "$ref": "#/definitions/model.SysAuthority"
+                },
+                "oldAuthorityId": {
+                    "type": "integer"
+                }
+            }
+        }
+    },
+    "securityDefinitions": {
+        "ApiKeyAuth": {
+            "type": "apiKey",
+            "name": "x-token",
+            "in": "header"
         }
     }
 }

+ 93 - 0
server/docs/swagger.yaml

@@ -1,3 +1,4 @@
+basePath: /
 definitions:
   config.Captcha:
     properties:
@@ -217,10 +218,14 @@ definitions:
         type: array
       component:
         type: string
+      defaultMenu:
+        type: boolean
       hidden:
         type: boolean
       icon:
         type: string
+      keepAlive:
+        type: boolean
       name:
         type: string
       parentId:
@@ -380,9 +385,20 @@ definitions:
       uuid:
         type: string
     type: object
+  response.SysAuthorityCopyResponse:
+    properties:
+      authority:
+        $ref: '#/definitions/model.SysAuthority'
+        type: object
+      oldAuthorityId:
+        type: integer
+    type: object
 info:
   contact: {}
+  description: This is a sample Server pets
   license: {}
+  title: Swagger Example API
+  version: 0.0.1
 paths:
   /api/createApi:
     post:
@@ -520,6 +536,30 @@ paths:
       summary: 创建基础api
       tags:
       - SysApi
+  /authority/copyAuthority:
+    post:
+      consumes:
+      - application/json
+      parameters:
+      - description: 拷贝角色
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/response.SysAuthorityCopyResponse'
+          type: object
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: '{"success":true,"data":{},"msg":"拷贝成功"}'
+          schema:
+            type: string
+      security:
+      - ApiKeyAuth: []
+      summary: 拷贝角色
+      tags:
+      - authority
   /authority/createAuthority:
     post:
       consumes:
@@ -616,6 +656,30 @@ paths:
       summary: 设置角色资源权限
       tags:
       - authority
+  /authority/updateAuthority:
+    post:
+      consumes:
+      - application/json
+      parameters:
+      - description: 设置角色资源权限
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/model.SysAuthority'
+          type: object
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: '{"success":true,"data":{},"msg":"设置成功"}'
+          schema:
+            type: string
+      security:
+      - ApiKeyAuth: []
+      summary: 设置角色资源权限
+      tags:
+      - authority
   /autoCode/createTemp:
     post:
       consumes:
@@ -1343,6 +1407,30 @@ paths:
       summary: 用户修改密码
       tags:
       - SysUser
+  /user/deleteUser:
+    delete:
+      consumes:
+      - application/json
+      parameters:
+      - description: 删除用户
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/request.SetUserAuth'
+          type: object
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: '{"success":true,"data":{},"msg":"修改成功"}'
+          schema:
+            type: string
+      security:
+      - ApiKeyAuth: []
+      summary: 删除用户
+      tags:
+      - SysUser
   /user/getUserList:
     post:
       consumes:
@@ -1438,4 +1526,9 @@ paths:
       summary: 注册工作流
       tags:
       - workflow
+securityDefinitions:
+  ApiKeyAuth:
+    in: header
+    name: x-token
+    type: apiKey
 swagger: "2.0"

+ 1 - 0
server/initialize/router.go

@@ -11,6 +11,7 @@ import (
 )
 
 //初始化总路由
+
 func Routers() *gin.Engine {
 	var Router = gin.Default()
 

+ 7 - 0
server/main.go

@@ -7,6 +7,13 @@ import (
 	//"runtime"
 )
 
+// @title Swagger Example API
+// @version 0.0.1
+// @description This is a sample Server pets
+// @securityDefinitions.apikey ApiKeyAuth
+// @in header
+// @name x-token
+// @BasePath /
 func main() {
 	switch global.GVA_CONFIG.System.DbType {
 	case "mysql":

+ 5 - 0
server/model/response/sys_authority.go

@@ -5,3 +5,8 @@ import "gin-vue-admin/model"
 type SysAuthorityResponse struct {
 	Authority model.SysAuthority `json:"authority"`
 }
+
+type SysAuthorityCopyResponse struct {
+	Authority      model.SysAuthority `json:"authority"`
+	OldAuthorityId string             `json:"oldAuthorityId"`
+}

+ 3 - 1
server/model/response/sys_casbin.go

@@ -1,5 +1,7 @@
 package response
 
+import "gin-vue-admin/model/request"
+
 type PolicyPathResponse struct {
-	Paths []map[string]string `json:"paths"`
+	Paths []request.CasbinInfo `json:"paths"`
 }

+ 1 - 0
server/router/sys_authority.go

@@ -12,6 +12,7 @@ func InitAuthorityRouter(Router *gin.RouterGroup) {
 		AuthorityRouter.POST("createAuthority", v1.CreateAuthority)   //创建角色
 		AuthorityRouter.POST("deleteAuthority", v1.DeleteAuthority)   //删除角色
 		AuthorityRouter.PUT("updateAuthority", v1.UpdateAuthority)    //更新角色
+		AuthorityRouter.POST("copyAuthority", v1.CopyAuthority)       //更新角色
 		AuthorityRouter.POST("getAuthorityList", v1.GetAuthorityList) //获取角色列表
 		AuthorityRouter.POST("setDataAuthority", v1.SetDataAuthority) //设置角色资源权限
 	}

+ 1 - 0
server/service/exa_file_upload_download.go

@@ -53,6 +53,7 @@ func GetFileRecordInfoList(info request.PageInfo) (err error, list interface{},
 	offset := info.PageSize * (info.Page - 1)
 	db := global.GVA_DB
 	var fileLists []model.ExaFileUploadAndDownload
+	err = db.Find(&fileLists).Count(&total).Error
 	err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&fileLists).Error
 	return err, fileLists, total
 }

+ 29 - 0
server/service/sys_authority.go

@@ -5,6 +5,8 @@ import (
 	"gin-vue-admin/global"
 	"gin-vue-admin/model"
 	"gin-vue-admin/model/request"
+	"gin-vue-admin/model/response"
+	"strconv"
 )
 
 // @title    CreateAuthority
@@ -19,6 +21,33 @@ func CreateAuthority(auth model.SysAuthority) (err error, authority model.SysAut
 	return err, auth
 }
 
+// @title    CopyAuthority
+// @description   复制一个角色
+// @auth                     (2020/04/05  20:22)
+// @param     copyInfo        response.SysAuthorityCopyResponse
+// @return                    error
+// @return    authority       model.SysAuthority
+
+func CopyAuthority(copyInfo response.SysAuthorityCopyResponse) (err error, authority model.SysAuthority) {
+	copyInfo.Authority.Children = []model.SysAuthority{}
+	err, menus := GetMenuAuthority(copyInfo.OldAuthorityId)
+	var baseMenu []model.SysBaseMenu
+	for _, v := range menus {
+		intNum, _ := strconv.Atoi(v.MenuId)
+		v.SysBaseMenu.ID = uint(intNum)
+		baseMenu = append(baseMenu, v.SysBaseMenu)
+	}
+	copyInfo.Authority.SysBaseMenus = baseMenu
+	err = global.GVA_DB.Create(&copyInfo.Authority).Error
+
+	paths := GetPolicyPathByAuthorityId(copyInfo.OldAuthorityId)
+	err = UpdateCasbin(copyInfo.Authority.AuthorityId, paths)
+	if err != nil {
+		_ = DeleteAuthority(&copyInfo.Authority)
+	}
+	return err, copyInfo.Authority
+}
+
 // @title    UpdateAuthority
 // @description   更改一个角色
 // @auth                     (2020/04/05  20:22)

+ 4 - 4
server/service/sys_casbin.go

@@ -71,13 +71,13 @@ func UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod
 // @param     authorityId     string
 // @return                    []string
 
-func GetPolicyPathByAuthorityId(authorityId string) (pathMaps []map[string]string) {
+func GetPolicyPathByAuthorityId(authorityId string) (pathMaps []request.CasbinInfo) {
 	e := Casbin()
 	list := e.GetFilteredPolicy(0, authorityId)
 	for _, v := range list {
-		pathMaps = append(pathMaps, map[string]string{
-			"path":   v[1],
-			"method": v[2],
+		pathMaps = append(pathMaps, request.CasbinInfo{
+			Path:   v[1],
+			Method: v[2],
 		})
 	}
 	return pathMaps

+ 1 - 0
server/service/sys_user.go

@@ -79,6 +79,7 @@ func GetUserInfoList(info request.PageInfo) (err error, list interface{}, total
 	offset := info.PageSize * (info.Page - 1)
 	db := global.GVA_DB
 	var userList []model.SysUser
+	err = db.Find(&userList).Count(&total).Error
 	err = db.Limit(limit).Offset(offset).Preload("Authority").Find(&userList).Error
 	return err, userList, total
 }

+ 320 - 0
web/src/view/dashboard/component/todoList/index.scss

@@ -0,0 +1,320 @@
+.todoapp {
+  font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
+  line-height: 1.4em;
+  color: #4d4d4d;
+  min-width: 230px;
+  max-width: 666px;
+  margin: 0 auto ;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  font-weight: 300;
+  background: #fff;
+  z-index: 1;
+  position: relative;
+  button {
+    margin: 0;
+    padding: 0;
+    border: 0;
+    background: none;
+    font-size: 100%;
+    vertical-align: baseline;
+    font-family: inherit;
+    font-weight: inherit;
+    color: inherit;
+    -webkit-appearance: none;
+    appearance: none;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+  :focus {
+    outline: 0;
+  }
+  .hidden {
+    display: none;
+  }
+  .todoapp {
+    background: #fff;
+    margin: 130px 0 40px 0;
+    position: relative;
+    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
+  }
+  .todoapp input::-webkit-input-placeholder {
+    font-style: italic;
+    font-weight: 300;
+    color: #e6e6e6;
+  }
+  .todoapp input::-moz-placeholder {
+    font-style: italic;
+    font-weight: 300;
+    color: #e6e6e6;
+  }
+  .todoapp input::input-placeholder {
+    font-style: italic;
+    font-weight: 300;
+    color: #e6e6e6;
+  }
+  .todoapp h1 {
+    position: absolute;
+    top: -155px;
+    width: 100%;
+    font-size: 100px;
+    font-weight: 100;
+    text-align: center;
+    color: rgba(175, 47, 47, 0.15);
+    -webkit-text-rendering: optimizeLegibility;
+    -moz-text-rendering: optimizeLegibility;
+    text-rendering: optimizeLegibility;
+  }
+  .new-todo,
+  .edit {
+    position: relative;
+    margin: 0;
+    width: 100%;
+    font-size: 18px;
+    font-family: inherit;
+    font-weight: inherit;
+    line-height: 1.4em;
+    border: 0;
+    color: inherit;
+    padding: 6px;
+    border: 1px solid #999;
+    box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
+    box-sizing: border-box;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+  .new-todo {
+    padding: 10px 16px 16px 60px;
+    border: none;
+    background: rgba(0, 0, 0, 0.003);
+    box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
+  }
+  .main {
+    position: relative;
+    z-index: 2;
+    border-top: 1px solid #e6e6e6;
+  }
+  .toggle-all {
+    text-align: center;
+    border: none;
+    /* Mobile Safari */
+    opacity: 0;
+    position: absolute;
+  }
+  .toggle-all+label {
+    width: 60px;
+    height: 34px;
+    font-size: 0;
+    position: absolute;
+    top: -52px;
+    left: -13px;
+    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
+  }
+  .toggle-all+label:before {
+    content: '❯';
+    font-size: 22px;
+    color: #e6e6e6;
+    padding: 10px 27px 10px 27px;
+  }
+  .toggle-all:checked+label:before {
+    color: #737373;
+  }
+  .todo-list {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+  }
+  .todo-list li {
+    position: relative;
+    font-size: 24px;
+    border-bottom: 1px solid #ededed;
+  }
+  .todo-list li:last-child {
+    border-bottom: none;
+  }
+  .todo-list li.editing {
+    border-bottom: none;
+    padding: 0;
+  }
+  .todo-list li.editing .edit {
+    display: block;
+    width: 506px;
+    padding: 12px 16px;
+    margin: 0 0 0 43px;
+  }
+  .todo-list li.editing .view {
+    display: none;
+  }
+  .todo-list li .toggle {
+    text-align: center;
+    width: 40px;
+    /* auto, since non-WebKit browsers doesn't support input styling */
+    height: auto;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    margin: auto 0;
+    border: none;
+    /* Mobile Safari */
+    -webkit-appearance: none;
+    appearance: none;
+  }
+  .todo-list li .toggle {
+    opacity: 0;
+  }
+  .todo-list li .toggle+label {
+    /*
+    Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
+    IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
+  */
+    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
+    background-repeat: no-repeat;
+    background-position: center left;
+    background-size: 36px;
+  }
+  .todo-list li .toggle:checked+label {
+    background-size: 36px;
+    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
+  }
+  .todo-list li label {
+    word-break: break-all;
+    padding: 15px 15px 15px 50px;
+    display: block;
+    line-height: 1.0;
+        font-size: 14px;
+    transition: color 0.4s;
+  }
+  .todo-list li.completed label {
+    color: #d9d9d9;
+    text-decoration: line-through;
+  }
+  .todo-list li .destroy {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 10px;
+    bottom: 0;
+    width: 40px;
+    height: 40px;
+    margin: auto 0;
+    font-size: 30px;
+    color: #cc9a9a;
+    transition: color 0.2s ease-out;
+    cursor: pointer;
+  }
+  .todo-list li .destroy:hover {
+    color: #af5b5e;
+  }
+  .todo-list li .destroy:after {
+    content: '×';
+  }
+  .todo-list li:hover .destroy {
+    display: block;
+  }
+  .todo-list li .edit {
+    display: none;
+  }
+  .todo-list li.editing:last-child {
+    margin-bottom: -1px;
+  }
+  .footer {
+    color: #777;
+    position: relative;
+    padding: 10px 15px;
+    height: 40px;
+    text-align: center;
+    border-top: 1px solid #e6e6e6;
+  }
+  .footer:before {
+    content: '';
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    height: 40px;
+    overflow: hidden;
+    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
+  }
+  .todo-count {
+    float: left;
+    text-align: left;
+  }
+  .todo-count strong {
+    font-weight: 300;
+  }
+  .filters {
+    margin: 0;
+    padding: 0;
+    position: relative;
+    z-index: 1;
+    list-style: none;
+  }
+  .filters li {
+    display: inline;
+  }
+  .filters li a {
+    color: inherit;
+    font-size: 12px;
+    padding: 3px 7px;
+    text-decoration: none;
+    border: 1px solid transparent;
+    border-radius: 3px;
+  }
+  .filters li a:hover {
+    border-color: rgba(175, 47, 47, 0.1);
+  }
+  .filters li a.selected {
+    border-color: rgba(175, 47, 47, 0.2);
+  }
+  .clear-completed,
+  html .clear-completed:active {
+    float: right;
+    position: relative;
+    line-height: 20px;
+    text-decoration: none;
+    cursor: pointer;
+  }
+  .clear-completed:hover {
+    text-decoration: underline;
+  }
+  .info {
+    margin: 65px auto 0;
+    color: #bfbfbf;
+    font-size: 10px;
+    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+    text-align: center;
+  }
+  .info p {
+    line-height: 1;
+  }
+  .info a {
+    color: inherit;
+    text-decoration: none;
+    font-weight: 400;
+  }
+  .info a:hover {
+    text-decoration: underline;
+  }
+  /*
+  Hack to remove background from Mobile Safari.
+  Can't use it globally since it destroys checkboxes in Firefox
+*/
+  @media screen and (-webkit-min-device-pixel-ratio:0) {
+    .toggle-all,
+    .todo-list li .toggle {
+      background: none;
+    }
+    .todo-list li .toggle {
+      height: 40px;
+    }
+  }
+  @media (max-width: 430px) {
+    .footer {
+      height: 50px;
+    }
+    .filters {
+      bottom: 10px;
+    }
+  }
+}

+ 2 - 7
web/src/view/dashboard/component/todoList/index.vue

@@ -48,13 +48,8 @@
     }
     const defalutList = [
         { text: 'star this repository', done: false },
-        { text: 'fork this repository', done: false },
         { text: 'follow author', done: false },
-        { text: 'vue-element-admin', done: true },
-        { text: 'vue', done: true },
-        { text: 'element-ui', done: true },
-        { text: 'axios', done: true },
-        { text: 'webpack', done: true }
+        { text: 'vue-element-admin', done: true }
     ]
     export default {
         components: { Todo },
@@ -123,5 +118,5 @@
 </script>
 
 <style lang="scss">
-    /*@import './index.scss';*/
+    @import './index.scss';
 </style>

+ 14 - 10
web/src/view/dashboard/index.vue

@@ -22,7 +22,7 @@
      <div class="top">
          <div id="main" class="chart-container"></div>
      </div>
-     <!-- <div class="bottom">
+      <div class="bottom">
          <el-row :gutter="32">
              <el-col :xs="24" :sm="24" :lg="12">
                  <div class="chart-player">
@@ -30,11 +30,12 @@
                  </div>
              </el-col>
              <el-col :xs="24" :sm="24" :lg="12">
-                 <div  style="background:pink;">
+                 <div  class="chart-player">
+                     <todo-list />
                  </div>
              </el-col>
          </el-row>
-     </div> -->
+     </div>
  </div>
 
 </template>
@@ -45,7 +46,9 @@ require('echarts/theme/macarons') // echarts theme
 import RaddarChart from "./component/RaddarChart"
 import stackMap from "./component/stackMap"
 import Sunburst from "./component/Sunburst"
-// import musicPlayer from "./component/musicPlayer"
+import musicPlayer from "./component/musicPlayer"
+import TodoList from "./component/todoList"
+
 export default {
   name: 'Dashboard',
   data() {
@@ -57,7 +60,8 @@ export default {
         RaddarChart, //雷达图
         stackMap, //堆叠图
         Sunburst, //旭日图
-        // musicPlayer  //音乐播放器
+        musicPlayer,  //音乐播放器
+        TodoList //TodoList
     },
   mounted() {
       let myChart = echarts.init(document.getElementById('main'),'macarons');
@@ -190,13 +194,13 @@ export default {
             }
         }
         .bottom{
-            width: 98%;
-            height: 230px;
-            margin-top: 20px;
+            width: 97%;
+            height: 300px;
+            margin: 20px 0;
             .chart-player{
                 width: 100%;
-                height: 100%;
-                padding: 20px;
+                height: 270px;
+                padding: 10px;
                 background-color: #fff;
             }
         }

+ 1 - 1
web/src/view/login/login.vue

@@ -25,7 +25,7 @@
             placeholder="请输入验证码"
             maxlength="10"
           />
-          <img :src="path + picPath" alt="请输入验证码" @click="loginVefify()" class="vPic">
+          <img v-if="picPath" :src="path + picPath" alt="请输入验证码" @click="loginVefify()" class="vPic">
         </el-form-item>
         <el-form-item>
           <el-button @click="submitForm" style="width:100%">登 录</el-button>

+ 0 - 10
web/src/view/systemTools/system/system.vue

@@ -78,11 +78,6 @@
       <el-form-item label="secretKey">
         <el-input v-model="config.qiniu.secretKey"></el-input>
       </el-form-item>
-      <el-form-item>
-        <el-button @click="update" type="primary">立即更新</el-button>
-        <el-button @click="reload" type="primary">重启服务(开发中)</el-button>
-      </el-form-item>
-
       <h2>验证码配置</h2>
       <el-form-item label="keyLong">
         <el-input v-model.number="config.captcha.keyLong"></el-input>
@@ -93,11 +88,6 @@
       <el-form-item label="imgHeight">
         <el-input v-model.number="config.captcha.imgHeight"></el-input>
       </el-form-item>
-      <el-form-item>
-        <el-button @click="update" type="primary">立即更新</el-button>
-        <el-button @click="reload" type="primary">重启服务(开发中)</el-button>
-      </el-form-item>
-
        <h2>日志配置</h2>
       <el-form-item label="prefix">
         <el-input v-model.number="config.log.prefix"></el-input>