Browse Source

chomd(cli): merge avue-cli2

smallwei 7 years ago
commit
668e907880
100 changed files with 24092 additions and 0 deletions
  1. 8 0
      .babelrc
  2. 3 0
      .browserslistrc
  3. 14 0
      .editorconfig
  4. 3 0
      .eslintignore
  5. 17 0
      .eslintrc.js
  6. 24 0
      .gitignore
  7. 5 0
      .postcssrc.js
  8. 48 0
      README.md
  9. 5 0
      babel.config.js
  10. 3 0
      cypress.json
  11. 60 0
      package.json
  12. 11 0
      public/cdn/animate/3.5.2/animate.css
  13. 9 0
      public/cdn/axios/1.0.0/axios.min.js
  14. 1 0
      public/cdn/element-ui/2.4.0/index.js
  15. BIN
      public/cdn/element-ui/2.4.0/theme-chalk/fonts/element-icons.ttf
  16. BIN
      public/cdn/element-ui/2.4.0/theme-chalk/fonts/element-icons.woff
  17. 1 0
      public/cdn/element-ui/2.4.0/theme-chalk/index.css
  18. 23 0
      public/cdn/iconfont/1.0.0/index.css
  19. 191 0
      public/cdn/store/1.3.20/store.js
  20. 2631 0
      public/cdn/vue-router/3.0.1/vue-router.js
  21. 10552 0
      public/cdn/vue/2.5.2/vue.min.js
  22. 6 0
      public/cdn/vuex/2.4.1/vuex.min.js
  23. BIN
      public/favicon.ico
  24. BIN
      public/img/bg/login.png
  25. BIN
      public/img/bg/star-squashed.jpg
  26. BIN
      public/img/code/qq-code.jpg
  27. BIN
      public/img/code/wechat-code.jpg
  28. BIN
      public/img/login.png
  29. BIN
      public/img/mock/card/card-1.jpg
  30. BIN
      public/img/mock/card/card-2.jpg
  31. BIN
      public/img/mock/card/card-3.jpg
  32. BIN
      public/img/mock/card/card-4.jpg
  33. BIN
      public/img/mock/cli/1.png
  34. BIN
      public/img/mock/cli/2.png
  35. BIN
      public/img/mock/cli/3.png
  36. BIN
      public/img/mock/iconfont.png
  37. 37 0
      public/index.html
  38. 24 0
      public/svg/logo.svg
  39. 18 0
      public/util/aes.js
  40. 5949 0
      public/util/crypto-js.js
  41. 7 0
      public/util/mode-ecb.js
  42. 25 0
      public/util/pad-zeropadding.js
  43. 25 0
      src/App.vue
  44. 56 0
      src/api/client.js
  45. 56 0
      src/api/dept.js
  46. 63 0
      src/api/dict.js
  47. 41 0
      src/api/gen.js
  48. 64 0
      src/api/log.js
  49. 57 0
      src/api/login.js
  50. 61 0
      src/api/menu.js
  51. 25 0
      src/api/qiniu.js
  52. 96 0
      src/api/role.js
  53. 63 0
      src/api/route.js
  54. 56 0
      src/api/statustracelog.js
  55. 56 0
      src/api/syssocialdetails.js
  56. 57 0
      src/api/user.js
  57. 27 0
      src/components/basic-container/main.vue
  58. 129 0
      src/components/error-page/403.vue
  59. 91 0
      src/components/error-page/404.vue
  60. 112 0
      src/components/error-page/500.vue
  61. 129 0
      src/components/iframe/main.vue
  62. 21 0
      src/config/env.js
  63. 128 0
      src/const/crud/client.js
  64. 36 0
      src/const/crud/data.js
  65. 76 0
      src/const/crud/dict.js
  66. 79 0
      src/const/crud/gen.js
  67. 64 0
      src/const/crud/log.js
  68. 81 0
      src/const/crud/option.js
  69. 100 0
      src/const/crud/statustracelog.js
  70. 88 0
      src/const/crud/syssocialdetails.js
  71. 367 0
      src/const/dic.js
  72. 12 0
      src/const/errorCode.js
  73. 38 0
      src/const/logs/index.js
  74. 123 0
      src/const/setting/index.js
  75. 26 0
      src/const/website.js
  76. 6 0
      src/docker/Dockerfile
  77. 24 0
      src/error.js
  78. 130 0
      src/filters/index.js
  79. 66 0
      src/main.js
  80. 177 0
      src/mixins/color.js
  81. 110 0
      src/page/index/index.vue
  82. 3 0
      src/page/index/layout.vue
  83. 74 0
      src/page/index/logo.vue
  84. 8 0
      src/page/index/sidebar/config.js
  85. 48 0
      src/page/index/sidebar/index.vue
  86. 103 0
      src/page/index/sidebar/sidebarItem.vue
  87. 121 0
      src/page/index/tags.vue
  88. 170 0
      src/page/index/top/index.vue
  89. 29 0
      src/page/index/top/top-breadcrumb.vue
  90. 30 0
      src/page/index/top/top-color.vue
  91. 72 0
      src/page/index/top/top-lock.vue
  92. 43 0
      src/page/index/top/top-logs.vue
  93. 76 0
      src/page/index/top/top-menu.vue
  94. 111 0
      src/page/index/top/top-search.vue
  95. 158 0
      src/page/index/top/top-setting.vue
  96. 81 0
      src/page/index/top/top-theme.vue
  97. 105 0
      src/page/lock/index.vue
  98. 42 0
      src/page/login/authredirect.vue
  99. 127 0
      src/page/login/codelogin.vue
  100. 0 0
      src/page/login/index.vue

+ 8 - 0
.babelrc

@@ -0,0 +1,8 @@
+{
+  "presets": [
+    ["env", { "modules": false }],
+    "stage-2"
+  ],
+  "plugins": ["transform-runtime"],
+  "comments": false
+}

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not ie <= 8

+ 14 - 0
.editorconfig

@@ -0,0 +1,14 @@
+# http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 3 - 0
.eslintignore

@@ -0,0 +1,3 @@
+build/*.js
+config/*.js
+src/assets

+ 17 - 0
.eslintrc.js

@@ -0,0 +1,17 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  'extends': [
+    'plugin:vue/essential',
+    'eslint:recommended'
+  ],
+  rules: {
+    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
+  },
+  parserOptions: {
+    parser: 'babel-eslint'
+  }
+}

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+.DS_Store
+node_modules
+/dist
+
+/tests/e2e/videos/
+/tests/e2e/screenshots/
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*

+ 5 - 0
.postcssrc.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: {
+    autoprefixer: {}
+  }
+}

+ 48 - 0
README.md

@@ -0,0 +1,48 @@
+# avue-cli
+
+这是基于vue-cli@3.0改的一个脚手架  
+
+## Run vueUI
+* 1.npm install -g @vue/cli  全局安装vue脚手架最新版
+* 2.vue --version 查看版本是否为3.x版本
+* 3.vue-ui 运行管理工具,导入avue-cli项目
+* 4.剩下的自己摸索吧,下面时图示
+
+![图例1](https://gitee.wang/avue/avue-cli/raw/master/public/img/mock/cli/1.png)  
+![图例2](https://gitee.wang/avue/avue-cli/raw/master/public/img/mock/cli/2.png)  
+![图例3](https://gitee.wang/avue/avue-cli/raw/master/public/img/mock/cli/3.png)  
+
+## Run setup
+```
+cd avue-cli
+```
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Lints and fixes files
+```
+npm run lint
+```
+
+### Run your unit tests
+```
+npm run test:unit
+```
+
+### Run your end-to-end tests
+```
+npm run test:e2e
+```

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/app'
+  ]
+}

+ 3 - 0
cypress.json

@@ -0,0 +1,3 @@
+{
+  "pluginsFile": "tests/e2e/plugins/index.js"
+}

+ 60 - 0
package.json

@@ -0,0 +1,60 @@
+{
+    "name": "avue-cli",
+    "version": "2.0.0",
+    "private": true,
+    "scripts": {
+        "serve": "vue-cli-service serve",
+        "build": "vue-cli-service build",
+        "lint": "vue-cli-service lint",
+        "test:unit": "vue-cli-service test:unit",
+        "test:e2e": "vue-cli-service test:e2e"
+    },
+    "dependencies": {
+        "@smallwei/avue": "^1.1.17",
+        "avue-plugin-transfer": "^0.0.2",
+        "avue-plugin-ueditor": "^0.0.1",
+        "axios": "^0.18.0",
+        "babel-polyfill": "^6.26.0",
+        "classlist-polyfill": "^1.2.0",
+        "driver.js": "^0.6.2",
+        "element-ui": "^2.4.5",
+        "file-saver": "^1.3.8",
+        "html2canvas": "^1.0.0-alpha.12",
+        "js-cookie": "^2.2.0",
+        "mockjs": "^1.0.1-beta3",
+        "moment": "^2.22.2",
+        "nprogress": "^0.2.0",
+        "script-loader": "^0.7.2",
+        "vue": "^2.5.16",
+        "vue-axios": "^2.1.2",
+        "vue-clipboard2": "^0.2.1",
+        "vue-json-tree-view": "^2.1.4",
+        "vue-quill-editor": "^3.0.6",
+        "vue-router": "^3.0.1",
+        "vuedraggable": "^2.16.0",
+        "vuex": "^3.0.1",
+        "xlsx": "^0.13.3"
+    },
+    "devDependencies": {
+        "@vue/cli-plugin-babel": "^3.0.0",
+        "@vue/cli-plugin-e2e-cypress": "^3.0.0",
+        "@vue/cli-plugin-eslint": "^3.0.0",
+        "@vue/cli-plugin-unit-mocha": "^3.0.0",
+        "@vue/cli-service": "^3.0.0",
+        "@vue/test-utils": "^1.0.0-beta.20",
+        "chai": "^4.1.2",
+        "node-sass": "^4.9.0",
+        "sass-loader": "^7.0.1",
+        "vue-template-compiler": "^2.5.17"
+    },
+    "lint-staged": {
+        "*.js": [
+            "vue-cli-service lint",
+            "git add"
+        ],
+        "*.vue": [
+            "vue-cli-service lint",
+            "git add"
+        ]
+    }
+}

File diff suppressed because it is too large
+ 11 - 0
public/cdn/animate/3.5.2/animate.css


File diff suppressed because it is too large
+ 9 - 0
public/cdn/axios/1.0.0/axios.min.js


File diff suppressed because it is too large
+ 1 - 0
public/cdn/element-ui/2.4.0/index.js


BIN
public/cdn/element-ui/2.4.0/theme-chalk/fonts/element-icons.ttf


BIN
public/cdn/element-ui/2.4.0/theme-chalk/fonts/element-icons.woff


File diff suppressed because it is too large
+ 1 - 0
public/cdn/element-ui/2.4.0/theme-chalk/index.css


+ 23 - 0
public/cdn/iconfont/1.0.0/index.css

@@ -0,0 +1,23 @@
+
+[class^="icon-"]{
+	font-family: "iconfont" !important;
+	/* 以下内容参照第三方图标库本身的规则 */
+	font-size: 18px !important;
+	font-style: normal;
+	-webkit-font-smoothing: antialiased;
+	-moz-osx-font-smoothing: grayscale;
+}
+.el-menu-item [class^=icon-] {
+    margin-right: 5px;
+    width: 24px;
+    text-align: center;
+    font-size: 18px;
+    vertical-align: middle;
+}
+.el-submenu [class^=icon-] {
+    vertical-align: middle;
+    margin-right: 5px;
+    width: 24px;
+    text-align: center;
+    font-size: 18px;
+}

+ 191 - 0
public/cdn/store/1.3.20/store.js

@@ -0,0 +1,191 @@
+"use strict"
+// Module export pattern from
+// https://github.com/umdjs/umd/blob/master/returnExports.js
+;(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define([], factory);
+    } else if (typeof exports === 'object') {
+        // Node. Does not work with strict CommonJS, but
+        // only CommonJS-like environments that support module.exports,
+        // like Node.
+        module.exports = factory();
+    } else {
+        // Browser globals (root is window)
+        root.store = factory();
+  }
+}(this, function () {
+	
+	// Store.js
+	var store = {},
+		win = (typeof window != 'undefined' ? window : global),
+		doc = win.document,
+		localStorageName = 'localStorage',
+		scriptTag = 'script',
+		storage
+
+	store.disabled = false
+	store.version = '1.3.20'
+	store.set = function(key, value) {}
+	store.get = function(key, defaultVal) {}
+	store.has = function(key) { return store.get(key) !== undefined }
+	store.remove = function(key) {}
+	store.clear = function() {}
+	store.transact = function(key, defaultVal, transactionFn) {
+		if (transactionFn == null) {
+			transactionFn = defaultVal
+			defaultVal = null
+		}
+		if (defaultVal == null) {
+			defaultVal = {}
+		}
+		var val = store.get(key, defaultVal)
+		transactionFn(val)
+		store.set(key, val)
+	}
+	store.getAll = function() {}
+	store.forEach = function() {}
+
+	store.serialize = function(value) {
+		return JSON.stringify(value)
+	}
+	store.deserialize = function(value) {
+		if (typeof value != 'string') { return undefined }
+		try { return JSON.parse(value) }
+		catch(e) { return value || undefined }
+	}
+
+	// Functions to encapsulate questionable FireFox 3.6.13 behavior
+	// when about.config::dom.storage.enabled === false
+	// See https://github.com/marcuswestin/store.js/issues#issue/13
+	function isLocalStorageNameSupported() {
+		try { return (localStorageName in win && win[localStorageName]) }
+		catch(err) { return false }
+	}
+
+	if (isLocalStorageNameSupported()) {
+		storage = win[localStorageName]
+		store.set = function(key, val) {
+			if (val === undefined) { return store.remove(key) }
+			storage.setItem(key, store.serialize(val))
+			return val
+		}
+		store.get = function(key, defaultVal) {
+			var val = store.deserialize(storage.getItem(key))
+			return (val === undefined ? defaultVal : val)
+		}
+		store.remove = function(key) { storage.removeItem(key) }
+		store.clear = function() { storage.clear() }
+		store.getAll = function() {
+			var ret = {}
+			store.forEach(function(key, val) {
+				ret[key] = val
+			})
+			return ret
+		}
+		store.forEach = function(callback) {
+			for (var i=0; i<storage.length; i++) {
+				var key = storage.key(i)
+				callback(key, store.get(key))
+			}
+		}
+	} else if (doc && doc.documentElement.addBehavior) {
+		var storageOwner,
+			storageContainer
+		// Since #userData storage applies only to specific paths, we need to
+		// somehow link our data to a specific path.  We choose /favicon.ico
+		// as a pretty safe option, since all browsers already make a request to
+		// this URL anyway and being a 404 will not hurt us here.  We wrap an
+		// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
+		// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
+		// since the iframe access rules appear to allow direct access and
+		// manipulation of the document element, even for a 404 page.  This
+		// document can be used instead of the current document (which would
+		// have been limited to the current path) to perform #userData storage.
+		try {
+			storageContainer = new ActiveXObject('htmlfile')
+			storageContainer.open()
+			storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>')
+			storageContainer.close()
+			storageOwner = storageContainer.w.frames[0].document
+			storage = storageOwner.createElement('div')
+		} catch(e) {
+			// somehow ActiveXObject instantiation failed (perhaps some special
+			// security settings or otherwse), fall back to per-path storage
+			storage = doc.createElement('div')
+			storageOwner = doc.body
+		}
+		var withIEStorage = function(storeFunction) {
+			return function() {
+				var args = Array.prototype.slice.call(arguments, 0)
+				args.unshift(storage)
+				// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
+				// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
+				storageOwner.appendChild(storage)
+				storage.addBehavior('#default#userData')
+				storage.load(localStorageName)
+				var result = storeFunction.apply(store, args)
+				storageOwner.removeChild(storage)
+				return result
+			}
+		}
+
+		// In IE7, keys cannot start with a digit or contain certain chars.
+		// See https://github.com/marcuswestin/store.js/issues/40
+		// See https://github.com/marcuswestin/store.js/issues/83
+		var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
+		var ieKeyFix = function(key) {
+			return key.replace(/^d/, '___$&').replace(forbiddenCharsRegex, '___')
+		}
+		store.set = withIEStorage(function(storage, key, val) {
+			key = ieKeyFix(key)
+			if (val === undefined) { return store.remove(key) }
+			storage.setAttribute(key, store.serialize(val))
+			storage.save(localStorageName)
+			return val
+		})
+		store.get = withIEStorage(function(storage, key, defaultVal) {
+			key = ieKeyFix(key)
+			var val = store.deserialize(storage.getAttribute(key))
+			return (val === undefined ? defaultVal : val)
+		})
+		store.remove = withIEStorage(function(storage, key) {
+			key = ieKeyFix(key)
+			storage.removeAttribute(key)
+			storage.save(localStorageName)
+		})
+		store.clear = withIEStorage(function(storage) {
+			var attributes = storage.XMLDocument.documentElement.attributes
+			storage.load(localStorageName)
+			for (var i=attributes.length-1; i>=0; i--) {
+				storage.removeAttribute(attributes[i].name)
+			}
+			storage.save(localStorageName)
+		})
+		store.getAll = function(storage) {
+			var ret = {}
+			store.forEach(function(key, val) {
+				ret[key] = val
+			})
+			return ret
+		}
+		store.forEach = withIEStorage(function(storage, callback) {
+			var attributes = storage.XMLDocument.documentElement.attributes
+			for (var i=0, attr; attr=attributes[i]; ++i) {
+				callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
+			}
+		})
+	}
+
+	try {
+		var testKey = '__storejs__'
+		store.set(testKey, testKey)
+		if (store.get(testKey) != testKey) { store.disabled = true }
+		store.remove(testKey)
+	} catch(e) {
+		store.disabled = true
+	}
+	store.enabled = !store.disabled
+	
+	return store
+}));

File diff suppressed because it is too large
+ 2631 - 0
public/cdn/vue-router/3.0.1/vue-router.js


File diff suppressed because it is too large
+ 10552 - 0
public/cdn/vue/2.5.2/vue.min.js


File diff suppressed because it is too large
+ 6 - 0
public/cdn/vuex/2.4.1/vuex.min.js


BIN
public/favicon.ico


BIN
public/img/bg/login.png


BIN
public/img/bg/star-squashed.jpg


BIN
public/img/code/qq-code.jpg


BIN
public/img/code/wechat-code.jpg


BIN
public/img/login.png


BIN
public/img/mock/card/card-1.jpg


BIN
public/img/mock/card/card-2.jpg


BIN
public/img/mock/card/card-3.jpg


BIN
public/img/mock/card/card-4.jpg


BIN
public/img/mock/cli/1.png


BIN
public/img/mock/cli/2.png


BIN
public/img/mock/cli/3.png


BIN
public/img/mock/iconfont.png


+ 37 - 0
public/index.html

@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black">
+    <meta name="format-detection" content="telephone=no">
+    <meta http-equiv="X-UA-Compatible" content="chrome=1" />
+    <link rel="stylesheet" href="<%= BASE_URL %>cdn/element-ui/2.4.0/theme-chalk/index.css">
+    <link rel="stylesheet" href="<%= BASE_URL %>cdn/animate/3.5.2/animate.css">
+    <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/index.css">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title>Avue 通用管理 系统快速开发框架</title>
+</head>
+
+<body>
+    <noscript>
+      <strong>We're sorry but avue2.0 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+    <script src="<%= BASE_URL %>util/aes.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>util/aes.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>util/crypto-js.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>util/mode-ecb.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>util/pad-zeropadding.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>cdn/vue/2.5.2/vue.min.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>cdn/vuex/2.4.1/vuex.min.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>cdn/vue-router/3.0.1/vue-router.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>cdn/axios/1.0.0/axios.min.js" charset="utf-8"></script>
+    <script src="<%= BASE_URL %>cdn/element-ui/2.4.0/index.js" charset="utf-8"></script>
+</body>
+
+</html>

+ 24 - 0
public/svg/logo.svg

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="186px" height="49.5px" viewBox="0 0 186 49.5" style="enable-background:new 0 0 186 49.5;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#333;}
+</style>
+<g>
+
+	<polygon class="st0" points="76.6,1.7 71.7,1.7 71.7,33 87.5,33 87.5,28.1 76.6,28.1 	"/>
+	<path class="st0" d="M114.2,6.3c-3.1-3.1-6.8-4.6-11.1-4.6c-4.3,0-8,1.5-11.1,4.6C89,9.3,87.5,13,87.5,17.3c0,4.3,1.6,8,4.6,11
+		c3.1,3.1,6.8,4.6,11.1,4.6c4.3,0,8-1.5,11.1-4.6c3-3.1,4.6-6.8,4.6-11C118.8,13,117.3,9.3,114.2,6.3z M110.8,25
+		c-2.1,2.1-4.7,3.2-7.7,3.2c-3,0-5.5-1.1-7.6-3.2c-2.1-2.1-3.2-4.7-3.2-7.6c0-3,1.1-5.5,3.2-7.7c2.1-2.1,4.7-3.2,7.6-3.2
+		c3,0,5.5,1.1,7.7,3.2c2.1,2.1,3.2,4.7,3.2,7.7C114,20.3,112.9,22.9,110.8,25z"/>
+	<path class="st0" d="M181.4,28.4c3.1-3.1,4.6-6.8,4.6-11c0-4.3-1.5-8-4.6-11.1c-3.1-3.1-6.8-4.6-11.1-4.6c-4.3,0-8,1.5-11.1,4.6
+		c-3.1,3.1-4.6,6.8-4.6,11.1c0,4.3,1.6,8,4.6,11c3.1,3.1,6.8,4.6,11.1,4.6C174.6,33,178.4,31.4,181.4,28.4z M159.5,17.3
+		c0-3,1.1-5.5,3.2-7.7c2.1-2.1,4.7-3.2,7.6-3.2c3,0,5.5,1.1,7.7,3.2c2.1,2.1,3.2,4.7,3.2,7.7c0,3-1.1,5.5-3.2,7.6
+		c-2.1,2.1-4.7,3.2-7.7,3.2c-3,0-5.5-1.1-7.6-3.2C160.6,22.9,159.5,20.3,159.5,17.3z"/>
+	<path class="st0" d="M147.7,28.4c1.6-1.6,2.8-3.3,3.5-5.3c0.2-0.6,1.1-2.8,1.1-5.8c0-1-0.1-1.8-0.2-2.4c-5.2,0-10.3,0-15.5,0
+		c0,1.6,0,3.2,0,4.8h10.6c-0.4,2-1.4,3.7-2.9,5.2c-2.1,2.1-4.7,3.2-7.7,3.2c-3,0-5.5-1.1-7.6-3.2c-2.1-2.1-3.2-4.7-3.2-7.6
+		c0-3,1.1-5.5,3.2-7.7c2.1-2.1,4.7-3.2,7.6-3.2c2.2,0,4.2,0.6,6,1.8l3.5-3.5c-2.7-2.1-5.9-3.2-9.4-3.2c-4.3,0-8,1.5-11.1,4.6
+		c-3.1,3.1-4.6,6.8-4.6,11.1c0,4.3,1.6,8,4.6,11c3.1,3.1,6.8,4.6,11.1,4.6C140.9,33,144.6,31.4,147.7,28.4z"/>
+</g>
+</svg>

File diff suppressed because it is too large
+ 18 - 0
public/util/aes.js


File diff suppressed because it is too large
+ 5949 - 0
public/util/crypto-js.js


+ 7 - 0
public/util/mode-ecb.js

@@ -0,0 +1,7 @@
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
+*/
+CryptoJS.mode.ECB=function(){var a=CryptoJS.lib.BlockCipherMode.extend();a.Encryptor=a.extend({processBlock:function(a,b){this._cipher.encryptBlock(a,b)}});a.Decryptor=a.extend({processBlock:function(a,b){this._cipher.decryptBlock(a,b)}});return a}();

+ 25 - 0
public/util/pad-zeropadding.js

@@ -0,0 +1,25 @@
+/**
+ * Zero padding strategy.
+ */
+CryptoJS.pad.ZeroPadding = {
+    pad: function (data, blockSize) {
+        // Shortcut
+        var blockSizeBytes = blockSize * 4;
+
+        // Pad
+        data.clamp();
+        data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes);
+    },
+
+    unpad: function (data) {
+        // Shortcut
+        var dataWords = data.words;
+
+        // Unpad
+        var i = data.sigBytes - 1;
+        while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) {
+            i--;
+        }
+        data.sigBytes = i + 1;
+    }
+};

+ 25 - 0
src/App.vue

@@ -0,0 +1,25 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'app',
+  data () {
+    return {}
+  },
+  watch: {},
+  created () { },
+  methods: {},
+  computed: {}
+}
+</script>
+<style lang="scss">
+#app {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

+ 56 - 0
src/api/client.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/client/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/client/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/client/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/client/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/client',
+    method: 'put',
+    data: obj
+  })
+}

+ 56 - 0
src/api/dept.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchTree(query) {
+  return request({
+    url: '/admin/dept/tree',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/dept/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/dept/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/dept/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/dept/',
+    method: 'put',
+    data: obj
+  })
+}

+ 63 - 0
src/api/dict.js

@@ -0,0 +1,63 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/dict/dictPage',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/dict/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/dict/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/admin/dict/' + row.id + '/' + row.type,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/dict/',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function remote(type) {
+  return request({
+    url: '/admin/dict/type/' + type,
+    method: 'get'
+  })
+}

+ 41 - 0
src/api/gen.js

@@ -0,0 +1,41 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/gen/generator/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function handleDown(table) {
+  request({
+    url: '/gen/generator/code',
+    method: 'post',
+    data: table,
+    responseType: 'arraybuffer'
+  }).then((response) => { // 处理返回的文件流
+    let blob = new Blob([response.data], { type:   'application/zip' } )
+    let link = document.createElement('a')
+    link.href = window.URL.createObjectURL(blob)
+    link.download = table.tableName + '.zip'
+    link.click()
+  })
+}

+ 64 - 0
src/api/log.js

@@ -0,0 +1,64 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/log/logPage',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/log/' + id,
+    method: 'delete'
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/log/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/log/' + id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/log/',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function sendLogs(logsList) {
+  return request({
+    url: '/admin/log/logs',
+    method: 'post',
+    data: logsList
+  })
+}

+ 57 - 0
src/api/login.js

@@ -0,0 +1,57 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+import request from '@/router/axios'
+export const loginByUsername = (username, password, code, randomStr) => {
+    var grant_type = 'password'
+    var scope = 'server'
+    return request({
+        url: '/auth/oauth/token',
+        headers: {
+            'TENANT_ID': '1',
+            'Authorization': 'Basic cGlnOnBpZw=='
+        },
+        method: 'post',
+        params: { username, password, randomStr, code, grant_type, scope }
+    })
+}
+
+export const loginBySocial = (state, code) => {
+    var grant_type = 'mobile'
+    return request({
+        url: '/auth/mobile/token',
+        headers: {
+            'TENANT_ID': '1',
+            'Authorization': 'Basic cGlnOnBpZw=='
+        },
+        method: 'post',
+        params: { mobile: state + '@' + code, grant_type }
+    })
+}
+
+export const getUserInfo = () => {
+    return request({
+        url: '/admin/user/info',
+        method: 'get'
+    })
+}
+
+export const logout = () => {
+    return request({
+        url: '/auth/oauth/removeToken',
+        method: 'get'
+    })
+}

+ 61 - 0
src/api/menu.js

@@ -0,0 +1,61 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+export function GetMenu() {
+  return request({
+    url: '/admin/menu/userMenu',
+    method: 'get'
+  })
+}
+export function fetchTree(query) {
+  return request({
+    url: '/admin/menu/allTree',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/menu/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/menu/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/menu/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/menu/',
+    method: 'put',
+    data: obj
+  })
+}

+ 25 - 0
src/api/qiniu.js

@@ -0,0 +1,25 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function getToken() {
+  return request({
+    url: '/zuul/admin/user/upload', // 假地址,自行替换
+    method: 'post'
+  })
+}

+ 96 - 0
src/api/role.js

@@ -0,0 +1,96 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function roleList() {
+  return request({
+    url: '/admin/role/roleList',
+    method: 'get'
+  })
+}
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/role/rolePage',
+    method: 'get',
+    params: query
+  })
+}
+
+export function deptRoleList(deptId) {
+  return request({
+    url: '/admin/role/roleList/' + deptId,
+    method: 'get'
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/role/' + id,
+    method: 'get'
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/role',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/role',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/role/' + id,
+    method: 'delete'
+  })
+}
+
+export function permissionUpd(roleId, menuIds) {
+  return request({
+    url: '/admin/role/roleMenuUpd',
+    method: 'put',
+    params: {
+      roleId: roleId,
+      menuIds: menuIds
+    }
+  })
+}
+
+export function fetchRoleTree(roleName) {
+  return request({
+    url: '/admin/menu/roleTree/' + roleName,
+    method: 'get'
+  })
+}
+
+export function fetchDeptTree(query) {
+  return request({
+    url: '/admin/dept/tree',
+    method: 'get',
+    params: query
+  })
+}

+ 63 - 0
src/api/route.js

@@ -0,0 +1,63 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/route/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/route/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/route/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/route/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/route',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function applyObj() {
+  return request({
+    url: '/admin/route/apply',
+    method: 'get'
+  })
+}

+ 56 - 0
src/api/statustracelog.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/daemon/statustracelog/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/daemon/statustracelog/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/daemon/statustracelog/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/daemon/statustracelog/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/daemon/statustracelog/',
+    method: 'put',
+    data: obj
+  })
+}

+ 56 - 0
src/api/syssocialdetails.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/social/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/social/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/social/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/social/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/social/',
+    method: 'put',
+    data: obj
+  })
+}

+ 57 - 0
src/api/user.js

@@ -0,0 +1,57 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/user/userPage',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/user/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/user/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/user/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/user',
+    method: 'put',
+    data: obj
+  })
+}
+

+ 27 - 0
src/components/basic-container/main.vue

@@ -0,0 +1,27 @@
+<template>
+  <div class="basic-container">
+    <el-card>
+      <slot></slot>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "basicContainer"
+};
+</script>
+
+<style lang="scss">
+.basic-container {
+  padding: 8px 10px;
+  border-radius: 10px;
+  box-sizing: border-box;
+  .el-card {
+    width: 100%;
+  }
+  &:first-child {
+    padding-top: 0;
+  }
+}
+</style>

+ 129 - 0
src/components/error-page/403.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="error403">
+    <div class="error403-body-con">
+      <el-card class="box-card">
+        <div class="error403-body-con-title">4
+          <span class="error403-0-span">
+            <i class="icon-quanxian"></i>
+          </span>
+          <span class="error403-key-span">
+            <i class="icon-iconset0216"></i>
+          </span>
+        </div>
+        <p class="error403-body-con-message">You don't have permission</p>
+        <div class="error403-btn-con">
+          <el-button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</el-button>
+          <el-button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Error403",
+  methods: {
+    backPage() {
+      this.$router.go(-1);
+    },
+    goHome() {
+      this.$router.push({
+        path: "/"
+      });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@keyframes error403animation {
+  0% {
+    transform: rotateZ(0deg);
+  }
+  40% {
+    transform: rotateZ(-20deg);
+  }
+  45% {
+    transform: rotateZ(-15deg);
+  }
+  50% {
+    transform: rotateZ(-20deg);
+  }
+  55% {
+    transform: rotateZ(-15deg);
+  }
+  60% {
+    transform: rotateZ(-20deg);
+  }
+  100% {
+    transform: rotateZ(0deg);
+  }
+}
+.error403 {
+  &-body-con {
+    width: 700px;
+    height: 500px;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    &-title {
+      text-align: center;
+      font-size: 240px;
+      font-weight: 700;
+      color: #2d8cf0;
+      height: 260px;
+      line-height: 260px;
+      margin-top: 40px;
+      .error403-0-span {
+        display: inline-block;
+        position: relative;
+        width: 170px;
+        height: 170px;
+        border-radius: 50%;
+        border: 20px solid #ed3f14;
+        color: #ed3f14;
+        margin-right: 10px;
+        i {
+          display: inline-block;
+          font-size: 120px !important;
+          position: absolute;
+          left: 50%;
+          top: 50%;
+          transform: translate(-50%, -50%);
+        }
+      }
+      .error403-key-span {
+        display: inline-block;
+        position: relative;
+        width: 100px;
+        height: 190px;
+        border-radius: 50%;
+        margin-right: 10px;
+        i {
+          display: inline-block;
+          font-size: 190px !important;
+          position: absolute;
+          left: 20px;
+          transform: translate(-50%, -60%);
+          transform-origin: center bottom;
+          animation: error403animation 2.8s ease 0s infinite;
+        }
+      }
+    }
+    &-message {
+      display: block;
+      text-align: center;
+      font-size: 30px;
+      font-weight: 500;
+      letter-spacing: 4px;
+      color: #dddde2;
+    }
+  }
+  &-btn-con {
+    text-align: center;
+    padding: 20px 0;
+    margin-bottom: 40px;
+  }
+}
+</style>

+ 91 - 0
src/components/error-page/404.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="error404">
+    <div class="error404-body-con">
+      <el-card class="box-card">
+        <div class="error404-body-con-title">4
+          <span>0</span>4</div>
+        <p class="error404-body-con-message">YOU&nbsp;&nbsp;LOOK&nbsp;&nbsp;LOST</p>
+        <div class="error404-btn-con">
+          <el-button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</el-button>
+          <el-button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Error404",
+  methods: {
+    backPage() {
+      this.$router.go(-1);
+    },
+    goHome() {
+      this.$router.push({
+        path: "/"
+      });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@keyframes error404animation {
+  0% {
+    transform: rotateZ(0deg);
+  }
+  20% {
+    transform: rotateZ(-60deg);
+  }
+  40% {
+    transform: rotateZ(-10deg);
+  }
+  60% {
+    transform: rotateZ(50deg);
+  }
+  80% {
+    transform: rotateZ(-20deg);
+  }
+  100% {
+    transform: rotateZ(0deg);
+  }
+}
+.error404 {
+  &-body-con {
+    width: 700px;
+    height: 500px;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    &-title {
+      text-align: center;
+      font-size: 240px;
+      font-weight: 700;
+      color: #2d8cf0;
+      height: 260px;
+      line-height: 260px;
+      margin-top: 40px;
+      span {
+        display: inline-block;
+        color: #19be6b;
+        font-size: 230px;
+        animation: error404animation 3s ease 0s infinite alternate;
+      }
+    }
+    &-message {
+      display: block;
+      text-align: center;
+      font-size: 30px;
+      font-weight: 500;
+      letter-spacing: 12px;
+      color: #dddde2;
+    }
+  }
+  &-btn-con {
+    text-align: center;
+    padding: 20px 0;
+    margin-bottom: 40px;
+  }
+}
+</style>

+ 112 - 0
src/components/error-page/500.vue

@@ -0,0 +1,112 @@
+<template>
+  <div class="error500">
+    <div class="error500-body-con">
+      <el-card class="box-card">
+        <div class="error500-body-con-title">
+          5
+          <span class="error500-0-span">
+            <i class="icon-debug"></i>
+          </span>
+          <span class="error500-0-span">
+            <i class="icon-debug"></i>
+          </span>
+        </div>
+        <p class="error500-body-con-message">Oops! the server is wrong</p>
+        <div class="error500-btn-con">
+          <el-button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</el-button>
+          <el-button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Error500",
+  methods: {
+    backPage() {
+      this.$router.go(-1);
+    },
+    goHome() {
+      this.$router.push({
+        path: "/"
+      });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@keyframes error500animation {
+  0% {
+    transform: rotateZ(0deg);
+  }
+  20% {
+    transform: rotateZ(-10deg);
+  }
+  40% {
+    transform: rotateZ(5deg);
+  }
+  60% {
+    transform: rotateZ(-5deg);
+  }
+  80% {
+    transform: rotateZ(10deg);
+  }
+  100% {
+    transform: rotateZ(0deg);
+  }
+}
+.error500 {
+  &-body-con {
+    width: 700px;
+    height: 500px;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    &-title {
+      text-align: center;
+      font-size: 240px;
+      font-weight: 700;
+      color: #2d8cf0;
+      height: 260px;
+      line-height: 260px;
+      margin-top: 40px;
+      .error500-0-span {
+        display: inline-block;
+        position: relative;
+        width: 170px;
+        height: 170px;
+        border-radius: 50%;
+        border: 20px solid #ed3f14;
+        color: #ed3f14;
+        margin-right: 10px;
+        i {
+          display: inline-block;
+          font-size: 120px !important;
+          position: absolute;
+          bottom: -43px;
+          left: 20px;
+          transform-origin: center bottom;
+          animation: error500animation 3s ease 0s infinite alternate;
+        }
+      }
+    }
+    &-message {
+      display: block;
+      text-align: center;
+      font-size: 30px;
+      font-weight: 500;
+      letter-spacing: 4px;
+      color: #dddde2;
+    }
+  }
+  &-btn-con {
+    text-align: center;
+    padding: 20px 0;
+    margin-bottom: 40px;
+  }
+}
+</style>
+

+ 129 - 0
src/components/iframe/main.vue

@@ -0,0 +1,129 @@
+<template>
+  <div>
+    <basic-container>
+      <iframe v-if="$route.query.src"
+              :src='$route.query.src'
+              class="iframe"
+              ref="iframe"></iframe>
+      <iframe v-else
+              :src="urlPath"
+              class="iframe"
+              ref="iframe"></iframe>
+    </basic-container>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import NProgress from 'nprogress' // progress bar
+import 'nprogress/nprogress.css' // progress bar style
+export default {
+  name: 'AvueIframe',
+  data () {
+    return {
+      urlPath: this.getUrlPath() //iframe src 路径
+    }
+  },
+  created () {
+    NProgress.configure({ showSpinner: false })
+  },
+  mounted () {
+    this.load()
+    this.resize()
+  },
+  props: ['routerPath'],
+  watch: {
+    $route: function () {
+      this.load()
+    },
+    routerPath: function () {
+      // 监听routerPath变化,改变src路径
+      this.urlPath = this.getUrlPath()
+    }
+  },
+  components: {
+    ...mapGetters(['screen']),
+  },
+  methods: {
+    // 显示等待框
+    show () {
+      NProgress.start()
+    },
+    // 隐藏等待狂
+    hide () {
+      NProgress.done()
+    },
+    // 加载浏览器窗口变化自适应
+    resize () {
+      window.onresize = () => {
+        this.iframeInit()
+      }
+    },
+    // 加载组件
+    load () {
+      this.show()
+      var flag = true //URL是否包含问号
+      if (this.$route.query.src.indexOf('?') == -1) {
+        flag = false
+      }
+      var list = []
+      for (var key in this.$route.query) {
+        if (key != 'src' && key != 'name') {
+          list.push(`${key}= this.$route.query[key]`)
+        }
+      }
+      list = list.join('&').toString()
+      if (flag) {
+        this.$route.query.src = `${this.$route.query.src}${
+          list.length > 0 ? `&list` : ''
+          }`
+      } else {
+        this.$route.query.src = `${this.$route.query.src}${
+          list.length > 0 ? `?list` : ''
+          }`
+      }
+      //超时3s自动隐藏等待狂,加强用户体验
+      let time = 3
+      const timeFunc = setInterval(() => {
+        time--
+        if (time == 0) {
+          this.hide()
+          clearInterval(timeFunc)
+        }
+      }, 1000)
+      this.iframeInit()
+    },
+    //iframe窗口初始化
+    iframeInit () {
+      const iframe = this.$refs.iframe
+      const clientHeight = document.documentElement.clientHeight - (screen > 1 ? 200 : 130);
+      iframe.style.height = `${clientHeight}px`
+      if (iframe.attachEvent) {
+        iframe.attachEvent('onload', () => {
+          this.hide()
+        })
+      } else {
+        iframe.onload = () => {
+          this.hide()
+        }
+      }
+    },
+    getUrlPath: function () {
+      //获取 iframe src 路径
+      let url = window.location.href
+      url = url.replace('/myiframe', '')
+      return url
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.iframe {
+  width: 100%;
+  height: 100%;
+  border: 0;
+  overflow: hidden;
+  box-sizing: border-box;
+}
+</style>

+ 21 - 0
src/config/env.js

@@ -0,0 +1,21 @@
+// 配置编译环境和线上环境之间的切换
+
+let baseUrl = '';
+let iconfontVersion = ['567566_r22zi6t8noas8aor', '567566_qo5lxgtishg', '599693_dfa50fge714', '667895_2ek3wqcg8w1', '667895_w0ofbdzuuir']
+let iconfontUrl = `//at.alicdn.com/t/font_$key.css`;
+let codeUrl = `${baseUrl}/code`
+const env = process.env
+if (env.NODE_ENV == 'development') {
+    baseUrl = ``; // 开发环境地址
+} else if (env.NODE_ENV == 'production') {
+    baseUrl = ``; //生产环境地址
+} else if (env.NODE_ENV == 'test') {
+    baseUrl = ``; //测试环境地址
+}
+export {
+    baseUrl,
+    iconfontUrl,
+    iconfontVersion,
+    codeUrl,
+    env
+}

+ 128 - 0
src/const/crud/client.js

@@ -0,0 +1,128 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+const DIC = {
+  vaild: [{
+    label: '否',
+    value: 'false'
+  }, {
+    label: '是',
+    value: 'true'
+  }]
+}
+export const tableOption = {
+  'border': true,
+  'index': true,
+  'indexLabel': '序号',
+  'stripe': true,
+  'menuAlign': 'center',
+  'align': 'center',
+  'editBtn': false,
+  'delBtn': false,
+  'dic': [],
+  'column': [{
+    width: 150,
+    label: '编号',
+    prop: 'clientId',
+    align: 'center',
+    sortable: true,
+    rules: [{
+      required: true,
+      message: '请输入clientId',
+      trigger: 'blur'
+    }]
+  }, {
+    width: 300,
+    label: '密钥',
+    prop: 'clientSecret',
+    align: 'center',
+    sortable: true,
+    rules: [{
+      required: true,
+      message: '请输入clientSecret',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '域',
+    prop: 'scope',
+    align: 'center',
+    width: 150,
+    rules: [{
+      required: true,
+      message: '请输入scope',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '授权模式',
+    prop: 'authorizedGrantTypes',
+    align: 'center',
+    width: 150,
+    hide: true,
+    rules: [{
+      required: true,
+      message: '请输入授权模式',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '回调地址',
+    prop: 'webServerRedirectUri',
+    align: 'center',
+    width: 150,
+    hide: true
+  }, {
+    label: '权限',
+    prop: 'authorities',
+    align: 'center',
+    width: 150,
+    hide: true
+  }, {
+    label: '请求令牌',
+    prop: 'accessTokenValidity',
+    align: 'center',
+    width: 150,
+    hide: true
+  }, {
+    label: '刷新令牌',
+    prop: 'refreshTokenValidity',
+    align: 'center',
+    width: 150,
+    hide: true
+  }, {
+    label: '扩展信息',
+    prop: 'additionalInformation',
+    align: 'center',
+    width: 150,
+    hide: true
+  }, {
+    label: '自动放行',
+    prop: 'autoapprove',
+    align: 'center',
+    type: 'radio',
+    dicData: DIC.vaild,
+    width: 150,
+    rules: [{
+      required: true,
+      message: '请选择是否放行',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '资源ID',
+    prop: 'resourceIds',
+    align: 'center',
+    width: 150
+  }]
+}

+ 36 - 0
src/const/crud/data.js

@@ -0,0 +1,36 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableData = [{
+  username: 'lengleng',
+  name: 'lengleng',
+  number: 12,
+  type: '0',
+  stars: 'https://gitee.com/log4j/pig/badge/star.svg?theme=white',
+  git: 'https://gitee.com/log4j/pig',
+  address: 'https://gitee.com/log4j',
+  info: 'Pig是基于Spring Cloud、OAuth2.0,使用Vue前后分离的开发平台,支持账号、 短信、 SSO等多种登录。 '
+}, {
+  username: 'smallwei',
+  name: 'smallwei',
+  number: 20,
+  type: '1',
+  stars: 'https://gitee.com/smallweigit/avue/badge/star.svg?theme=white',
+  git: 'https://gitee.com/smallweigit/avue',
+  address: 'https://gitee.com/smallweigit',
+  info: 'Avue是一个后台集成解决方案,它基于 Vue.js 和 element。 使用了最新的前端技术栈,支持权限验证,第三方网站嵌套等功能。'
+}]

+ 76 - 0
src/const/crud/dict.js

@@ -0,0 +1,76 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+export const tableOption = {
+  'border': true,
+  'index': true,
+  'indexLabel': '序号',
+  'stripe': true,
+  'menuAlign': 'center',
+  'align': 'center',
+  'refreshBtn': false,
+  'showClomnuBtn': false,
+  'searchSize': 'mini',
+  'editBtn': false,
+  'delBtn': false,
+  'dic': [],
+  'column': [{
+    width: 150,
+    label: '数据值',
+    prop: 'value',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '标签名',
+    prop: 'label',
+    rules: [{
+      required: true,
+      message: '请输入标签名',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '类型',
+    prop: 'type',
+    'search': true,
+    rules: [{
+      required: true,
+      message: '请输入字典类型',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '描述',
+    prop: 'description',
+    rules: [{
+      required: true,
+      message: '请输入字典描述',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '排序',
+    prop: 'sort',
+    rules: [{
+      required: true,
+      message: '请输入排序',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '备注信息',
+    prop: 'remarks'
+  }]
+}

+ 79 - 0
src/const/crud/gen.js

@@ -0,0 +1,79 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+  'border': true,
+  'index': true,
+  'stripe': true,
+  'menuAlign': 'center',
+  'align': 'center',
+  'addBtn': false,
+  'editBtn': false,
+  'delBtn': false,
+  'column': [{
+    label: '表名称',
+    prop: 'tableName',
+    align: 'center',
+    search: true
+  }, {
+    label: '表注释',
+    prop: 'tableComment',
+    align: 'center'
+  }, {
+    label: '索引',
+    prop: 'engine',
+    align: 'center'
+  }, {
+    type: 'datetime',
+    valueFormat: 'timestamp',
+    format: 'yyyy-MM-dd hh:mm:ss',
+    label: '创建时间',
+    prop: 'createTime',
+    align: 'center'
+  }]
+}
+
+export const formOption = {
+  submitText: '生成',
+  column: [
+    {
+      label: '表名称',
+      prop: 'tableName',
+      disabled: true
+    }, {
+      label: '包名',
+      prop: 'packageName',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '作者',
+      prop: 'author',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '模块',
+      prop: 'moduleName',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '表前缀',
+      prop: 'tablePrefix',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '注释',
+      prop: 'comments',
+      placeholder: '可为空,加载表备注'
+    }
+  ]
+}

+ 64 - 0
src/const/crud/log.js

@@ -0,0 +1,64 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+export const tableOption = {
+  'border': true,
+  'index': true,
+  'indexLabel': '序号',
+  'stripe': true,
+  'menuAlign': 'center',
+  'menuWidth': 150,
+  'align': 'center',
+  'refreshBtn': true,
+  'showClomnuBtn': false,
+  'searchSize': 'mini',
+  'addBtn': false,
+  'editBtn': false,
+  'delBtn': false,
+  'dicUrl': '/admin/dict/type/log_type',
+  props: {
+    label: 'label',
+    value: 'value'
+  },
+  'column': [{
+    label: '类型',
+    prop: 'type',
+    type: 'select',
+    dicData: 'log_type',
+    search: true
+  }, {
+    label: '请求接口',
+    prop: 'requestUri'
+  }, {
+    label: 'IP地址',
+    prop: 'remoteAddr'
+  }, {
+    label: '请求方式',
+    prop: 'method'
+  }, {
+    label: '客户端',
+    prop: 'serviceId'
+  }, {
+    label: '请求时间',
+    prop: 'time'
+  }, {
+    label: '创建时间',
+    prop: 'createTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss'
+  }]
+}

+ 81 - 0
src/const/crud/option.js

@@ -0,0 +1,81 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+    'border': true,
+    'index': true,
+    'expand': true,
+    'stripe': true,
+    'selection': true,
+    'page': false,
+    'menuAlign': 'center',
+    'defaultSort': {
+        prop: 'username',
+        order: 'descending'
+    },
+    'align': 'center',
+    'dic': [],
+    'column': [{
+        'label': '用户名',
+        'prop': 'username',
+        'span': 24,
+        'solt': true,
+        'sortable': true,
+        'width': 120,
+        'rules': [{
+            'required': true,
+            'message': '请输入用户名',
+            'trigger': 'blur'
+        }]
+    }, {
+        'label': '类型',
+        'prop': 'type',
+        'width': 80,
+        'type': 'select',
+        'sortable': true,
+        'dicData': [{
+            'label': '后端',
+            'value': '0'
+        }, {
+            'label': '前端',
+            'value': '1'
+        }]
+    }, {
+        'label': 'stars',
+        'width': '150',
+        'prop': 'stars',
+        'sortable': true,
+        'solt': true
+    }, {
+        'label': '码云',
+        'solt': true,
+        'span': 24,
+        'prop': 'address',
+        'type': 'textarea',
+        'overHidden': true
+    }, {
+        'label': '项目介绍',
+        'width': '300',
+        'prop': 'info',
+        'editDisabled': true,
+        'type': 'textarea',
+        'span': 24,
+        'maxRow': 4,
+        'minRow': 4,
+        'overHidden': true
+    }]
+}

+ 100 - 0
src/const/crud/statustracelog.js

@@ -0,0 +1,100 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+const DIC = {
+    state: [{
+            label: '准备中',
+            value: 'TASK_STAGING'
+        },
+        {
+            label: '执行中',
+            value: 'TASK_RUNNING'
+        }, {
+            label: '已经完成',
+            value: 'TASK_FINISHED'
+        }
+    ]
+}
+
+export const tableOption = {
+    "border": true,
+    "index": true,
+    "stripe": true,
+    "menuAlign": "center",
+    "align": "center",
+    "editBtn": false,
+    "delBtn": false,
+    "addBtn": false,
+    "dic": [],
+    "column": [{
+            label: 'ID',
+            prop: 'id',
+            hide: true
+        },
+        {
+            label: '作业名称',
+            prop: 'jobName',
+            search: true
+        },
+        {
+            label: '原任务',
+            prop: 'originalTaskId',
+            hide: true,
+            width: 100
+        },
+        {
+            label: '任务ID',
+            prop: 'taskId',
+            hide: true
+        },
+        {
+            label: '服务器IP',
+            prop: 'slaveId'
+        },
+        {
+            label: '资源',
+            prop: 'source',
+            hide: true
+        },
+        {
+            label: '执行类型',
+            prop: 'executionType',
+            hide: true,
+            width: 100
+        },
+        {
+            label: '分片项',
+            prop: 'shardingItem'
+        },
+        {
+            label: '状态',
+            prop: 'state',
+            type: 'select',
+            dicData: DIC.state,
+            search: true
+        },
+        {
+            label: '创建时间',
+            prop: 'creationTime'
+        },
+        {
+            label: '消息',
+            prop: 'message',
+            width: 100
+        }
+    ]
+}

+ 88 - 0
src/const/crud/syssocialdetails.js

@@ -0,0 +1,88 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+    "border": true,
+    "index": true,
+    "stripe": true,
+    "menuAlign": "center",
+    "align": "center",
+    "editBtn": false,
+    "delBtn": false,
+    "addBtn": false,
+    'dicUrl': '/admin/dict/type/social_type',
+    "column": [{
+            label: 'ID',
+            prop: 'id',
+            hide: true,
+            addVisdiplay: false,
+            editDisabled: true
+        },
+        {
+            label: '类型',
+            prop: 'type',
+            type: 'select',
+            dicData: 'social_type',
+            search: true,
+            rules: [{
+                required: true,
+                message: '请选择类型',
+                trigger: 'blur'
+            }]
+        },
+        {
+            label: '描述',
+            prop: 'remark'
+        },
+        {
+            label: 'appId',
+            prop: 'appId',
+            rules: [{
+                required: true,
+                message: '请输入appId',
+                trigger: 'blur'
+            }]
+        },
+        {
+            label: 'appSecret',
+            prop: 'appSecret',
+            rules: [{
+                required: true,
+                message: '请输入appSecret',
+                trigger: 'blur'
+            }]
+        },
+        {
+            label: '回调地址',
+            prop: 'redirectUrl',
+            rules: [{
+                required: true,
+                message: '请输入回调地址',
+                trigger: 'blur'
+            }]
+        },
+        {
+            valueFormat: 'timestamp',
+            format: 'yyyy-MM-dd hh:mm:ss',
+            label: '创建时间',
+            prop: 'createTime',
+            align: 'center',
+            addVisdiplay: false,
+            editDisabled: true
+        }
+    ]
+}

+ 367 - 0
src/const/dic.js

@@ -0,0 +1,367 @@
+export const DIC = {
+    VAILD: [{
+        label: '真',
+        value: 'true',
+        color: 'green'
+    }, {
+        label: '假',
+        value: 'false',
+        color: 'red'
+    }],
+    SEX: [{
+        label: '男',
+        value: 0,
+        color: 'green'
+    }, {
+        label: '女',
+        value: 1,
+        color: 'red'
+    }],
+    POSTIONDATA: [{
+        label: '左对齐',
+        value: 'left'
+    }, {
+        label: '居中',
+        value: 'center'
+    }, {
+        label: '右对齐',
+        value: 'right'
+    }],
+    TYPE: [{
+            label: '一级1',
+            value: 0,
+            children: [{
+                label: '一级1二级1',
+                value: 2,
+            }]
+        }, {
+            label: '一级2',
+            value: 1,
+            children: [{
+                label: '一级2二级1',
+                value: 2,
+            }]
+        }
+
+    ],
+    STATE: [{
+        label: '有效',
+        value: 0
+    }, {
+        label: '无效',
+        value: 1
+    }],
+    GRADE: [{
+            label: "管理员",
+            value: 0
+        },
+        {
+            label: "二级管理员",
+            value: 1
+        }
+    ],
+    VAILDATA: [{
+            label: "激活",
+            value: true,
+        },
+        {
+            label: "禁用",
+            value: false,
+        }
+    ],
+    CRUDTYPE: [{
+            label: "输入框",
+            value: ''
+        }, {
+            label: "选择框",
+            value: 'select'
+        },
+        {
+            label: "文本框",
+            value: 'text'
+        },
+        {
+            label: "密码框",
+            value: 'password'
+        },
+        {
+            label: "树型框",
+            value: 'tree'
+        },
+        {
+            label: "富文本编辑框",
+            value: 'ueditor'
+        },
+        {
+            label: "多行文本框",
+            value: 'textarea'
+        },
+        {
+            label: "单选框",
+            value: 'radio'
+        },
+        {
+            label: "多选框",
+            value: 'checkbox'
+        },
+        {
+            label: "数字框",
+            value: 'number'
+        },
+        {
+            label: "日期框",
+            value: 'date'
+        },
+        {
+            label: "日期范围框",
+            value: 'daterange'
+        },
+        {
+            label: "时间范围框",
+            value: 'datetimerange'
+        },
+        {
+            label: "时间框",
+            value: 'datetime'
+        },
+        {
+            label: "周",
+            value: 'week'
+        },
+        {
+            label: "月",
+            value: 'month'
+        },
+        {
+            label: "年",
+            value: 'year'
+        },
+        {
+            label: "滑动框",
+            value: 'silder'
+        },
+        {
+            label: "评价框",
+            value: 'rate'
+        },
+        {
+            label: "上传框",
+            value: 'upload'
+        }
+    ],
+    UITYPE: [{
+        value: 'zhinan',
+        label: '指南',
+        children: [{
+            value: 'shejiyuanze',
+            label: '设计原则',
+            children: [{
+                value: 'yizhi',
+                label: '一致'
+            }, {
+                value: 'fankui',
+                label: '反馈'
+            }, {
+                value: 'xiaolv',
+                label: '效率'
+            }, {
+                value: 'kekong',
+                label: '可控'
+            }]
+        }, {
+            value: 'daohang',
+            label: '导航',
+            children: [{
+                value: 'cexiangdaohang',
+                label: '侧向导航'
+            }, {
+                value: 'dingbudaohang',
+                label: '顶部导航'
+            }]
+        }]
+    }, {
+        value: 'zujian',
+        label: '组件',
+        children: [{
+            value: 'basic',
+            label: 'Basic',
+            children: [{
+                value: 'layout',
+                label: 'Layout 布局'
+            }, {
+                value: 'color',
+                label: 'Color 色彩'
+            }, {
+                value: 'typography',
+                label: 'Typography 字体'
+            }, {
+                value: 'icon',
+                label: 'Icon 图标'
+            }, {
+                value: 'button',
+                label: 'Button 按钮'
+            }]
+        }, {
+            value: 'form',
+            label: 'Form',
+            children: [{
+                value: 'radio',
+                label: 'Radio 单选框'
+            }, {
+                value: 'checkbox',
+                label: 'Checkbox 多选框'
+            }, {
+                value: 'input',
+                label: 'Input 输入框'
+            }, {
+                value: 'input-number',
+                label: 'InputNumber 计数器'
+            }, {
+                value: 'select',
+                label: 'Select 选择器'
+            }, {
+                value: 'cascader',
+                label: 'Cascader 级联选择器'
+            }, {
+                value: 'switch',
+                label: 'Switch 开关'
+            }, {
+                value: 'slider',
+                label: 'Slider 滑块'
+            }, {
+                value: 'time-picker',
+                label: 'TimePicker 时间选择器'
+            }, {
+                value: 'date-picker',
+                label: 'DatePicker 日期选择器'
+            }, {
+                value: 'datetime-picker',
+                label: 'DateTimePicker 日期时间选择器'
+            }, {
+                value: 'upload',
+                label: 'Upload 上传'
+            }, {
+                value: 'rate',
+                label: 'Rate 评分'
+            }, {
+                value: 'form',
+                label: 'Form 表单'
+            }]
+        }, {
+            value: 'data',
+            label: 'Data',
+            children: [{
+                value: 'table',
+                label: 'Table 表格'
+            }, {
+                value: 'tag',
+                label: 'Tag 标签'
+            }, {
+                value: 'progress',
+                label: 'Progress 进度条'
+            }, {
+                value: 'tree',
+                label: 'Tree 树形控件'
+            }, {
+                value: 'pagination',
+                label: 'Pagination 分页'
+            }, {
+                value: 'badge',
+                label: 'Badge 标记'
+            }]
+        }, {
+            value: 'notice',
+            label: 'Notice',
+            children: [{
+                value: 'alert',
+                label: 'Alert 警告'
+            }, {
+                value: 'loading',
+                label: 'Loading 加载'
+            }, {
+                value: 'message',
+                label: 'Message 消息提示'
+            }, {
+                value: 'message-box',
+                label: 'MessageBox 弹框'
+            }, {
+                value: 'notification',
+                label: 'Notification 通知'
+            }]
+        }, {
+            value: 'navigation',
+            label: 'Navigation',
+            children: [{
+                value: 'menu',
+                label: 'NavMenu 导航菜单'
+            }, {
+                value: 'tabs',
+                label: 'Tabs 标签页'
+            }, {
+                value: 'breadcrumb',
+                label: 'Breadcrumb 面包屑'
+            }, {
+                value: 'dropdown',
+                label: 'Dropdown 下拉菜单'
+            }, {
+                value: 'steps',
+                label: 'Steps 步骤条'
+            }]
+        }, {
+            value: 'others',
+            label: 'Others',
+            children: [{
+                value: 'dialog',
+                label: 'Dialog 对话框'
+            }, {
+                value: 'tooltip',
+                label: 'Tooltip 文字提示'
+            }, {
+                value: 'popover',
+                label: 'Popover 弹出框'
+            }, {
+                value: 'card',
+                label: 'Card 卡片'
+            }, {
+                value: 'carousel',
+                label: 'Carousel 走马灯'
+            }, {
+                value: 'collapse',
+                label: 'Collapse 折叠面板'
+            }]
+        }]
+    }, {
+        value: 'ziyuan',
+        label: '资源',
+        children: [{
+            value: 'axure',
+            label: 'Axure Components'
+        }, {
+            value: 'sketch',
+            label: 'Sketch Templates'
+        }, {
+            value: 'jiaohu',
+            label: '组件交互文档'
+        }]
+    }],
+    PAYTYPE: [{
+        label: '微信',
+        value: '0',
+    }, {
+        label: '支付宝',
+        value: '1',
+    }, {
+        label: '银行卡',
+        value: '2',
+    }],
+    DATALIST: [{
+        label: 'SEX',
+        value: 'SEX',
+    }, {
+        label: 'STATE',
+        value: 'STATE',
+    }, {
+        label: 'GRADE',
+        value: 'GRADE',
+    }]
+}

+ 12 - 0
src/const/errorCode.js

@@ -0,0 +1,12 @@
+export default {
+    '401': '当前操作没有权限',
+    '403': '当前操作没有权限',
+    '404': '资源不存在',
+    '417': '未绑定登录账号,请使用密码登录后绑定',
+    '423': '演示环境不能操作,如需了解联系冷冷',
+    '426': '用户名不存在或密码错误',
+    '428': '验证码错误,请重新输入',
+    '429': '请求过频繁',
+    '479': '演示环境,没有权限操作',
+    'default': '系统未知错误,请反馈给管理员'
+}

+ 38 - 0
src/const/logs/index.js

@@ -0,0 +1,38 @@
+export default {
+    menu: false,
+    addBtn: false,
+    page: false,
+    border: true,
+    expand: true,
+    refreshBtn: false,
+    headerAlign: 'center',
+    column: [{
+        label: '类型',
+        prop: 'type',
+        width: 80,
+        align: 'center',
+        solt: true,
+        dicData: [{
+            label: 'bug',
+            value: 'error'
+        }]
+    }, {
+        label: '地址',
+        width: 200,
+        prop: 'url',
+        overHidden: true,
+    }, {
+        label: '内容',
+        prop: 'message',
+        overHidden: true,
+    }, {
+        label: '错误堆栈',
+        prop: 'stack',
+        hide: true
+    }, {
+        label: '时间',
+        align: 'center',
+        prop: 'time',
+        width: 200,
+    }]
+}

+ 123 - 0
src/const/setting/index.js

@@ -0,0 +1,123 @@
+const dicData = [{
+    label: '开启',
+    value: 'true'
+}, {
+    label: '关闭',
+    value: 'false'
+}]
+export const list = [{
+    key: 'showTag',
+    commit: 'SET_SHOWTAG'
+}, {
+    key: 'showTheme',
+    commit: 'SET_SHOWTHEME'
+}, {
+    key: 'showColor',
+    commit: 'SET_SHOWCOLOR'
+}, {
+    key: 'showLock',
+    commit: 'SET_SHOWLOCK'
+}, {
+    key: 'showDebug',
+    commit: 'SET_SHOWDEBUG'
+}, {
+    key: 'showFullScren',
+    commit: 'SET_SHOWFULLSCREN'
+}, {
+    key: 'showCollapse',
+    commit: 'SET_SHOWCOLLAPSE'
+}, {
+    key: 'showSearch',
+    commit: 'SET_SHOWSEARCH'
+}, {
+    key: 'showMenu',
+    commit: 'SET_SHOWMENU'
+}]
+export const option = (safe) => {
+    const _safe = safe;
+    return {
+        submitBtn: false,
+        column: [{
+            label: '标签',
+            prop: 'showTag',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '日志',
+            prop: 'showDebug',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '主题',
+            prop: 'showTheme',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '主题色',
+            prop: 'showColor',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '全屏',
+            prop: 'showFullScren',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '锁屏',
+            prop: 'showLock',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '搜索',
+            prop: 'showSearch',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '缩放',
+            prop: 'showCollapse',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }, {
+            label: '顶部菜单',
+            prop: 'showMenu',
+            type: 'switch',
+            span: 24,
+            dicData: dicData,
+            click: ({ column }) => {
+                _safe.set(column.prop);
+            }
+        }]
+    }
+}

+ 26 - 0
src/const/website.js

@@ -0,0 +1,26 @@
+export default {
+    title: "Avue",
+    logo: "A",
+    indexTitle: 'avue-cli By smallwei',
+    whiteList: ["/login", "/404", "/401", "/lock"], //配置无权限可以访问的页面
+    whiteTagList: ["/login", "/404", "/401", "/lock", ], //配置不添加tags页面 ('/advanced-router/mutative-detail/*'——*为通配符)
+    lockPage: '/lock',
+    tokenTime: 6000,
+    info: {
+        title: "Avue 通用管理系统快速开发框架",
+        list: [
+            'Avue 是一个基于vue+vuex+vue-router快速后台管理模板,采用token交互验证方式。',
+            '您可以 Avue 为基础,不只限制于vue的页面,你可以嵌入第三方网站,基于iframe框架。',
+            'Avue 构建简单上手快,最大程度上帮助企业节省时间成本和费用开支。',
+        ]
+    },
+    //配置菜单的属性
+    menu: {
+        props: {
+            label: 'label',
+            path: 'path',
+            icon: 'icon',
+            children: 'children'
+        }
+    }
+}

+ 6 - 0
src/docker/Dockerfile

@@ -0,0 +1,6 @@
+FROM nginx
+VOLUME /tmp
+ENV LANG en_US.UTF-8
+ADD ./dist/ /usr/share/nginx/html/
+EXPOSE 80
+EXPOSE 443

+ 24 - 0
src/error.js

@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import store from './store'
+Vue.config.errorHandler = function(err, vm, info) {
+
+    Vue.nextTick(() => {
+        store.commit('ADD_LOGS', {
+            type: 'error',
+            message: err.message,
+            stack: err.stack,
+            info
+        })
+        if (process.env.NODE_ENV === 'development') {
+            console.group('>>>>>> 错误信息 >>>>>>')
+            console.log(info)
+            console.groupEnd();
+            console.group('>>>>>> Vue 实例 >>>>>>')
+            console.log(vm)
+            console.groupEnd();
+            console.group('>>>>>> Error >>>>>>')
+            console.log(err)
+            console.groupEnd();
+        }
+    })
+}

+ 130 - 0
src/filters/index.js

@@ -0,0 +1,130 @@
+function pluralize(time, label) {
+    if (time === 1) {
+        return time + label
+    }
+    return time + label + 's'
+}
+/**
+ * 日期格式化
+ */
+export function dateFormat(date) {
+    let format = 'yyyy-MM-dd hh:mm:ss';
+    if (date != 'Invalid Date') {
+        var o = {
+            "M+": date.getMonth() + 1, //month
+            "d+": date.getDate(), //day
+            "h+": date.getHours(), //hour
+            "m+": date.getMinutes(), //minute
+            "s+": date.getSeconds(), //second
+            "q+": Math.floor((date.getMonth() + 3) / 3), //quarter
+            "S": date.getMilliseconds() //millisecond
+        }
+        if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
+            (date.getFullYear() + "").substr(4 - RegExp.$1.length));
+        for (var k in o)
+            if (new RegExp("(" + k + ")").test(format))
+                format = format.replace(RegExp.$1,
+                    RegExp.$1.length == 1 ? o[k] :
+                    ("00" + o[k]).substr(("" + o[k]).length));
+        return format;
+    }
+    return '';
+
+}
+export function timeAgo(time) {
+    const between = Date.now() / 1000 - Number(time)
+    if (between < 3600) {
+        return pluralize(~~(between / 60), ' minute')
+    } else if (between < 86400) {
+        return pluralize(~~(between / 3600), ' hour')
+    } else {
+        return pluralize(~~(between / 86400), ' day')
+    }
+}
+
+export function parseTime(time, cFormat) {
+    if (arguments.length === 0) {
+        return null
+    }
+
+    if ((time + '').length === 10) {
+        time = +time * 1000
+    }
+
+    const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
+    let date
+    if (typeof time === 'object') {
+        date = time
+    } else {
+        date = new Date(parseInt(time))
+    }
+    const formatObj = {
+        y: date.getFullYear(),
+        m: date.getMonth() + 1,
+        d: date.getDate(),
+        h: date.getHours(),
+        i: date.getMinutes(),
+        s: date.getSeconds(),
+        a: date.getDay()
+    }
+    const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+        let value = formatObj[key]
+        if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
+        if (result.length > 0 && value < 10) {
+            value = '0' + value
+        }
+        return value || 0
+    })
+    return time_str
+}
+
+export function formatTime(time, option) {
+    time = +time * 1000
+    const d = new Date(time)
+    const now = Date.now()
+
+    const diff = (now - d) / 1000
+
+    if (diff < 30) {
+        return '刚刚'
+    } else if (diff < 3600) { // less 1 hour
+        return Math.ceil(diff / 60) + '分钟前'
+    } else if (diff < 3600 * 24) {
+        return Math.ceil(diff / 3600) + '小时前'
+    } else if (diff < 3600 * 24 * 2) {
+        return '1天前'
+    }
+    if (option) {
+        return parseTime(time, option)
+    } else {
+        return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
+    }
+}
+
+/* 数字 格式化*/
+export function nFormatter(num, digits) {
+    const si = [
+        { value: 1E18, symbol: 'E' },
+        { value: 1E15, symbol: 'P' },
+        { value: 1E12, symbol: 'T' },
+        { value: 1E9, symbol: 'G' },
+        { value: 1E6, symbol: 'M' },
+        { value: 1E3, symbol: 'k' }
+    ]
+    for (let i = 0; i < si.length; i++) {
+        if (num >= si[i].value) {
+            return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
+        }
+    }
+    return num.toString()
+}
+
+export function html2Text(val) {
+    const div = document.createElement('div')
+    div.innerHTML = val
+    return div.textContent || div.innerText
+}
+
+export function toThousandslsFilter(num) {
+    return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
+}

+ 66 - 0
src/main.js

@@ -0,0 +1,66 @@
+import 'babel-polyfill';
+import 'classlist-polyfill';
+import Vue from 'vue';
+import axios from './router/axios';
+import VueAxios from 'vue-axios';
+import App from './App';
+import './permission'; // 权限
+import './error'; // 日志
+import router from './router/router';
+import 'avue-plugin-transfer/packages' //引入avue-plugin-transfer插件
+// import 'avue-plugin-ueditor/packages' //引入avue-plugin-ueditor插件(如果要兼容ie自行换掉富文本编辑器,此款插件不兼容ie)
+import store from './store';
+import {
+    loadStyle
+} from './util/util'
+import * as urls from '@/config/env';
+import {
+    iconfontUrl,
+    iconfontVersion
+} from '@/config/env';
+import * as filters from './filters' // 全局filter
+import './styles/common.scss';
+// 引入avue的包
+import '@smallwei/avue/lib/index.js';
+// 引入avue的样式文件
+import '@smallwei/avue/lib/theme-chalk/index.css';
+
+// //源文件包
+// import './packages/index.js';
+// import './packages/theme-chalk/src/index.scss';
+
+import basicContainer from './components/basic-container/main'
+import VueClipboard from 'vue-clipboard2'
+// 插件 json 展示
+import vueJsonTreeView from 'vue-json-tree-view'
+
+
+Vue.use(router)
+
+Vue.use(VueClipboard)
+
+Vue.use(vueJsonTreeView)
+
+Vue.use(VueAxios, axios)
+
+Vue.component('basicContainer', basicContainer)
+
+Object.keys(urls).forEach(key => {
+    Vue.prototype[key] = urls[key];
+})
+
+Object.keys(filters).forEach(key => {
+    Vue.filter(key, filters[key])
+})
+
+iconfontVersion.forEach(ele => {
+    loadStyle(iconfontUrl.replace('$key', ele));
+})
+
+Vue.config.productionTip = false;
+
+new Vue({
+    router,
+    store,
+    render: h => h(App)
+}).$mount('#app')

+ 177 - 0
src/mixins/color.js

@@ -0,0 +1,177 @@
+import {
+  mapGetters
+} from "vuex";
+const version = require("element-ui/package.json").version; // element-ui version from node_modules
+const ORIGINAL_THEME = "#409EFF"; // default color
+export default function () {
+  return {
+    data() {
+      return {
+        themeVal: ORIGINAL_THEME
+      }
+    },
+    created() {
+      this.themeVal = this.theme;
+    },
+    watch: {
+      themeVal(val, oldVal) {
+        this.$store.commit("SET_THEME", val);
+        this.updateTheme(val, oldVal);
+      }
+    },
+    computed: {
+      ...mapGetters(["theme"])
+    },
+    methods: {
+      updateTheme(val, oldVal) {
+        if (typeof val !== "string") return;
+        const head = document.getElementsByTagName("head")[0];
+        const themeCluster = this.getThemeCluster(val.replace("#", ""));
+        const originalCluster = this.getThemeCluster(oldVal.replace("#", ""));
+        const getHandler = (variable, id) => {
+          return () => {
+            const originalCluster = this.getThemeCluster(
+              ORIGINAL_THEME.replace("#", "")
+            );
+            const newStyle = this.updateStyle(
+              this[variable],
+              originalCluster,
+              themeCluster
+            );
+
+            let styleTag = document.getElementById(id);
+            if (!styleTag) {
+              styleTag = document.createElement("style");
+              styleTag.setAttribute("id", id);
+              head.appendChild(styleTag);
+            }
+            styleTag.innerText = newStyle;
+          };
+        };
+
+        const chalkHandler = getHandler("chalk", "chalk-style");
+
+        if (!this.chalk) {
+          const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
+          this.getCSSString(url, chalkHandler, "chalk");
+        } else {
+          chalkHandler();
+        }
+
+        const link = [].slice.call(
+          document.getElementsByTagName("head")[0].getElementsByTagName("link")
+        );
+        for (let i = link.length - 3; i < link.length; i++) {
+          const style = link[i];
+          if (style.href.indexOf('app') != -1) {
+            this.getCSSString(style.href, innerText => {
+              const originalCluster = this.getThemeCluster(
+                ORIGINAL_THEME.replace("#", "")
+              );
+              const newStyle = this.updateStyle(
+                innerText,
+                originalCluster,
+                themeCluster
+              );
+              let styleTag = document.getElementById(i);
+              if (!styleTag) {
+                styleTag = document.createElement("style");
+                styleTag.id = i;
+                styleTag.innerText = newStyle;
+                head.appendChild(styleTag);
+              }
+            });
+          }
+
+        }
+
+        const styles = [].slice
+          .call(document.querySelectorAll("style"))
+          .filter(style => {
+            const text = style.innerText;
+            return (
+              new RegExp(oldVal, "i").test(text) && !/Chalk Variables/.test(text)
+            );
+          });
+        styles.forEach(style => {
+          const {
+            innerText
+          } = style;
+          if (typeof innerText !== "string") return;
+          style.innerText = this.updateStyle(
+            innerText,
+            originalCluster,
+            themeCluster
+          );
+        });
+      },
+      updateStyle(style, oldCluster, newCluster) {
+        let newStyle = style;
+        oldCluster.forEach((color, index) => {
+          newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
+        });
+        return newStyle;
+      },
+
+      getCSSString(url, callback, variable) {
+        const xhr = new XMLHttpRequest();
+        xhr.onreadystatechange = () => {
+          if (xhr.readyState === 4 && xhr.status === 200) {
+            if (variable) {
+              this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
+            }
+            callback(xhr.responseText);
+          }
+        };
+        xhr.open("GET", url);
+        xhr.send();
+      },
+
+      getThemeCluster(theme) {
+        const tintColor = (color, tint) => {
+          let red = parseInt(color.slice(0, 2), 16);
+          let green = parseInt(color.slice(2, 4), 16);
+          let blue = parseInt(color.slice(4, 6), 16);
+
+          if (tint === 0) {
+            // when primary color is in its rgb space
+            return [red, green, blue].join(",");
+          } else {
+            red += Math.round(tint * (255 - red));
+            green += Math.round(tint * (255 - green));
+            blue += Math.round(tint * (255 - blue));
+
+            red = red.toString(16);
+            green = green.toString(16);
+            blue = blue.toString(16);
+
+            return `#${red}${green}${blue}`;
+          }
+        };
+
+        const shadeColor = (color, shade) => {
+          let red = parseInt(color.slice(0, 2), 16);
+          let green = parseInt(color.slice(2, 4), 16);
+          let blue = parseInt(color.slice(4, 6), 16);
+
+          red = Math.round((1 - shade) * red);
+          green = Math.round((1 - shade) * green);
+          blue = Math.round((1 - shade) * blue);
+
+          red = red.toString(16);
+          green = green.toString(16);
+          blue = blue.toString(16);
+
+          return `#${red}${green}${blue}`;
+        };
+
+        const clusters = [theme];
+        for (let i = 0; i <= 9; i++) {
+          clusters.push(tintColor(theme, Number((i / 10).toFixed(2))));
+        }
+        clusters.push(shadeColor(theme, 0.1));
+        return clusters;
+      }
+    }
+  }
+}

+ 110 - 0
src/page/index/index.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="avue-contail"
+       :class="{'avue--collapse':isCollapse}">
+    <div class="avue-header">
+      <!-- 顶部导航栏 -->
+      <top />
+    </div>
+
+    <div class="avue-layout">
+      <div class="avue-left">
+        <!-- 左侧导航栏 -->
+        <sidebar />
+      </div>
+      <div class="avue-main">
+        <!-- 顶部标签卡 -->
+        <tags />
+        <!-- 主体视图层 -->
+        <el-scrollbar style="height:100%">
+          <keep-alive>
+            <router-view class="avue-view"
+                         v-if="$route.meta.keepAlive" />
+          </keep-alive>
+          <router-view class="avue-view"
+                       v-if="!$route.meta.keepAlive" />
+        </el-scrollbar>
+
+      </div>
+    </div>
+    <!-- <el-footer class="avue-footer">
+      <img src="/svg/logo.svg"
+           alt=""
+           class="logo">
+      <p class="copyright">© 2018 Avue designed by smallwei</p>
+    </el-footer> -->
+    <div class="avue-shade"
+         @click="showCollapse"></div>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import tags from './tags'
+import top from './top/'
+import sidebar from './sidebar/'
+import admin from '@/util/admin';
+import { validatenull } from '@/util/validate';
+import { calcDate } from '@/util/date.js';
+import { getStore } from '@/util/store.js';
+export default {
+  components: {
+    top,
+    tags,
+    sidebar
+  },
+  name: 'index',
+  data () {
+    return {
+      //刷新token锁
+      refreshLock: false,
+      //刷新token的时间
+      refreshTime: '',
+    }
+  },
+  created () {
+    //实时检测刷新token
+    // this.refreshToken();
+  },
+  mounted () {
+    this.init();
+  },
+  computed: mapGetters(['isLock', 'isCollapse', 'website']),
+  props: [],
+  methods: {
+    showCollapse () {
+      this.$store.commit("SET_COLLAPSE");
+    },
+    // 屏幕检测
+    init () {
+      this.$store.commit('SET_SCREEN', admin.getScreen());
+      window.onresize = () => {
+        setTimeout(() => {
+          this.$store.commit('SET_SCREEN', admin.getScreen());
+        }, 0);
+      }
+    },
+    // 实时检测刷新token
+    refreshToken () {
+      this.refreshTime = setInterval(() => {
+        const token = getStore({
+          name: 'token',
+          debug: true,
+        });
+        const date = calcDate(token.datetime, new Date().getTime());
+        if (validatenull(date)) return;
+        if (!(date.seconds >= this.website.tokenTime) && !this.refreshLock) {
+          this.refreshLock = true;
+          this.$store
+            .dispatch('RefeshToken')
+            .then(() => {
+              clearInterval(this.refreshTime);
+            })
+            .catch(() => {
+              this.refreshLock = false;
+            });
+        }
+      }, 3000);
+    },
+  }
+}
+</script>

+ 3 - 0
src/page/index/layout.vue

@@ -0,0 +1,3 @@
+<template>
+  <router-view></router-view>
+</template>

+ 74 - 0
src/page/index/logo.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="avue-logo">
+    <transition name="fade">
+      <span v-if="keyCollapse"
+            class="avue-logo_subtitle"
+            key="0">
+        {{website.logo}}
+      </span>
+    </transition>
+    <transition-group name="fade">
+      <template v-if="!keyCollapse">
+        <span class="avue-logo_title"
+              key="1">{{website.indexTitle}} </span>
+      </template>
+    </transition-group>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+export default {
+  name: "logo",
+  data () {
+    return {};
+  },
+  created () {
+  },
+  computed: {
+    ...mapGetters(["website", 'keyCollapse'])
+  },
+  methods: {}
+};
+</script>
+
+<style lang="scss">
+.fade-leave-active {
+  transition: opacity 0.2s;
+}
+.fade-enter-active {
+  transition: opacity 2.5s;
+}
+.fade-enter,
+.fade-leave-to {
+  opacity: 0;
+}
+.avue-logo {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 220px;
+  height: 50px;
+  line-height: 50px;
+  background-color: #20222a;
+  font-size: 20px;
+  overflow: hidden;
+  box-sizing: border-box;
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
+  color: rgba(255, 255, 255, 0.8);
+  z-index: 1024;
+  &_title {
+    display: block;
+    text-align: center;
+    font-weight: 300;
+    font-size: 16px;
+  }
+  &_subtitle {
+    display: block;
+    text-align: center;
+    font-size: 18px;
+    font-weight: bold;
+    color: #fff;
+  }
+}
+</style>

+ 8 - 0
src/page/index/sidebar/config.js

@@ -0,0 +1,8 @@
+export default {
+    propsDefault: {
+        label: 'label',
+        path: 'path',
+        icon: 'icon',
+        children: 'children'
+    }
+}

+ 48 - 0
src/page/index/sidebar/index.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="avue-sidebar">
+    <logo></logo>
+    <el-scrollbar style="height:100%">
+      <el-menu unique-opened
+               :default-active="nowTagValue"
+               mode="vertical"
+               :show-timeout="200"
+               background-color="#20222a"
+               text-color="rgba(255,255,255,0.7)"
+               :collapse="keyCollapse">
+        <sidebar-item :menu="menu"
+                      :screen="screen"
+                      :props="website.menu.props"
+                      :collapse="keyCollapse"></sidebar-item>
+      </el-menu>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import logo from '../logo';
+import sidebarItem from './sidebarItem'
+export default {
+  name: 'sidebar',
+  components: { sidebarItem, logo },
+  data () {
+    return {}
+  },
+  created () {
+    console.log(this.menu);
+    this.$store.dispatch("GetMenu").then(data => {
+      if (data.length === 0) return
+      this.$router.addRoutes(this.$router.$avueRouter.formatRoutes(data, true))
+    });
+  },
+  computed: {
+    ...mapGetters(['website', 'menu', 'tag', 'keyCollapse', 'screen']),
+    nowTagValue: function () { return this.$router.$avueRouter.getValue(this.$route) }
+  },
+  mounted () { },
+  methods: {}
+}
+</script>
+<style lang="scss" scoped>
+</style>
+

+ 103 - 0
src/page/index/sidebar/sidebarItem.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="menu-wrapper">
+    <template v-for="(item,index) in menu">
+      <el-menu-item v-if="validatenull(item[childrenKey])"
+                    :index="filterPath(item[pathKey],index)"
+                    @click="open(item)"
+                    :key="item[labelKey]">
+        <i :class="item[iconKey]"></i>
+        <span slot="title">{{item[labelKey]}}</span>
+      </el-menu-item>
+      <el-submenu v-else
+                  :index="filterPath(item[labelKey],index)"
+                  :key="item[labelKey]">
+        <template slot="title">
+          <i :class="item[iconKey]"></i>
+          <span slot="title"
+                :class="{'el-menu--display':collapse}">{{item[labelKey]}}</span>
+        </template>
+        <template v-for="(child,cindex) in item[childrenKey]">
+          <el-menu-item :class="{'siderbar-active':nowTagValue==child[pathKey]}"
+                        :index="filterPath(child[pathKey],cindex)"
+                        @click="open(child)"
+                        v-if="validatenull(child[childrenKey])"
+                        :key="child[labelKey]">
+            <i :class="child[iconKey]"></i>
+            <span slot="title">{{child[labelKey]}}</span>
+          </el-menu-item>
+          <sidebar-item v-else
+                        :menu="[child]"
+                        :key="cindex"
+                        :props="props"
+                        :screen="screen"
+                        :collapse="collapse"></sidebar-item>
+        </template>
+      </el-submenu>
+    </template>
+  </div>
+</template>
+<script>
+import { validatenull } from '@/util/validate';
+import config from './config.js'
+export default {
+  name: 'sidebarItem',
+  data () {
+    return {
+      config: config
+    }
+  },
+  props: {
+    menu: {
+      type: Array
+    },
+    screen: {
+      type: Number
+    },
+    props: {
+      type: Object,
+      default: () => { return {} }
+    },
+    collapse: {
+      type: Boolean
+    }
+  },
+  created () {
+  },
+  mounted () { },
+  computed: {
+    labelKey () { return this.props.label || this.config.propsDefault.label },
+    pathKey () { return this.props.path || this.config.propsDefault.path },
+    iconKey () { return this.props.icon || this.config.propsDefault.icon },
+    childrenKey () { return this.props.children || this.config.propsDefault.children },
+    nowTagValue () { return this.$router.$avueRouter.getValue(this.$route) }
+  },
+  methods: {
+    validatenull (val) {
+      return validatenull(val);
+    },
+    filterPath (path, index) {
+      return path == null ? index + '' : path
+    },
+    open (item) {
+      if (this.screen <= 1) this.$store.commit("SET_COLLAPSE");
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          name: item[this.labelKey],
+          src: item[this.pathKey]
+        }),
+        query: item.query
+      })
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+//刷新激活状态
+.siderbar-active {
+  i,
+  span {
+    color: #409eff;
+  }
+}
+</style>
+

+ 121 - 0
src/page/index/tags.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="avue-tags"
+       v-if="showTag">
+    <!-- tag盒子 -->
+    <div class="tags-box"
+         ref="tagBox">
+      <el-tabs v-model="active"
+               type="card"
+               :closable="tagLen!==1"
+               @tab-click="openTag"
+               @edit="menuTag">
+        <el-tab-pane :key="item.value"
+                     v-for="item in tagList"
+                     :label="item.label"
+                     :name="item.value">
+        </el-tab-pane>
+      </el-tabs>
+      <el-dropdown class="tags-menu pull-right">
+        <el-button type="primary"
+                   size="mini">
+          更多
+          <i class="el-icon-arrow-down el-icon--right"></i>
+        </el-button>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item @click.native="closeOthersTags">关闭其他</el-dropdown-item>
+          <el-dropdown-item @click.native="closeAllTags">关闭全部</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+
+  </div>
+</template>
+<script>
+import { mapGetters, mapState } from 'vuex'
+
+import { isObjectValueEqual } from '@/util/util';
+export default {
+  name: 'tags',
+  data () {
+    return {
+      active: '',
+    }
+  },
+  created () { },
+  mounted () {
+    this.setActive();
+  },
+  watch: {
+    tag () {
+      this.setActive();
+    }
+  },
+  computed: {
+    ...mapGetters(['tagWel', 'tagList', 'tag']),
+    ...mapState({
+      showTag: state => state.common.showTag,
+    }),
+    tagLen () {
+      return this.tagList.length || 0;
+    }
+  },
+  methods: {
+    //激活当前选项
+    setActive () {
+      this.active = this.tag.value;
+    },
+    menuTag (value, action) {
+      if (action === 'remove') {
+        let { tag, key } = this.findTag(value);
+        this.$store.commit('DEL_TAG', tag)
+        if (tag.value === this.tag.value) {
+          tag = this.tagList[key === 0 ? key : key - 1] //如果关闭本标签让前推一个
+          this.openTag(tag)
+        }
+      }
+    },
+    openTag (item) {
+      let tag;
+      if (item.name) {
+        tag = this.findTag(item.name).tag;
+      } else {
+        tag = item;
+      }
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          name: tag.label,
+          src: tag.value
+        }),
+        query: tag.query
+      })
+    },
+    isObjectValueEqual (a, b) {
+      return isObjectValueEqual(a, b)
+    },
+    closeOthersTags () {
+      this.$store.commit('DEL_TAG_OTHER')
+    },
+    findTag (value) {
+      let tag, key;
+      this.tagList.map((item, index) => {
+        if (item.value === value) {
+          tag = item;
+          key = index;
+        }
+      });
+      return { tag: tag, key: key };
+    },
+    closeAllTags () {
+      this.$store.commit('DEL_ALL_TAG')
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          src: this.tagWel.value
+        }),
+        query: this.tagWel.query
+      })
+    },
+  }
+}
+</script>
+
+

+ 170 - 0
src/page/index/top/index.vue

@@ -0,0 +1,170 @@
+<template>
+  <div class="avue-top">
+    <div class="top-bar__left">
+      <div class="avue-breadcrumb"
+           v-if="showCollapse">
+        <i class="icon-navicon avue-breadcrumb_collapse"
+           :class="[{ 'avue-breadcrumb_collapse--right': isCollapse }]"
+           @click="setCollapse"></i>
+      </div>
+    </div>
+    <h1 class="top-bar__title">
+      <div class="top-bar__item top-bar__item--show"
+           v-if="showMenu">
+        <top-menu></top-menu>
+      </div>
+      <span class="top-bar__item"
+            v-if="showSearch">
+        <top-search></top-search>
+      </span>
+    </h1>
+    <div class="top-bar__right">
+      <el-tooltip v-if="showColor"
+                  effect="dark"
+                  content="主题色"
+                  placement="bottom">
+        <div class="top-bar__item">
+          <top-color></top-color>
+        </div>
+      </el-tooltip>
+      <el-tooltip v-if="showDebug"
+                  effect="dark"
+                  :content="logsFlag?'没有错误日志':`${logsLen}条错误日志`"
+                  placement="bottom">
+        <div class="top-bar__item">
+          <top-logs></top-logs>
+        </div>
+      </el-tooltip>
+      <el-tooltip v-if="showLock"
+                  effect="dark"
+                  content="锁屏"
+                  placement="bottom">
+        <div class="top-bar__item">
+          <top-lock></top-lock>
+        </div>
+      </el-tooltip>
+      <el-tooltip v-if="showTheme"
+                  effect="dark"
+                  content="特色主题"
+                  placement="bottom">
+        <div class="top-bar__item top-bar__item--show">
+          <top-theme></top-theme>
+        </div>
+      </el-tooltip>
+      <el-tooltip v-if="showFullScren"
+                  effect="dark"
+                  :content="isFullScren?'退出全屏':'全屏'"
+                  placement="bottom">
+        <div class="top-bar__item">
+          <i :class="isFullScren?'icon-tuichuquanping':'icon-quanping'"
+             @click="handleScreen"></i>
+        </div>
+      </el-tooltip>
+      <el-tooltip effect="dark"
+                  content="用户头像"
+                  placement="bottom">
+        <img class="top-bar__img"
+             :src="userInfo.avatar">
+      </el-tooltip>
+      <el-dropdown>
+        <span class="el-dropdown-link">
+          {{userInfo.username}}
+          <i class="el-icon-arrow-down el-icon--right"></i>
+        </span>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item>
+            <router-link to="/">首页</router-link>
+          </el-dropdown-item>
+          <el-dropdown-item>
+            <router-link to="/info/index">个人信息</router-link>
+          </el-dropdown-item>
+          <el-dropdown-item>
+            <a href="https://gitee.com/smallweigit/avue"
+               target="_blank">码云地址</a>
+          </el-dropdown-item>
+          <el-dropdown-item>
+            <a href="https://github.com/nmxiaowei/avue"
+               target="_blank">github</a>
+          </el-dropdown-item>
+          <el-dropdown-item @click.native="logout"
+                            divided>退出系统</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+      <top-setting></top-setting>
+    </div>
+  </div>
+</template>
+<script>
+import { mapGetters, mapState } from "vuex";
+import { fullscreenToggel, listenfullscreen } from "@/util/util";
+import topLock from "./top-lock";
+import topMenu from "./top-menu";
+import topSearch from './top-search';
+import topBreadcrumb from "./top-breadcrumb";
+import topColor from "./top-color";
+import topTheme from "./top-theme";
+import topLogs from "./top-logs";
+import topSetting from "./top-setting";
+export default {
+  components: { topLock, topMenu, topSearch, topBreadcrumb, topColor, topTheme, topLogs, topSetting },
+  name: "top",
+  data () {
+    return {
+
+    };
+  },
+  filters: {},
+  created () { },
+  mounted () {
+    listenfullscreen(this.setScreen);
+  },
+  computed: {
+    ...mapState({
+      showDebug: state => state.common.showDebug,
+      showColor: state => state.common.showColor,
+      showTheme: state => state.common.showTheme,
+      showLock: state => state.common.showLock,
+      showFullScren: state => state.common.showFullScren,
+      showCollapse: state => state.common.showCollapse,
+      showSearch: state => state.common.showSearch,
+      showMenu: state => state.common.showMenu
+    }),
+    ...mapGetters([
+      "userInfo",
+      "isFullScren",
+      "tagWel",
+      "tagList",
+      "isCollapse",
+      "tag",
+      "logsLen",
+      "logsFlag"
+    ]),
+  },
+  methods: {
+    handleScreen () {
+      fullscreenToggel();
+    },
+    setCollapse () {
+      this.$store.commit("SET_COLLAPSE");
+    },
+    setScreen () {
+      this.$store.commit("SET_FULLSCREN");
+    },
+    logout () {
+      this.$confirm("是否退出系统, 是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.$store.dispatch("LogOut").then(() => {
+          this.$router.push({ path: "/login" });
+        });
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+</style>
+

+ 29 - 0
src/page/index/top/top-breadcrumb.vue

@@ -0,0 +1,29 @@
+<template>
+  <el-breadcrumb separator="/"
+                 class="avue-breadcrumb-list">
+    <template v-for="(item,index) in tagCurrent">
+      <el-breadcrumb-item :to="{ path: item.value }"
+                          :key="index">{{item.label}}</el-breadcrumb-item>
+    </template>
+  </el-breadcrumb>
+</template>
+<script>
+import { mapGetters } from "vuex";
+export default {
+  name: "breadcrumb",
+  data () {
+    return {};
+  },
+  created () { },
+  mounted () { },
+  watch: {},
+  computed: {
+    ...mapGetters(["tagCurrent"])
+  },
+  methods: {}
+};
+</script>
+<style lang="scss" scoped>
+</style>
+
+

+ 30 - 0
src/page/index/top/top-color.vue

@@ -0,0 +1,30 @@
+<template>
+  <el-color-picker size="small"
+                   class="theme-picker"
+                   popper-class="theme-picker-dropdown"
+                   v-model="themeVal"></el-color-picker>
+</template>
+
+<script>
+import color from "@/mixins/color";
+
+export default {
+  name: "topColor",
+  mixins: [color()],
+  data () {
+    return {
+      chalk: ""
+    };
+  }
+};
+</script>
+
+<style>
+.theme-picker .el-color-picker__trigger {
+  vertical-align: middle;
+}
+
+.theme-picker-dropdown .el-color-dropdown__link-btn {
+  display: none;
+}
+</style>

+ 72 - 0
src/page/index/top/top-lock.vue

@@ -0,0 +1,72 @@
+<template>
+  <span>
+    <i class="icon-bofangqi-suoping"
+       @click="handleLock"></i>
+    <el-dialog title="设置锁屏密码"
+               :visible.sync="box"
+               width="30%"
+               append-to-body>
+      <el-form :model="form"
+               ref="form"
+               label-width="80px">
+        <el-form-item label="锁屏密码"
+                      prop="passwd"
+                      :rules="[{ required: true, message: '锁屏密码不能为空'}]">
+          <el-input v-model="form.passwd"
+                    placeholder="请输入锁屏密码"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer"
+            class="dialog-footer">
+        <el-button type="primary"
+                   @click="handleSetLock">确 定</el-button>
+      </span>
+    </el-dialog>
+  </span>
+</template>
+
+<script>
+import { validatenull } from "@/util/validate";
+import { mapGetters } from "vuex";
+export default {
+  name: "top-lock",
+  data () {
+    return {
+      box: false,
+      form: {
+        passwd: ""
+      }
+    };
+  },
+  created () { },
+  mounted () { },
+  computed: {
+    ...mapGetters(["lockPasswd"])
+  },
+  props: [],
+  methods: {
+    handleSetLock () {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          this.$store.commit("SET_LOCK_PASSWD", this.form.passwd);
+          this.handleLock();
+        }
+      });
+    },
+    handleLock () {
+      if (validatenull(this.lockPasswd)) {
+        this.box = true;
+        return;
+      }
+      this.$store.commit("SET_LOCK");
+      setTimeout(() => {
+        this.$router.push({ path: "/lock" });
+      }, 100);
+    }
+  },
+  components: {}
+};
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 43 - 0
src/page/index/top/top-logs.vue

@@ -0,0 +1,43 @@
+<template>
+  <span @click="logsFlag?'':handleOpen()">
+    <el-badge :value="logsFlag?'':logsLen">
+      <i class="icon-bug"></i>
+    </el-badge>
+    <el-dialog title="日志"
+               fullscreen
+               :visible.sync="box"
+               width="100%"
+               append-to-body>
+      <logs></logs>
+    </el-dialog>
+  </span>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import logs from '@/page/logs/index'
+export default {
+  name: "top-logs",
+  components: { logs },
+  data () {
+    return {
+      box: false
+    };
+  },
+  created () {
+  },
+  mounted () { },
+  computed: {
+    ...mapGetters(["logsFlag", "logsLen"]),
+  },
+  props: [],
+  methods: {
+    handleOpen () {
+      this.box = true;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 76 - 0
src/page/index/top/top-menu.vue

@@ -0,0 +1,76 @@
+<template>
+  <div class="top-menu">
+    <el-menu :default-active="activeIndex"
+             mode="horizontal"
+             text-color="#333">
+      <template v-for="(item,index) in items">
+        <el-menu-item :index="item.parentId+''"
+                      @click.native="openMenu(item)"
+                      :key="index">
+          <template slot="title">
+            <i :class="item.icon"></i>
+          </template>
+        </el-menu-item>
+      </template>
+    </el-menu>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+export default {
+  name: "top-menu",
+  data () {
+    return {
+      activeIndex: "0",
+      items: [
+        {
+          label: "首页",
+          href: "/wel/index",
+          icon: 'el-icon-menu',
+          parentId: 0
+        },
+        {
+          label: '文档',
+          href: 'https://www.kancloud.cn/lengleng/pig-guide/627970',
+          icon: 'el-icon-document',
+          parentId: 1
+        },
+        {
+          label: 'crud实例',
+          href: '/crud/index',
+          icon: 'el-icon-setting',
+          parentId: 2
+        }
+      ]
+    };
+  },
+  created () { },
+  computed: {
+    ...mapGetters(["tagCurrent", "menu"])
+  },
+  methods: {
+    openMenu (item) {
+      this.$store.dispatch("GetMenu", item.parentId).then(() => {
+        let itemActive,
+          childItemActive = 0;
+        if (item.href) {
+          itemActive = item;
+        } else {
+          if (this.menu[childItemActive].length == 0) {
+            itemActive = this.menu[childItemActive];
+          } else {
+            itemActive = this.menu[childItemActive].children[childItemActive];
+          }
+        }
+        this.$router.push({
+          path: this.$router.$avueRouter.getPath({
+            name: itemActive.label,
+            src: itemActive.href
+          })
+        });
+      });
+    }
+  }
+};
+</script>

+ 111 - 0
src/page/index/top/top-search.vue

@@ -0,0 +1,111 @@
+<template>
+  <el-autocomplete class="top-search"
+                   popper-class="my-autocomplete"
+                   v-model="value"
+                   :fetch-suggestions="querySearch"
+                   placeholder="请输入搜索内容"
+                   @select="handleSelect">
+
+    <template slot-scope="{ item }">
+      <i :class="[item[iconKey],'icon']"></i>
+      <div class="name">{{ item[labelKey] }}</div>
+      <p class="addr">{{ item[pathKey] }}</p>
+    </template>
+  </el-autocomplete>
+</template>
+
+<script>
+import config from '../sidebar/config.js'
+import { mapGetters } from "vuex";
+export default {
+  data () {
+    return {
+      config: config,
+      value: '',
+      menuList: [],
+    }
+  },
+  created () {
+    this.getMenuList();
+  },
+
+  watch: {
+    menu () {
+      this.getMenuList();
+    }
+  },
+  computed: {
+    labelKey () { return this.website.menu.props.label || this.config.propsDefault.label },
+    pathKey () { return this.website.menu.props.path || this.config.propsDefault.path },
+    iconKey () { return this.website.menu.props.icon || this.config.propsDefault.icon },
+    childrenKey () { return this.website.menu.props.children || this.config.propsDefault.children },
+    ...mapGetters(['menu', 'website'])
+  },
+  methods: {
+    getMenuList () {
+      const findMenu = (list) => {
+        for (let i = 0; i < list.length; i++) {
+          const ele = Object.assign({}, list[i]);
+          if (ele[this.childrenKey]) findMenu(ele[this.childrenKey]);
+          delete ele[this.childrenKey];
+          this.menuList.push(ele);
+        }
+      }
+      this.menuList = [];
+      findMenu(this.menu);
+    },
+    querySearch (queryString, cb) {
+      var restaurants = this.menuList;
+      var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
+      // 调用 callback 返回建议列表的数据
+      cb(results);
+    },
+    createFilter (queryString) {
+      return (restaurant) => {
+        return (restaurant.label.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
+      };
+    },
+    handleSelect (item) {
+      this.value = '';
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          name: item[this.labelKey],
+          src: item[this.pathKey]
+        }),
+        query: item.query
+      })
+    },
+  }
+
+}
+</script>
+
+<style lang="scss">
+.my-autocomplete {
+  li {
+    line-height: normal;
+    padding: 7px;
+    .icon {
+      margin-right: 5px;
+      display: inline-block;
+      vertical-align: middle;
+    }
+    .name {
+      display: inline-block;
+      text-overflow: ellipsis;
+      overflow: hidden;
+      vertical-align: middle;
+    }
+    .addr {
+      padding-top: 5px;
+      width: 100%;
+      font-size: 12px;
+      color: #b4b4b4;
+    }
+
+    .highlighted .addr {
+      color: #ddd;
+    }
+  }
+}
+</style>

+ 158 - 0
src/page/index/top/top-setting.vue

@@ -0,0 +1,158 @@
+<template>
+  <span class="setting">
+    <div class="setting__shade"
+         :class="{'setting__shade--show':isShade}"
+         @click="close"></div>
+    <i class="el-icon-more setting__icon"
+       @click="open"></i>
+    <div class="setting__content"
+         :class="{'setting__content--show':box}">
+      <div class="setting__header">版权信息</div>
+      <div class="setting__body setting__about">
+        <p>当前版本:avue-cli v2.x </p>
+        <p>基于框架:avue v2.x</p>
+        <a href="https://avue.top/#/component/avue2.x"
+           target="_blank">
+          <el-button type="primary">
+            获取源码
+          </el-button>
+        </a>
+        &nbsp;&nbsp;&nbsp;&nbsp;
+        <a href="https://pig4cloud.com"
+           target="_blank">
+          <el-button type="primary">
+            查看详情
+          </el-button>
+        </a>
+      </div>
+      <div class="setting__header">设置
+        <small>(滑动鼠标下面还有更多设置)</small>
+      </div>
+      <el-scrollbar style="height:500px">
+        <div class="setting__body setting__form">
+          <avue-form v-model="form"
+                     :option="option"></avue-form>
+        </div>
+      </el-scrollbar>
+    </div>
+  </span>
+</template>
+
+<script>
+import { mapState, mapGetters } from 'vuex'
+import { validatenull } from '@/util/validate'
+import { option, list } from '@/const/setting/'
+export default {
+  data () {
+    return {
+      box: false,
+      form: {},
+      list: list,
+      option: option(this)
+    }
+  },
+  computed: {
+    ...mapGetters(['isShade']),
+    ...mapState({
+      showTag: state => state.common.showTag,
+      showDebug: state => state.common.showDebug,
+      showLock: state => state.common.showLock,
+      showColor: state => state.common.showColor,
+      showFullScren: state => state.common.showFullScren,
+      showCollapse: state => state.common.showCollapse,
+      showSearch: state => state.common.showSearch,
+      showMenu: state => state.common.showMenu,
+      showTheme: state => state.common.showTheme
+    })
+  },
+  created () {
+    this.init();
+  },
+  methods: {
+    close () {
+      this.box = false;
+      this.$store.commit('SET_SHADE', false);
+    },
+    set (key) {
+      const ele = this.find(key);
+      this.$store.commit(ele.commit, eval(this.form[ele.key]));
+    },
+    find (key) {
+      return this.list.filter(ele => ele.key === key)[0]
+    },
+    init () {
+      this.list.forEach(ele => {
+        this.form[ele.key] = validatenull(this[ele.key]) ? 'true' : this[ele.key] + '';
+        this.set(ele.key);
+      })
+    },
+    open () {
+      this.box = true;
+      this.$store.commit('SET_SHADE', true);
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.setting {
+  margin-left: 10px;
+  &__icon {
+    transform: rotate(90deg);
+  }
+  &__header {
+    height: 42px;
+    line-height: 42px;
+    padding: 0 15px;
+    border-bottom: 1px solid #f6f6f6;
+    color: #333;
+    border-radius: 2px 2px 0 0;
+    font-size: 14px;
+    small {
+      margin-left: 8px;
+      color: #999;
+    }
+  }
+  &__body {
+    padding: 10px 15px;
+    line-height: 24px;
+  }
+  &__about {
+    font-size: 14px;
+    line-height: 30px;
+  }
+  &__shade {
+    position: fixed;
+    display: none;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    background-color: rgba(0, 0, 0, 0.3);
+    z-index: 2048;
+    &--show {
+      display: block;
+    }
+  }
+  &__form {
+    width: 230px;
+    margin: 0 auto;
+  }
+  &__content {
+    box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.1);
+    transition: all 0.3s;
+    position: fixed;
+    width: 320px;
+    height: 100%;
+    right: -450px;
+    top: 0;
+    z-index: 2048;
+    background-color: #fff;
+    &--show {
+      right: 0;
+    }
+  }
+}
+</style>

+ 81 - 0
src/page/index/top/top-theme.vue

@@ -0,0 +1,81 @@
+<template>
+  <div>
+    <el-dialog title="选择"
+               :visible.sync="box"
+               width="50%">
+      <el-radio-group v-model="text"
+                      class="list">
+        <el-row :span="24">
+
+          <el-col v-for="(item,index) in list"
+                  :key="index"
+                  :md="4"
+                  :xs="12"
+                  :sm="4">
+            <el-radio :label="item.value">{{item.name}}</el-radio>
+          </el-col>
+        </el-row>
+      </el-radio-group>
+    </el-dialog>
+
+    <span>
+      <i class="el-icon-view"
+         @click="open"></i>
+    </span>
+  </div>
+</template>
+
+<script>
+import { setTheme } from '@/util/util'
+import { mapGetters } from 'vuex';
+export default {
+  data () {
+    return {
+      box: false,
+      text: '',
+      list: [
+        {
+          name: '默认主题',
+          value: '',
+        }, {
+          name: '炫彩主题',
+          value: 'theme-star'
+        }, {
+          name: '黑色主题',
+          value: 'theme-black'
+        }, {
+          name: '渐变主题',
+          value: 'theme-gradual'
+        }
+      ]
+    }
+  },
+  watch: {
+    text: function (val) {
+      this.$store.commit('SET_THEME_NAME', val);
+      setTheme(val);
+    }
+  },
+  computed: {
+    ...mapGetters(["themeName"])
+  },
+  mounted () {
+    this.text = this.themeName;
+    if (!this.text) {
+      this.text = '';
+    }
+  },
+  methods: {
+    open () {
+      this.box = true;
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.list {
+  width: 100%;
+}
+</style>
+

+ 105 - 0
src/page/lock/index.vue

@@ -0,0 +1,105 @@
+<template>
+  <div class="lock-container pull-height">
+    <div class="lock-form animated bounceInDown">
+      <div class="animated"
+           :class="{'shake':passwdError,'bounceOut':pass}">
+        <h3 class="title">{{userInfo.username}}</h3>
+        <el-input placeholder="请输入登录密码"
+                  type="password"
+                  class="input-with-select animated"
+                  v-model="passwd"
+                  @keyup.enter.native="handleLogin">
+          <el-button slot="append"
+                     icon="icon-bofangqi-suoping"
+                     @click="handleLogin"></el-button>
+          <el-button slot="append"
+                     icon="icon-tuichu"
+                     @click="handleLogout"></el-button>
+        </el-input>
+      </div>
+
+    </div>
+  </div>
+</template>
+<script>
+import { mapGetters, mapState } from "vuex";
+export default {
+  name: "lock",
+  data () {
+    return {
+      passwd: "",
+      passwdError: false,
+      pass: false
+    };
+  },
+  created () { },
+  mounted () { },
+  computed: {
+    ...mapState({
+      userInfo: state => state.user.userInfo
+    }),
+    ...mapGetters(["tag", "lockPasswd"])
+  },
+  props: [],
+  methods: {
+    handleLogout () {
+      this.$confirm("是否退出系统, 是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.$store.dispatch("LogOut").then(() => {
+          this.$router.push({ path: "/login" });
+        });
+      });
+    },
+    handleLogin () {
+      if (this.passwd != this.lockPasswd) {
+        this.passwd = "";
+        this.$message({
+          message: "解锁密码错误,请重新输入",
+          type: "error"
+        });
+        this.passwdError = true;
+        setTimeout(() => {
+          this.passwdError = false;
+        }, 1000);
+        return;
+      }
+      this.pass = true;
+      setTimeout(() => {
+        this.$store.commit("CLEAR_LOCK");
+        this.$router.push({ path: this.$router.$avueRouter.getPath({ src: this.tag.value }) });
+      }, 1000);
+    }
+  },
+  components: {}
+};
+</script>
+
+<style lang="scss">
+.lock-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  .title {
+    margin-bottom: 8px;
+    color: #333;
+  }
+}
+.lock-container::before {
+  z-index: -999;
+  content: '';
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background-image: url('/img/login.png');
+  background-size: cover;
+}
+.lock-form {
+  width: 300px;
+}
+</style>

+ 42 - 0
src/page/login/authredirect.vue

@@ -0,0 +1,42 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+import request from '@/router/axios'
+import { mapGetters } from 'vuex'
+export default {
+  name: 'authredirect',
+  computed: {
+    ...mapGetters(['tagWel'])
+  },
+  created () {
+    const params = this.$route.query
+    const state = params.state
+    const code = params.code
+    const type = params.type
+
+    if (type === 'BIND') {
+      request({
+        url: '/admin/social/bind',
+        method: 'post',
+        params: { state, code }
+      }).then(() => {
+        this.$alert('社交账号绑定成功', '成功', {
+          confirmButtonText: '确定',
+          callback: action => {
+            window.close()
+          }
+        })
+      })
+    } else {
+      window.close()
+      window.opener.location.href = `${window.location.origin}/#/login?state=${state}&code=${code}`
+    }
+  }
+}
+</script>
+
+
+<style>
+</style>

+ 127 - 0
src/page/login/codelogin.vue

@@ -0,0 +1,127 @@
+<template>
+  <el-form class="login-form"
+           status-icon
+           :rules="loginRules"
+           ref="loginForm"
+           :model="loginForm"
+           label-width="0">
+    <el-form-item prop="phone">
+      <el-input size="small"
+                @keyup.enter.native="handleLogin"
+                v-model="loginForm.phone"
+                auto-complete="off"
+                placeholder="请输入手机号码">
+        <i slot="prefix"
+           class="icon-shouji"></i>
+      </el-input>
+    </el-form-item>
+    <el-form-item prop="code">
+      <el-input size="small"
+                @keyup.enter.native="handleLogin"
+                v-model="loginForm.code"
+                auto-complete="off"
+                placeholder="请输入验证码">
+        <i slot="prefix"
+           class="icon-yanzhengma"
+           style="margin-top:6px;"></i>
+        <template slot="append">
+          <span @click="handleSend"
+                class="msg-text"
+                :class="[{display:msgKey}]">{{msgText}}</span>
+        </template>
+      </el-input>
+    </el-form-item>
+    <el-form-item>
+      <el-button size="small"
+                 type="primary"
+                 @click.native.prevent="handleLogin"
+                 class="login-submit">登录</el-button>
+    </el-form-item>
+  </el-form>
+</template>
+
+<script>
+const MSGINIT = "发送验证码",
+  // MSGERROR = "验证码发送失败",
+  MSGSCUCCESS = "${time}秒后重发",
+  MSGTIME = 60;
+import { isvalidatemobile } from "@/util/validate";
+import { mapGetters } from "vuex";
+export default {
+  name: "codelogin",
+  data () {
+    const validatePhone = (rule, value, callback) => {
+      if (isvalidatemobile(value)[0]) {
+        callback(new Error(isvalidatemobile(value)[1]));
+      } else {
+        callback();
+      }
+    };
+    const validateCode = (rule, value, callback) => {
+      if (value.length != 4) {
+        callback(new Error("请输入4位数的验证码"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      msgText: MSGINIT,
+      msgTime: MSGTIME,
+      msgKey: false,
+      loginForm: {
+        phone: "17547400800",
+        code: ""
+      },
+      loginRules: {
+        phone: [{ required: true, trigger: "blur", validator: validatePhone }],
+        code: [{ required: true, trigger: "blur", validator: validateCode }]
+      }
+    };
+  },
+  created () { },
+  mounted () { },
+  computed: {
+    ...mapGetters(["tagWel"])
+  },
+  props: [],
+  methods: {
+    handleSend () {
+      if (this.msgKey) return;
+      this.msgText = MSGSCUCCESS.replace("${time}", this.msgTime);
+      this.msgKey = true;
+      const time = setInterval(() => {
+        this.msgTime--;
+        this.msgText = MSGSCUCCESS.replace("${time}", this.msgTime);
+        if (this.msgTime == 0) {
+          this.msgTime = MSGTIME;
+          this.msgText = MSGINIT;
+          this.msgKey = false;
+          clearInterval(time);
+        }
+      }, 1000);
+    },
+    handleLogin () {
+      this.$refs.loginForm.validate(valid => {
+        if (valid) {
+          this.$store.dispatch("LoginByPhone", this.loginForm).then(() => {
+            this.$router.push({ path: this.tagWel.value });
+          });
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style>
+.msg-text {
+  display: block;
+  width: 60px;
+  font-size: 12px;
+  text-align: center;
+  cursor: pointer;
+}
+.msg-text.display {
+  color: #ccc;
+}
+</style>

+ 0 - 0
src/page/login/index.vue


Some files were not shown because too many files changed in this diff