├── .gitignore ├── vue ├── README.md ├── .gitignore ├── config │ ├── prod.env.js │ ├── dev.env.js │ └── index.js ├── src │ ├── assets │ │ ├── images │ │ │ ├── logo.png │ │ │ ├── logo1.png │ │ │ ├── logo2.png │ │ │ ├── logo3.png │ │ │ ├── logo4.png │ │ │ ├── logo5.png │ │ │ ├── logout_16.png │ │ │ ├── logout_24.png │ │ │ └── logout_36.png │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ └── js │ │ │ ├── form_com.js │ │ │ ├── list_com.js │ │ │ ├── global.js │ │ │ └── filter.js │ ├── vuex │ │ ├── getters.js │ │ ├── state.js │ │ ├── store.js │ │ ├── mutations.js │ │ └── actions.js │ ├── components │ │ ├── base │ │ │ ├── 404.vue │ │ │ ├── refresh.vue │ │ │ ├── changePwd.vue │ │ │ └── login.vue │ │ ├── configs │ │ │ ├── preview.vue │ │ │ └── add.vue │ │ ├── common │ │ │ ├── listStatus.vue │ │ │ ├── pagination.vue │ │ │ ├── leftMenu.vue │ │ │ ├── listActions.vue │ │ │ └── btnGroup.vue │ │ ├── posts │ │ │ ├── add.vue │ │ │ ├── list.vue │ │ │ └── edit.vue │ │ ├── menus │ │ │ ├── rule.vue │ │ │ ├── list.vue │ │ │ ├── add.vue │ │ │ └── edit.vue │ │ ├── groups │ │ │ └── list.vue │ │ ├── rules │ │ │ ├── list.vue │ │ │ ├── add.vue │ │ │ └── edit.vue │ │ ├── structures │ │ │ ├── list.vue │ │ │ ├── add.vue │ │ │ └── edit.vue │ │ └── users │ │ │ ├── list.vue │ │ │ └── add.vue │ ├── App.vue │ ├── main.js │ └── routes.js ├── .babelrc ├── index.html ├── build │ ├── dev-client.js │ ├── build.js │ ├── webpack.dev.conf.js │ ├── check-versions.js │ ├── utils.js │ ├── dev-server.js │ ├── webpack.base.conf.js │ └── webpack.prod.conf.js ├── .eslintrc.json └── package.json ├── hyperf ├── README.md ├── app │ ├── Kernel │ │ ├── Verify │ │ │ └── assets │ │ │ │ ├── bgs │ │ │ │ ├── 1.jpg │ │ │ │ ├── 2.jpg │ │ │ │ ├── 3.jpg │ │ │ │ ├── 4.jpg │ │ │ │ ├── 5.jpg │ │ │ │ ├── 6.jpg │ │ │ │ ├── 7.jpg │ │ │ │ └── 8.jpg │ │ │ │ ├── ttfs │ │ │ │ ├── 1.ttf │ │ │ │ ├── 2.ttf │ │ │ │ ├── 3.ttf │ │ │ │ ├── 4.ttf │ │ │ │ ├── 5.ttf │ │ │ │ └── 6.ttf │ │ │ │ └── zhttfs │ │ │ │ └── 1.ttf │ │ ├── Log │ │ │ └── LoggerFactory.php │ │ └── Http │ │ │ ├── Response.php │ │ │ └── Router.php │ ├── Event │ │ └── UserPermissionChanged.php │ ├── Model │ │ ├── Model.php │ │ ├── AdminAccess.php │ │ ├── AdminPost.php │ │ ├── AdminStructure.php │ │ ├── AdminGroup.php │ │ ├── AdminRule.php │ │ ├── SystemConfig.php │ │ ├── AdminMenu.php │ │ └── AdminUser.php │ ├── Service │ │ ├── StructureService.php │ │ ├── GroupService.php │ │ ├── PostService.php │ │ ├── RuleService.php │ │ ├── MenuService.php │ │ └── SystemConfigService.php │ ├── Request │ │ ├── PostRequest.php │ │ ├── DeletesRequest.php │ │ ├── StructureRequest.php │ │ ├── GroupRequest.php │ │ ├── EnablesRequest.php │ │ ├── LoginRequest.php │ │ ├── ChangePwdRequest.php │ │ ├── RuleRequest.php │ │ ├── MenuRequest.php │ │ ├── UserUpdateRequest.php │ │ └── UserStoreRequest.php │ ├── Controller │ │ ├── SystemConfigsController.php │ │ ├── IndexController.php │ │ ├── AbstractController.php │ │ ├── MenusController.php │ │ ├── GroupsController.php │ │ ├── StructuresController.php │ │ ├── RulesController.php │ │ ├── PostsController.php │ │ ├── UsersController.php │ │ └── BaseController.php │ ├── Listener │ │ ├── FetchModeListener.php │ │ ├── DbQueryExecutedListener.php │ │ └── FlushUserOwnRulesListener.php │ ├── Exception │ │ ├── BusinessException.php │ │ └── Handler │ │ │ ├── AppExceptionHandler.php │ │ │ └── BusinessExceptionHandler.php │ ├── Middleware │ │ ├── ConfigMiddleware.php │ │ ├── CorsMiddleware.php │ │ └── PermissionMiddleware.php │ └── Constants │ │ └── ErrorCode.php ├── .gitignore ├── config │ ├── autoload │ │ ├── translation.php │ │ ├── aspects.php │ │ ├── commands.php │ │ ├── processes.php │ │ ├── listeners.php │ │ ├── dependencies.php │ │ ├── annotations.php │ │ ├── middlewares.php │ │ ├── exceptions.php │ │ ├── cache.php │ │ ├── redis.php │ │ ├── logger.php │ │ ├── devtool.php │ │ ├── databases.php │ │ └── server.php │ ├── container.php │ ├── config.php │ └── routes.php ├── .phpstorm.meta.php ├── phpstan.neon ├── .env.example ├── test │ ├── Cases │ │ └── ExampleTest.php │ ├── bootstrap.php │ └── HttpTestCase.php ├── deploy.test.yml ├── phpunit.xml ├── bin │ └── hyperf.php ├── .gitlab-ci.yml ├── Dockerfile ├── .php_cs └── composer.json ├── .DS_Store ├── doc └── img │ ├── .DS_Store │ ├── demo_auth.jpg │ └── demo_menu.jpg ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /vue/README.md: -------------------------------------------------------------------------------- 1 | # HyperVue-vue -------------------------------------------------------------------------------- /hyperf/README.md: -------------------------------------------------------------------------------- 1 | # HyperVue-hyperf -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/.DS_Store -------------------------------------------------------------------------------- /vue/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | yarn.lock 4 | dist -------------------------------------------------------------------------------- /vue/config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /doc/img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/doc/img/.DS_Store -------------------------------------------------------------------------------- /doc/img/demo_auth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/doc/img/demo_auth.jpg -------------------------------------------------------------------------------- /doc/img/demo_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/doc/img/demo_menu.jpg -------------------------------------------------------------------------------- /vue/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logo.png -------------------------------------------------------------------------------- /vue/src/assets/images/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logo1.png -------------------------------------------------------------------------------- /vue/src/assets/images/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logo2.png -------------------------------------------------------------------------------- /vue/src/assets/images/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logo3.png -------------------------------------------------------------------------------- /vue/src/assets/images/logo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logo4.png -------------------------------------------------------------------------------- /vue/src/assets/images/logo5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logo5.png -------------------------------------------------------------------------------- /vue/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-3"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /vue/src/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /vue/src/assets/images/logout_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logout_16.png -------------------------------------------------------------------------------- /vue/src/assets/images/logout_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logout_24.png -------------------------------------------------------------------------------- /vue/src/assets/images/logout_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/images/logout_36.png -------------------------------------------------------------------------------- /vue/src/vuex/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | // getCount: state => state.count, 3 | } 4 | 5 | export default getters 6 | -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/1.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/2.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/3.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/4.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/5.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/6.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/7.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/bgs/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/bgs/8.jpg -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/ttfs/1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/ttfs/1.ttf -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/ttfs/2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/ttfs/2.ttf -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/ttfs/3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/ttfs/3.ttf -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/ttfs/4.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/ttfs/4.ttf -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/ttfs/5.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/ttfs/5.ttf -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/ttfs/6.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/ttfs/6.ttf -------------------------------------------------------------------------------- /hyperf/app/Kernel/Verify/assets/zhttfs/1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/hyperf/app/Kernel/Verify/assets/zhttfs/1.ttf -------------------------------------------------------------------------------- /vue/src/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /vue/src/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /vue/src/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /vue/src/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WarnerYang/HyperVue/HEAD/vue/src/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /vue/config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /hyperf/.gitignore: -------------------------------------------------------------------------------- 1 | .buildpath 2 | .settings/ 3 | .project 4 | *.patch 5 | .idea/ 6 | .git/ 7 | runtime/ 8 | vendor/ 9 | .phpintel/ 10 | .env 11 | .DS_Store 12 | *.lock 13 | .phpunit* -------------------------------------------------------------------------------- /hyperf/config/autoload/translation.php: -------------------------------------------------------------------------------- 1 | 'zh_CN', 6 | 'fallback_locale' => '', 7 | 'path' => BASE_PATH . '/storage/languages', 8 | ]; -------------------------------------------------------------------------------- /vue/src/assets/js/form_com.js: -------------------------------------------------------------------------------- 1 | const formMixin = { 2 | methods: { 3 | goback() { 4 | router.go(-1) 5 | }, 6 | go(num) { 7 | router.go(num) 8 | } 9 | } 10 | } 11 | 12 | export default formMixin 13 | -------------------------------------------------------------------------------- /vue/src/vuex/state.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | showLeftMenu: true, 3 | globalLoading: true, 4 | menus: [], 5 | rules: [], 6 | users: {}, 7 | userGroups: [], 8 | organizes: [] 9 | } 10 | 11 | export default state 12 | -------------------------------------------------------------------------------- /vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | loading... 7 | 8 | 9 |
10 | 11 |
12 | 13 | -------------------------------------------------------------------------------- /hyperf/.phpstorm.meta.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |

页面未找到 404

4 | 登录 5 | 返回 6 |
7 | 8 | 9 | -------------------------------------------------------------------------------- /hyperf/config/autoload/aspects.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | -------------------------------------------------------------------------------- /vue/src/components/configs/preview.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /hyperf/config/autoload/listeners.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /hyperf/config/autoload/dependencies.php: -------------------------------------------------------------------------------- 1 | App\Kernel\Log\LoggerFactory::class, 15 | ]; 16 | -------------------------------------------------------------------------------- /vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /hyperf/app/Event/UserPermissionChanged.php: -------------------------------------------------------------------------------- 1 | model = $model; 22 | $this->uid = $uid; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hyperf/app/Model/Model.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'paths' => [ 16 | BASE_PATH . '/app', 17 | ], 18 | 'ignore_annotations' => [ 19 | 'mixin', 20 | ], 21 | ], 22 | ]; 23 | -------------------------------------------------------------------------------- /hyperf/config/autoload/middlewares.php: -------------------------------------------------------------------------------- 1 | [ 15 | \App\Middleware\CorsMiddleware::class, 16 | \App\Middleware\ConfigMiddleware::class, 17 | \Hyperf\Validation\Middleware\ValidationMiddleware::class, 18 | ], 19 | ]; 20 | -------------------------------------------------------------------------------- /hyperf/config/autoload/exceptions.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'http' => [ 16 | // App\Exception\Handler\AppExceptionHandler::class, 17 | App\Exception\Handler\BusinessExceptionHandler::class, 18 | ], 19 | ], 20 | ]; 21 | -------------------------------------------------------------------------------- /hyperf/app/Service/StructureService.php: -------------------------------------------------------------------------------- 1 | model, ['id', 'pid', 'name', 'title']); 22 | $data = $cat->getList([], 0, 'id'); 23 | return $data; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hyperf/config/autoload/cache.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'driver' => Hyperf\Cache\Driver\RedisDriver::class, 16 | // 'driver' => Hyperf\Cache\Driver\FileSystemDriver::class, 17 | 'packer' => Hyperf\Utils\Packer\PhpSerializerPacker::class, 18 | 'prefix' => 'hyperVue:', 19 | ], 20 | ]; 21 | -------------------------------------------------------------------------------- /vue/src/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | const mutations = { 2 | showLeftMenu(state, status) { 3 | state.showLeftMenu = status 4 | }, 5 | showLoading(state, status) { 6 | state.globalLoading = status 7 | }, 8 | setMenus(state, menus) { 9 | state.menus = menus 10 | }, 11 | setRules(state, rules) { 12 | state.rules = rules 13 | }, 14 | setUsers(state, users) { 15 | state.users = users 16 | }, 17 | setUserGroups(state, userGroups) { 18 | state.userGroups = userGroups 19 | }, 20 | setOrganizes(state, organizes) { 21 | state.organizes = organizes 22 | } 23 | } 24 | 25 | export default mutations 26 | -------------------------------------------------------------------------------- /hyperf/app/Request/PostRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hyperf/app/Controller/SystemConfigsController.php: -------------------------------------------------------------------------------- 1 | validated(); 21 | $params = $this->request->all(); 22 | $data = $this->service->createData($params); 23 | return success(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hyperf/app/Request/DeletesRequest.php: -------------------------------------------------------------------------------- 1 | 'required|array', 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hyperf/app/Request/StructureRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hyperf/app/Kernel/Log/LoggerFactory.php: -------------------------------------------------------------------------------- 1 | get(HyperfLoggerFactory::class)->make(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hyperf/app/Request/GroupRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | 'rules' => 'required', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hyperf/test/Cases/ExampleTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 26 | $this->assertTrue(is_array($this->get('/'))); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hyperf/app/Request/EnablesRequest.php: -------------------------------------------------------------------------------- 1 | 'required|array', 26 | 'status' => 'required|boolean', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hyperf/app/Request/LoginRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | 'password' => 'required|between:6,12', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vue/src/vuex/actions.js: -------------------------------------------------------------------------------- 1 | const actions = { 2 | showLeftMenu ({ commit }, status) { 3 | commit('showLeftMenu', status) 4 | }, 5 | showLoading ({ commit }, status) { 6 | commit('showLoading', status) 7 | }, 8 | setMenus({ commit }, menus) { 9 | commit('setMenus', menus) 10 | }, 11 | setRules({ commit }, rules) { 12 | commit('setRules', rules) 13 | }, 14 | setUsers({ commit }, users) { 15 | commit('setUsers', users) 16 | }, 17 | setUserGroups({ commit }, userGroups) { 18 | commit('setUserGroups', userGroups) 19 | }, 20 | setOrganizes({ commit }, organizes) { 21 | commit('setOrganizes', organizes) 22 | } 23 | } 24 | 25 | export default actions 26 | -------------------------------------------------------------------------------- /hyperf/app/Service/GroupService.php: -------------------------------------------------------------------------------- 1 | model, ['id', 'pid', 'title', 'title']); 26 | $data = $cat->getList([], 0, 'id'); 27 | return $data; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hyperf/app/Request/ChangePwdRequest.php: -------------------------------------------------------------------------------- 1 | 'required|between:6,12', 26 | 'new_pwd' => 'required|between:6,12', 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /hyperf/app/Request/RuleRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | 'name' => 'required', 27 | 'level' => 'required', 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /hyperf/app/Request/MenuRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | 'menu_type' => 'required', 27 | 'rule_id' => 'required', 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /hyperf/app/Controller/IndexController.php: -------------------------------------------------------------------------------- 1 | request->input('user', 'Hyperf'); 20 | $method = $this->request->getMethod(); 21 | 22 | return [ 23 | 'method' => $method, 24 | 'message' => "Hello {$user}.", 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hyperf/deploy.test.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | hyperf: 4 | image: $REGISTRY_URL/$PROJECT_NAME:test 5 | environment: 6 | - "APP_PROJECT=hyperf" 7 | - "APP_ENV=test" 8 | ports: 9 | - 9501:9501 10 | deploy: 11 | replicas: 1 12 | restart_policy: 13 | condition: on-failure 14 | delay: 5s 15 | max_attempts: 5 16 | update_config: 17 | parallelism: 2 18 | delay: 5s 19 | order: start-first 20 | networks: 21 | - hyperf_net 22 | configs: 23 | - source: hyperf_v1.0 24 | target: /opt/www/.env 25 | configs: 26 | hyperf_v1.0: 27 | external: true 28 | networks: 29 | hyperf_net: 30 | external: true 31 | -------------------------------------------------------------------------------- /hyperf/app/Listener/FetchModeListener.php: -------------------------------------------------------------------------------- 1 | statement->setFetchMode(PDO::FETCH_ASSOC); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vue/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "vue", 4 | "env": { 5 | "browser": true, 6 | "es6": true 7 | }, 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "linebreak-style": 0, // 换行风格 13 | "quotes": 0, // 引号类型 14 | "semi": 0, // 分号作为语句结尾 15 | "eqeqeq": 0, // 强制使用 '===' 和 '!==' 来做判断比较 16 | "no-unused-vars": 0, // 强制声明未使用变量 17 | "space-before-function-paren": 0, // 函数名后的空格 18 | "prefer-const": 0, // 首选const 19 | "no-undef": 0, // 不能使用未定义变量 20 | "camelcase": 0, 21 | "object-curly-spacing": 0, 22 | "indent": 0, // 缩进 23 | "eol-last": 0 24 | } 25 | } -------------------------------------------------------------------------------- /hyperf/app/Request/UserUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 26 | 'realname' => 'required', 27 | 'groups' => 'required', 28 | 'password' => 'between:6,12' 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hyperf/app/Service/PostService.php: -------------------------------------------------------------------------------- 1 | model; 25 | if ($keywords) { 26 | $keywords = rtrim(ltrim($keywords)); 27 | $query = $query->where('name', 'like', "%{$keywords}%"); 28 | } 29 | $data = $query->get(); 30 | return $data; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /hyperf/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./test 14 | 15 | 16 | 17 | 18 | ./app 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /hyperf/app/Request/UserStoreRequest.php: -------------------------------------------------------------------------------- 1 | 'required|unique:admin_user', 26 | 'realname' => 'required', 27 | 'groups' => 'required', 28 | 'password' => 'required|between:6,12' 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hyperf/bin/hyperf.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | get(\Hyperf\Contract\ApplicationInterface::class); 21 | $application->run(); 22 | })(); 23 | -------------------------------------------------------------------------------- /hyperf/app/Model/AdminAccess.php: -------------------------------------------------------------------------------- 1 | 'integer', 'user_id' => 'integer', 'group_id' => 'integer']; 32 | } -------------------------------------------------------------------------------- /hyperf/app/Exception/BusinessException.php: -------------------------------------------------------------------------------- 1 | get(Hyperf\Contract\ApplicationInterface::class); 29 | -------------------------------------------------------------------------------- /hyperf/config/autoload/redis.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'host' => env('REDIS_HOST', 'localhost'), 16 | 'auth' => env('REDIS_AUTH', null), 17 | 'port' => (int) env('REDIS_PORT', 6379), 18 | 'db' => (int) env('REDIS_DB', 0), 19 | 'pool' => [ 20 | 'min_connections' => 1, 21 | 'max_connections' => 10, 22 | 'connect_timeout' => 10.0, 23 | 'wait_timeout' => 3.0, 24 | 'heartbeat' => -1, 25 | 'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60), 26 | ], 27 | ], 28 | ]; 29 | -------------------------------------------------------------------------------- /hyperf/config/autoload/logger.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'handler' => [ 16 | 'class' => Monolog\Handler\StreamHandler::class, 17 | 'constructor' => [ 18 | 'stream' => BASE_PATH . '/runtime/logs/hyperf.log', 19 | 'level' => Monolog\Logger::DEBUG, 20 | ], 21 | ], 22 | 'formatter' => [ 23 | 'class' => Monolog\Formatter\LineFormatter::class, 24 | 'constructor' => [ 25 | 'format' => null, 26 | 'dateFormat' => null, 27 | 'allowInlineLineBreaks' => true, 28 | ], 29 | ], 30 | ], 31 | ]; 32 | -------------------------------------------------------------------------------- /hyperf/config/config.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'skeleton'), 18 | 'app_secret' => env('APP_SECRET', 'Tn2YbxQ37PFUBMd6'), 19 | 'logged_info_cache_key' => env('LOGGED_INFO_CACHE_KEY', 'loggedInfo:'), 20 | StdoutLoggerInterface::class => [ 21 | 'log_level' => [ 22 | LogLevel::ALERT, 23 | LogLevel::CRITICAL, 24 | // LogLevel::DEBUG, 25 | LogLevel::EMERGENCY, 26 | LogLevel::ERROR, 27 | LogLevel::INFO, 28 | LogLevel::NOTICE, 29 | LogLevel::WARNING, 30 | ], 31 | ], 32 | ]; 33 | -------------------------------------------------------------------------------- /vue/src/assets/js/list_com.js: -------------------------------------------------------------------------------- 1 | const listMixin = { 2 | data() { 3 | return { 4 | currentPage: null, // 分页当前页 5 | keywords: '', // 关键字搜索 6 | multipleSelection: [], // 列表当前已勾选项 7 | limit: 15, // 每页数据数目 8 | dataCount: 0 9 | } 10 | }, 11 | methods: { 12 | selectItem(val) { 13 | this.multipleSelection = val 14 | }, 15 | getCurrentPage() { 16 | let data = this.$route.query 17 | if (data) { 18 | if (data.page) { 19 | this.currentPage = parseInt(data.page) 20 | } else { 21 | this.currentPage = 1 22 | } 23 | } 24 | }, 25 | getKeywords() { 26 | let data = this.$route.query 27 | if (data) { 28 | if (data.keywords) { 29 | this.keywords = data.keywords 30 | } else { 31 | this.keywords = '' 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | export default listMixin 39 | -------------------------------------------------------------------------------- /hyperf/app/Model/AdminPost.php: -------------------------------------------------------------------------------- 1 | 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 35 | } -------------------------------------------------------------------------------- /hyperf/app/Model/AdminStructure.php: -------------------------------------------------------------------------------- 1 | 'integer', 'pid' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 35 | } -------------------------------------------------------------------------------- /hyperf/app/Controller/AbstractController.php: -------------------------------------------------------------------------------- 1 | 'integer', 'pid' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 37 | } -------------------------------------------------------------------------------- /hyperf/app/Service/RuleService.php: -------------------------------------------------------------------------------- 1 | model, ['id', 'pid', 'title', 'title']); 35 | $data = $cat->getList([], 0, 'id'); 36 | if ($type == 'tree') { 37 | foreach ($data as $k => $v) { 38 | $data[$k]['check'] = false; 39 | } 40 | $data = $this->tree->list_to_tree($data, 'id', 'pid', 'child', 0, true, ['pid']); 41 | } 42 | return $data; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /hyperf/app/Model/AdminRule.php: -------------------------------------------------------------------------------- 1 | 'integer', 'level' => 'integer', 'pid' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 37 | } -------------------------------------------------------------------------------- /hyperf/app/Model/SystemConfig.php: -------------------------------------------------------------------------------- 1 | 'integer', 'group' => 'integer', 'need_auth' => 'integer']; 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 WarnerYang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hyperf/app/Middleware/ConfigMiddleware.php: -------------------------------------------------------------------------------- 1 | systemConfigService->getDataList(); 32 | if (is_array($data)) { 33 | foreach ($data as $key => $value) { 34 | $this->config->set($key, $value); 35 | } 36 | } 37 | return $handler->handle($request); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hyperf/test/HttpTestCase.php: -------------------------------------------------------------------------------- 1 | client = make(Client::class); 36 | } 37 | 38 | public function __call($name, $arguments) 39 | { 40 | return $this->client->{$name}(...$arguments); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /hyperf/app/Middleware/CorsMiddleware.php: -------------------------------------------------------------------------------- 1 | withHeader('Access-Control-Allow-Origin', env('APP_ALLOW_ORIGIN', '*')) 20 | ->withHeader('Access-Control-Allow-Credentials', 'true') 21 | ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') 22 | ->withHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, authKey, sessionId'); 23 | Context::set(ResponseInterface::class, $response); 24 | 25 | if ($request->getMethod() == 'OPTIONS') { 26 | return $response; 27 | } 28 | 29 | return $handler->handle($request); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hyperf/app/Model/AdminMenu.php: -------------------------------------------------------------------------------- 1 | 'integer', 'pid' => 'integer', 'menu_type' => 'integer', 'sort' => 'integer', 'status' => 'integer', 'rule_id' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 40 | } -------------------------------------------------------------------------------- /hyperf/config/autoload/devtool.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'amqp' => [ 16 | 'consumer' => [ 17 | 'namespace' => 'App\\Amqp\\Consumer', 18 | ], 19 | 'producer' => [ 20 | 'namespace' => 'App\\Amqp\\Producer', 21 | ], 22 | ], 23 | 'aspect' => [ 24 | 'namespace' => 'App\\Aspect', 25 | ], 26 | 'command' => [ 27 | 'namespace' => 'App\\Command', 28 | ], 29 | 'controller' => [ 30 | 'namespace' => 'App\\Controller', 31 | ], 32 | 'job' => [ 33 | 'namespace' => 'App\\Job', 34 | ], 35 | 'listener' => [ 36 | 'namespace' => 'App\\Listener', 37 | ], 38 | 'middleware' => [ 39 | 'namespace' => 'App\\Middleware', 40 | ], 41 | 'Process' => [ 42 | 'namespace' => 'App\\Processes', 43 | ], 44 | ], 45 | ]; 46 | -------------------------------------------------------------------------------- /vue/config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8888, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vue/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var webpack = require('webpack') 3 | var merge = require('webpack-merge') 4 | var utils = require('./utils') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | 8 | // add hot-reload related code to entry chunks 9 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 10 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 11 | }) 12 | 13 | module.exports = merge(baseWebpackConfig, { 14 | module: { 15 | loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 16 | }, 17 | // eval-source-map is faster for development 18 | devtool: '#eval-source-map', 19 | plugins: [ 20 | new webpack.DefinePlugin({ 21 | 'process.env': config.dev.env 22 | }), 23 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 24 | new webpack.optimize.OccurenceOrderPlugin(), 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }) 33 | ] 34 | }) 35 | -------------------------------------------------------------------------------- /hyperf/app/Service/MenuService.php: -------------------------------------------------------------------------------- 1 | model, ['id', 'pid', 'title', 'title']); 35 | $data = $cat->getList([], 0, 'sort'); 36 | return $data; 37 | } 38 | 39 | public function getDataById($id) 40 | { 41 | $result = $this->model 42 | ->leftJoin('admin_rule as rule', 'admin_menu.rule_id', '=', 'rule.id') 43 | ->select('admin_menu.*', 'rule.title as rule_name') 44 | ->find($id); 45 | if (!$result) { 46 | throw new BusinessException(ErrorCode::RECORD_NOT_EXIST); 47 | } 48 | return $result; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vue/build/check-versions.js: -------------------------------------------------------------------------------- 1 | var semver = require('semver') 2 | var chalk = require('chalk') 3 | var packageConfig = require('../package.json') 4 | var exec = function (cmd) { 5 | return require('child_process') 6 | .execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | }, 15 | { 16 | name: 'npm', 17 | currentVersion: exec('npm --version'), 18 | versionRequirement: packageConfig.engines.npm 19 | } 20 | ] 21 | 22 | module.exports = function () { 23 | var warnings = [] 24 | for (var i = 0; i < versionRequirements.length; i++) { 25 | var mod = versionRequirements[i] 26 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 27 | warnings.push(mod.name + ': ' + 28 | chalk.red(mod.currentVersion) + ' should be ' + 29 | chalk.green(mod.versionRequirement) 30 | ) 31 | } 32 | } 33 | 34 | if (warnings.length) { 35 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 36 | for (var i = 0; i < warnings.length; i++) { 37 | var warning = warnings[i] 38 | console.log(' ' + warning) 39 | } 40 | process.exit(1) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /hyperf/config/autoload/databases.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'driver' => env('DB_DRIVER', 'mysql'), 16 | 'host' => env('DB_HOST', 'localhost'), 17 | 'database' => env('DB_DATABASE', 'hyperf'), 18 | 'port' => env('DB_PORT', 3306), 19 | 'username' => env('DB_USERNAME', 'root'), 20 | 'password' => env('DB_PASSWORD', ''), 21 | 'charset' => env('DB_CHARSET', 'utf8'), 22 | 'collation' => env('DB_COLLATION', 'utf8_unicode_ci'), 23 | 'prefix' => env('DB_PREFIX', ''), 24 | 'pool' => [ 25 | 'min_connections' => 1, 26 | 'max_connections' => 10, 27 | 'connect_timeout' => 10.0, 28 | 'wait_timeout' => 3.0, 29 | 'heartbeat' => -1, 30 | 'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60), 31 | ], 32 | 'commands' => [ 33 | 'db:model' => [ 34 | 'path' => 'app/Model', 35 | 'force_casts' => true, 36 | 'inheritance' => 'Model', 37 | ], 38 | ], 39 | ], 40 | ]; 41 | -------------------------------------------------------------------------------- /hyperf/app/Exception/Handler/AppExceptionHandler.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 31 | } 32 | 33 | public function handle(Throwable $throwable, ResponseInterface $response) 34 | { 35 | $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile())); 36 | $this->logger->error($throwable->getTraceAsString()); 37 | return $response->withStatus(500)->withBody(new SwooleStream('Internal Server Error.')); 38 | } 39 | 40 | public function isValid(Throwable $throwable): bool 41 | { 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /hyperf/app/Model/AdminUser.php: -------------------------------------------------------------------------------- 1 | 'integer', 'structure_id' => 'integer', 'post_id' => 'integer', 'status' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime']; 40 | 41 | public function groups() 42 | { 43 | return $this->belongsToMany(AdminGroup::class, 'admin_access', 'user_id', 'group_id'); 44 | } 45 | } -------------------------------------------------------------------------------- /vue/src/assets/js/global.js: -------------------------------------------------------------------------------- 1 | const commonFn = { 2 | j2s(obj) { 3 | return JSON.stringify(obj) 4 | }, 5 | shallowRefresh(name, query) { 6 | router.replace({ path: '/refresh', query: { name: name, query: query } }) 7 | }, 8 | closeGlobalLoading() { 9 | setTimeout(() => { 10 | store.dispatch('showLoading', false) 11 | }) 12 | }, 13 | openGlobalLoading() { 14 | setTimeout(() => { 15 | store.dispatch('showLoading', true) 16 | }) 17 | }, 18 | cloneJson(obj) { 19 | return JSON.parse(JSON.stringify(obj)) 20 | }, 21 | toastMsg(type, msg) { 22 | switch (type) { 23 | case 'normal': 24 | bus.$message(msg) 25 | break 26 | case 'success': 27 | bus.$message({ 28 | message: msg, 29 | type: 'success' 30 | }) 31 | break 32 | case 'warning': 33 | bus.$message({ 34 | message: msg, 35 | type: 'warning' 36 | }) 37 | break 38 | case 'error': 39 | bus.$message.error(msg) 40 | break 41 | } 42 | }, 43 | clearVuex(cate) { 44 | store.dispatch(cate, []) 45 | }, 46 | getHasRule(val) { 47 | const userInfo = Lockr.get('userInfo') 48 | if (userInfo.id == 1) { 49 | return true 50 | } else { 51 | const authList = Lockr.get('authList') 52 | return _.includes(authList, val) 53 | } 54 | } 55 | } 56 | 57 | export default commonFn 58 | -------------------------------------------------------------------------------- /hyperf/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # usermod -aG docker gitlab-runner 2 | 3 | stages: 4 | - build 5 | - deploy 6 | 7 | variables: 8 | PROJECT_NAME: hyperf 9 | REGISTRY_URL: registry-docker.org 10 | 11 | build_test_docker: 12 | stage: build 13 | before_script: 14 | # - git submodule sync --recursive 15 | # - git submodule update --init --recursive 16 | script: 17 | - docker build . -t $PROJECT_NAME 18 | - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:test 19 | - docker push $REGISTRY_URL/$PROJECT_NAME:test 20 | only: 21 | - test 22 | tags: 23 | - builder 24 | 25 | deploy_test_docker: 26 | stage: deploy 27 | script: 28 | - docker stack deploy -c deploy.test.yml --with-registry-auth $PROJECT_NAME 29 | only: 30 | - test 31 | tags: 32 | - test 33 | 34 | build_docker: 35 | stage: build 36 | before_script: 37 | # - git submodule sync --recursive 38 | # - git submodule update --init --recursive 39 | script: 40 | - docker build . -t $PROJECT_NAME 41 | - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME 42 | - docker tag $PROJECT_NAME $REGISTRY_URL/$PROJECT_NAME:latest 43 | - docker push $REGISTRY_URL/$PROJECT_NAME:$CI_COMMIT_REF_NAME 44 | - docker push $REGISTRY_URL/$PROJECT_NAME:latest 45 | only: 46 | - tags 47 | tags: 48 | - builder 49 | 50 | deploy_docker: 51 | stage: deploy 52 | script: 53 | - echo SUCCESS 54 | only: 55 | - tags 56 | tags: 57 | - builder 58 | -------------------------------------------------------------------------------- /vue/src/components/common/pagination.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 59 | -------------------------------------------------------------------------------- /hyperf/app/Service/SystemConfigService.php: -------------------------------------------------------------------------------- 1 | model->get(); 31 | if ($systemConfig) { 32 | foreach ($systemConfig as $config) { 33 | $data[$config['key']] = $config['value']; 34 | } 35 | } 36 | 37 | return $data; 38 | } 39 | 40 | /** 41 | * @CacheEvict(prefix="systemConfig", value="") 42 | */ 43 | public function createData($params) 44 | { 45 | Db::beginTransaction(); 46 | try { 47 | foreach ($params as $key => $value) { 48 | $this->model->where('key', $key)->update(['value' => $value]); 49 | } 50 | Db::commit(); 51 | return true; 52 | } catch (\Throwable $th) { 53 | Db::rollback(); 54 | throw new BusinessException(ErrorCode::UPDATE_ERROR, $th->getMessage()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /hyperf/config/autoload/server.php: -------------------------------------------------------------------------------- 1 | SWOOLE_PROCESS, 18 | 'servers' => [ 19 | [ 20 | 'name' => 'http', 21 | 'type' => Server::SERVER_HTTP, 22 | 'host' => '0.0.0.0', 23 | 'port' => 9501, 24 | 'sock_type' => SWOOLE_SOCK_TCP, 25 | 'callbacks' => [ 26 | SwooleEvent::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'], 27 | ], 28 | ], 29 | ], 30 | 'settings' => [ 31 | 'enable_coroutine' => true, 32 | 'worker_num' => swoole_cpu_num(), 33 | 'pid_file' => BASE_PATH . '/runtime/hyperf.pid', 34 | 'open_tcp_nodelay' => true, 35 | 'max_coroutine' => 100000, 36 | 'open_http2_protocol' => true, 37 | 'max_request' => 100000, 38 | 'socket_buffer_size' => 2 * 1024 * 1024, 39 | ], 40 | 'callbacks' => [ 41 | SwooleEvent::ON_BEFORE_START => [Hyperf\Framework\Bootstrap\ServerStartCallback::class, 'beforeStart'], 42 | SwooleEvent::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'], 43 | SwooleEvent::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'], 44 | ], 45 | ]; 46 | -------------------------------------------------------------------------------- /hyperf/app/Listener/DbQueryExecutedListener.php: -------------------------------------------------------------------------------- 1 | logger = $container->get(LoggerFactory::class)->get('sql'); 37 | } 38 | 39 | public function listen(): array 40 | { 41 | return [ 42 | QueryExecuted::class, 43 | ]; 44 | } 45 | 46 | /** 47 | * @param QueryExecuted $event 48 | */ 49 | public function process(object $event) 50 | { 51 | if ($event instanceof QueryExecuted) { 52 | $sql = $event->sql; 53 | if (! Arr::isAssoc($event->bindings)) { 54 | foreach ($event->bindings as $key => $value) { 55 | $sql = Str::replaceFirst('?', "'{$value}'", $sql); 56 | } 57 | } 58 | 59 | $this->logger->info(sprintf('[%s] %s', $event->time, $sql)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /hyperf/app/Listener/FlushUserOwnRulesListener.php: -------------------------------------------------------------------------------- 1 | uid; 40 | $model = $event->model; 41 | 42 | // 变更用户信息 清理相应用户拥有的权限列表缓存 43 | if ($model instanceof AdminUser) { 44 | $uid = $event->uid; 45 | if (!is_array($uid)) $uid = [$uid]; 46 | } 47 | 48 | // 变更用户组或者权限 清理所有用户拥有的权限列表缓存 49 | if ($model instanceof AdminGroup || $model instanceof AdminRule) { 50 | $uid = $userService->getOrSetUserOwnRulesIds(); 51 | } 52 | 53 | foreach ($uid as $id) { 54 | $eventDispatcher->dispatch(new DeleteListenerEvent('getUserOwnRulesUpdate', [$id])); 55 | } 56 | 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /vue/src/main.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import Vue from 'vue' 3 | import App from './App' 4 | import axios from 'axios' 5 | import Lockr from 'lockr' 6 | import Cookies from 'js-cookie' 7 | import _ from 'lodash' 8 | import moment from 'moment' 9 | import ElementUI from 'element-ui' 10 | import 'element-ui/lib/theme-chalk/index.css' 11 | import routes from './routes' 12 | import VueRouter from 'vue-router' 13 | import store from './vuex/store' 14 | import filter from './assets/js/filter' 15 | import _g from './assets/js/global' 16 | import NProgress from 'nprogress' 17 | import 'nprogress/nprogress.css' 18 | import 'assets/css/global.css' 19 | import 'assets/css/base.css' 20 | 21 | axios.defaults.baseURL = HOST 22 | axios.defaults.timeout = 1000 * 15 23 | axios.defaults.headers.authKey = Lockr.get('authKey') || '' 24 | axios.defaults.headers.sessionId = Lockr.get('sessionId') || '' 25 | axios.defaults.headers['Content-Type'] = 'application/json' 26 | 27 | const router = new VueRouter({ 28 | mode: 'hash', 29 | base: __dirname, 30 | routes 31 | }) 32 | 33 | router.beforeEach((to, from, next) => { 34 | store.dispatch('showLoading', true) 35 | NProgress.start() 36 | next() 37 | }) 38 | 39 | router.afterEach(transition => { 40 | NProgress.done() 41 | }) 42 | 43 | Vue.use(ElementUI) 44 | Vue.use(VueRouter) 45 | 46 | window.router = router 47 | window.store = store 48 | window.HOST = HOST 49 | window.axios = axios 50 | window._ = _ 51 | window.moment = moment 52 | window.Lockr = Lockr 53 | window.Cookies = Cookies 54 | window._g = _g 55 | window.pageSize = 15 56 | 57 | const bus = new Vue() 58 | window.bus = bus 59 | 60 | new Vue({ 61 | el: '#app', 62 | template: '', 63 | filters: filter, 64 | router, 65 | store, 66 | components: { App } 67 | // render: h => h(Login) 68 | }).$mount('#app') 69 | -------------------------------------------------------------------------------- /hyperf/Dockerfile: -------------------------------------------------------------------------------- 1 | # Default Dockerfile 2 | # 3 | # @link https://www.hyperf.io 4 | # @document https://doc.hyperf.io 5 | # @contact group@hyperf.io 6 | # @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE 7 | 8 | FROM hyperf/hyperf:7.2-alpine-cli 9 | LABEL maintainer="Hyperf Developers " version="1.0" license="MIT" 10 | 11 | ## 12 | # ---------- env settings ---------- 13 | ## 14 | # --build-arg timezone=Asia/Shanghai 15 | ARG timezone 16 | 17 | ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \ 18 | COMPOSER_VERSION=1.8.6 \ 19 | APP_ENV=prod 20 | 21 | # update 22 | RUN set -ex \ 23 | && apk update \ 24 | # install composer 25 | && cd /tmp \ 26 | && wget https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar \ 27 | && chmod u+x composer.phar \ 28 | && mv composer.phar /usr/local/bin/composer \ 29 | # show php version and extensions 30 | && php -v \ 31 | && php -m \ 32 | # ---------- some config ---------- 33 | && cd /etc/php7 \ 34 | # - config PHP 35 | && { \ 36 | echo "upload_max_filesize=100M"; \ 37 | echo "post_max_size=108M"; \ 38 | echo "memory_limit=1024M"; \ 39 | echo "date.timezone=${TIMEZONE}"; \ 40 | } | tee conf.d/99-overrides.ini \ 41 | # - config timezone 42 | && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ 43 | && echo "${TIMEZONE}" > /etc/timezone \ 44 | # ---------- clear works ---------- 45 | && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \ 46 | && echo -e "\033[42;37m Build Completed :).\033[0m\n" 47 | 48 | COPY . /opt/www 49 | 50 | WORKDIR /opt/www 51 | 52 | RUN composer install --no-dev \ 53 | && composer dump-autoload -o \ 54 | && php /opt/www/bin/hyperf.php di:init-proxy 55 | 56 | EXPOSE 9501 57 | 58 | ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"] 59 | -------------------------------------------------------------------------------- /vue/src/components/common/leftMenu.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | -------------------------------------------------------------------------------- /vue/src/components/posts/add.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /vue/src/components/menus/rule.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /hyperf/app/Controller/MenusController.php: -------------------------------------------------------------------------------- 1 | service->getDataList(); 24 | return success($data); 25 | } 26 | 27 | public function show($id) 28 | { 29 | $data = $this->service->getDataById((int) $id); 30 | return success($data); 31 | } 32 | 33 | public function store(MenuRequest $request) 34 | { 35 | $validated = $request->validated(); 36 | $params = $this->request->all(); 37 | $data = $this->service->createData($params); 38 | return success($data); 39 | } 40 | 41 | public function update($id, MenuRequest $request) 42 | { 43 | $validated = $request->validated(); 44 | $params = $this->request->all(); 45 | $data = $this->service->updateDataById($params, (int) $id); 46 | return success($data); 47 | } 48 | 49 | public function destroy($id) 50 | { 51 | $data = $this->service->delDataById((int) $id); 52 | return success($data); 53 | } 54 | 55 | public function deletes(DeletesRequest $request) 56 | { 57 | $validated = $request->validated(); 58 | $ids = $this->request->input('ids'); 59 | $data = $this->service->delDatas($ids); 60 | return success($data); 61 | } 62 | 63 | public function enables(EnablesRequest $request) 64 | { 65 | $validated = $request->validated(); 66 | $ids = $this->request->input('ids'); 67 | $status = $this->request->input('status'); 68 | $data = $this->service->enableDatas($ids, $status); 69 | return success($data); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /hyperf/app/Controller/GroupsController.php: -------------------------------------------------------------------------------- 1 | service->getDataList(); 24 | return success($data); 25 | } 26 | 27 | public function show($id) 28 | { 29 | $data = $this->service->getDataById((int) $id); 30 | return success($data); 31 | } 32 | 33 | public function store(GroupRequest $request) 34 | { 35 | $validated = $request->validated(); 36 | $params = $this->request->all(); 37 | $data = $this->service->createData($params); 38 | return success($data); 39 | } 40 | 41 | public function update($id, GroupRequest $request) 42 | { 43 | $validated = $request->validated(); 44 | $params = $this->request->all(); 45 | $data = $this->service->updateDataById($params, (int) $id); 46 | return success($data); 47 | } 48 | 49 | public function destroy($id) 50 | { 51 | $data = $this->service->delDataById((int) $id); 52 | return success($data); 53 | } 54 | 55 | public function deletes(DeletesRequest $request) 56 | { 57 | $validated = $request->validated(); 58 | $ids = $this->request->input('ids'); 59 | $data = $this->service->delDatas($ids); 60 | return success($data); 61 | } 62 | 63 | public function enables(EnablesRequest $request) 64 | { 65 | $validated = $request->validated(); 66 | $ids = $this->request->input('ids'); 67 | $status = $this->request->input('status'); 68 | $data = $this->service->enableDatas($ids, $status); 69 | return success($data); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /hyperf/app/Kernel/Http/Response.php: -------------------------------------------------------------------------------- 1 | container = $container; 36 | $this->response = $container->get(ResponseInterface::class); 37 | } 38 | 39 | public function success($data = []) 40 | { 41 | return $this->response->json([ 42 | 'code' => 200, 43 | 'data' => $data, 44 | ]); 45 | } 46 | 47 | public function fail($code, $message = '') 48 | { 49 | return $this->response->json([ 50 | 'code' => $code, 51 | 'message' => $message, 52 | ]); 53 | } 54 | 55 | public function redirect($url, $status = 302) 56 | { 57 | return $this->response() 58 | ->withAddedHeader('Location', (string) $url) 59 | ->withStatus($status); 60 | } 61 | 62 | public function cookie(Cookie $cookie) 63 | { 64 | $response = $this->response()->withCookie($cookie); 65 | Context::set(PsrResponseInterface::class, $response); 66 | return $this; 67 | } 68 | 69 | /** 70 | * @return \Hyperf\HttpMessage\Server\Response 71 | */ 72 | public function response() 73 | { 74 | return Context::get(PsrResponseInterface::class); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /hyperf/app/Controller/StructuresController.php: -------------------------------------------------------------------------------- 1 | service->getDataList(); 24 | return success($data); 25 | } 26 | 27 | public function show($id) 28 | { 29 | $data = $this->service->getDataById((int) $id); 30 | return success($data); 31 | } 32 | 33 | public function store(StructureRequest $request) 34 | { 35 | $validated = $request->validated(); 36 | $params = $this->request->all(); 37 | $data = $this->service->createData($params); 38 | return success($data); 39 | } 40 | 41 | public function update($id, StructureRequest $request) 42 | { 43 | $validated = $request->validated(); 44 | $params = $this->request->all(); 45 | $data = $this->service->updateDataById($params, (int) $id); 46 | return success($data); 47 | } 48 | 49 | public function destroy($id) 50 | { 51 | $data = $this->service->delDataById((int) $id); 52 | return success($data); 53 | } 54 | 55 | public function deletes(DeletesRequest $request) 56 | { 57 | $validated = $request->validated(); 58 | $ids = $this->request->input('ids'); 59 | $data = $this->service->delDatas($ids); 60 | return success($data); 61 | } 62 | 63 | public function enables(EnablesRequest $request) 64 | { 65 | $validated = $request->validated(); 66 | $ids = $this->request->input('ids'); 67 | $status = $this->request->input('status'); 68 | $data = $this->service->enableDatas($ids, $status); 69 | return success($data); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /hyperf/app/Controller/RulesController.php: -------------------------------------------------------------------------------- 1 | request->input('type'); 24 | $data = $this->service->getDataList($type); 25 | return success($data); 26 | } 27 | 28 | public function show($id) 29 | { 30 | $data = $this->service->getDataById((int) $id); 31 | return success($data); 32 | } 33 | 34 | public function store(RuleRequest $request) 35 | { 36 | $validated = $request->validated(); 37 | $params = $this->request->all(); 38 | $data = $this->service->createData($params); 39 | return success($data); 40 | } 41 | 42 | public function update($id, RuleRequest $request) 43 | { 44 | $validated = $request->validated(); 45 | $params = $this->request->all(); 46 | $data = $this->service->updateDataById($params, (int) $id); 47 | return success($data); 48 | } 49 | 50 | public function destroy($id) 51 | { 52 | $data = $this->service->delDataById((int) $id); 53 | return success($data); 54 | } 55 | 56 | public function deletes(DeletesRequest $request) 57 | { 58 | $validated = $request->validated(); 59 | $ids = $this->request->input('ids'); 60 | $data = $this->service->delDatas($ids); 61 | return success($data); 62 | } 63 | 64 | public function enables(EnablesRequest $request) 65 | { 66 | $validated = $request->validated(); 67 | $ids = $this->request->input('ids'); 68 | $status = $this->request->input('status'); 69 | $data = $this->service->enableDatas($ids, $status); 70 | return success($data); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /hyperf/app/Controller/PostsController.php: -------------------------------------------------------------------------------- 1 | request->input('keywords'); 24 | $data = $this->service->getDataList($keywords); 25 | return success($data); 26 | } 27 | 28 | public function show($id) 29 | { 30 | $data = $this->service->getDataById((int) $id); 31 | return success($data); 32 | } 33 | 34 | public function store(PostRequest $request) 35 | { 36 | $validated = $request->validated(); 37 | $params = $this->request->all(); 38 | $data = $this->service->createData($params); 39 | return success($data); 40 | } 41 | 42 | public function update($id, PostRequest $request) 43 | { 44 | $validated = $request->validated(); 45 | $params = $this->request->all(); 46 | $data = $this->service->updateDataById($params, (int) $id); 47 | return success($data); 48 | } 49 | 50 | public function destroy($id) 51 | { 52 | $data = $this->service->delDataById((int) $id); 53 | return success($data); 54 | } 55 | 56 | public function deletes(DeletesRequest $request) 57 | { 58 | $validated = $request->validated(); 59 | $ids = $this->request->input('ids'); 60 | $data = $this->service->delDatas($ids); 61 | return success($data); 62 | } 63 | 64 | public function enables(EnablesRequest $request) 65 | { 66 | $validated = $request->validated(); 67 | $ids = $this->request->input('ids'); 68 | $status = $this->request->input('status'); 69 | $data = $this->service->enableDatas($ids, $status); 70 | return success($data); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /vue/src/components/posts/list.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /vue/src/components/groups/list.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /vue/src/components/rules/list.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | -------------------------------------------------------------------------------- /hyperf/app/Exception/Handler/BusinessExceptionHandler.php: -------------------------------------------------------------------------------- 1 | container = $container; 45 | $this->response = $container->get(Response::class); 46 | $this->logger = $container->get(StdoutLoggerInterface::class); 47 | } 48 | 49 | public function handle(Throwable $throwable, ResponseInterface $response) 50 | { 51 | if ($throwable instanceof BusinessException) { 52 | $this->logger->warning(format_throwable($throwable)); 53 | return $this->response->fail($throwable->getCode(), $throwable->getMessage()); 54 | } 55 | 56 | if ($throwable instanceof ValidationException) { 57 | $message = $throwable->validator->errors()->first(); 58 | return $this->response->fail(ErrorCode::PARAMS_INVALID, $message); 59 | } 60 | 61 | $this->logger->error(format_throwable($throwable)); 62 | return $this->response->fail(ErrorCode::SERVER_ERROR, 'Server Error'); 63 | } 64 | 65 | public function isValid(Throwable $throwable): bool 66 | { 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /vue/src/components/structures/list.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /vue/build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | // generate loader string to be used with extract text plugin 15 | function generateLoaders (loaders) { 16 | var sourceLoader = loaders.map(function (loader) { 17 | var extraParamChar 18 | if (/\?/.test(loader)) { 19 | loader = loader.replace(/\?/, '-loader?') 20 | extraParamChar = '&' 21 | } else { 22 | loader = loader + '-loader' 23 | extraParamChar = '?' 24 | } 25 | return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '') 26 | }).join('!') 27 | 28 | // Extract CSS when that option is specified 29 | // (which is the case during production build) 30 | if (options.extract) { 31 | return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) 32 | } else { 33 | return ['vue-style-loader', sourceLoader].join('!') 34 | } 35 | } 36 | 37 | // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html 38 | return { 39 | css: generateLoaders(['css']), 40 | postcss: generateLoaders(['css']), 41 | less: generateLoaders(['css', 'less']), 42 | sass: generateLoaders(['css', 'sass?indentedSyntax']), 43 | scss: generateLoaders(['css', 'sass']), 44 | stylus: generateLoaders(['css', 'stylus']), 45 | styl: generateLoaders(['css', 'stylus']), 46 | // 额外添加 47 | // js: 'babel!eslint' 48 | } 49 | } 50 | 51 | // Generate loaders for standalone style files (outside of .vue) 52 | exports.styleLoaders = function (options) { 53 | var output = [] 54 | var loaders = exports.cssLoaders(options) 55 | for (var extension in loaders) { 56 | var loader = loaders[extension] 57 | output.push({ 58 | test: new RegExp('\\.' + extension + '$'), 59 | loader: loader 60 | }) 61 | } 62 | return output 63 | } 64 | -------------------------------------------------------------------------------- /vue/build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | var config = require('../config') 3 | if (!process.env.NODE_ENV) process.env.NODE_ENV = config.dev.env 4 | var path = require('path') 5 | var express = require('express') 6 | var webpack = require('webpack') 7 | var opn = require('opn') 8 | var proxyMiddleware = require('http-proxy-middleware') 9 | var webpackConfig = require('./webpack.dev.conf') 10 | 11 | // default port where dev server listens for incoming traffic 12 | var port = process.env.PORT || config.dev.port 13 | // Define HTTP proxies to your custom API backend 14 | // https://github.com/chimurai/http-proxy-middleware 15 | var proxyTable = config.dev.proxyTable 16 | 17 | var app = express() 18 | var compiler = webpack(webpackConfig) 19 | 20 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 21 | publicPath: webpackConfig.output.publicPath, 22 | stats: { 23 | colors: true, 24 | chunks: false 25 | } 26 | }) 27 | 28 | var hotMiddleware = require('webpack-hot-middleware')(compiler) 29 | // force page reload when html-webpack-plugin template changes 30 | compiler.plugin('compilation', function (compilation) { 31 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 32 | hotMiddleware.publish({ action: 'reload' }) 33 | cb() 34 | }) 35 | }) 36 | 37 | // proxy api requests 38 | Object.keys(proxyTable).forEach(function (context) { 39 | var options = proxyTable[context] 40 | if (typeof options === 'string') { 41 | options = { target: options } 42 | } 43 | app.use(proxyMiddleware(context, options)) 44 | }) 45 | 46 | // handle fallback for HTML5 history API 47 | app.use(require('connect-history-api-fallback')()) 48 | 49 | // serve webpack bundle output 50 | app.use(devMiddleware) 51 | 52 | // enable hot-reload and state-preserving 53 | // compilation error display 54 | app.use(hotMiddleware) 55 | 56 | // serve pure static assets 57 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 58 | app.use(staticPath, express.static('./static')) 59 | 60 | module.exports = app.listen(port, function (err) { 61 | if (err) { 62 | console.log(err) 63 | return 64 | } 65 | var uri = 'http://localhost:' + port 66 | console.log('Listening at ' + uri + '\n') 67 | opn(uri) 68 | }) 69 | -------------------------------------------------------------------------------- /hyperf/app/Constants/ErrorCode.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /vue/src/components/structures/add.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /hyperf/app/Controller/UsersController.php: -------------------------------------------------------------------------------- 1 | request->input('keywords'); 25 | $page = $this->request->input('page', 1); 26 | $limit = $this->request->input('limit', $this->limit); 27 | $data = $this->service->getDataList($keywords, (int) $page, (int) $limit); 28 | return success($data); 29 | } 30 | 31 | public function show($id) 32 | { 33 | $data = $this->service->getDataById((int) $id); 34 | return success($data); 35 | } 36 | 37 | public function store(UserStoreRequest $request) 38 | { 39 | $validated = $request->validated(); 40 | $params = $this->request->all(); 41 | $data = $this->service->createData($params); 42 | return success($data); 43 | } 44 | 45 | public function update($id, UserUpdateRequest $request) 46 | { 47 | $validated = $request->validated(); 48 | $params = $this->request->all(); 49 | $data = $this->service->updateDataById($params, (int) $id); 50 | return success($data); 51 | } 52 | 53 | public function destroy($id) 54 | { 55 | $data = $this->service->delDataById((int) $id); 56 | return success($data); 57 | } 58 | 59 | public function deletes(DeletesRequest $request) 60 | { 61 | $validated = $request->validated(); 62 | $ids = $this->request->input('ids'); 63 | $data = $this->service->delDatas($ids); 64 | return success($data); 65 | } 66 | 67 | public function enables(EnablesRequest $request) 68 | { 69 | $validated = $request->validated(); 70 | $ids = $this->request->input('ids'); 71 | $status = $this->request->input('status'); 72 | $data = $this->service->enableDatas($ids, $status); 73 | return success($data); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyper_vue", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "dev": "node build/dev-server.js", 7 | "build": "node build/build.js" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.19.0", 11 | "element-ui": "^2.12.0", 12 | "font-awesome": "^4.7.0", 13 | "js-cookie": "^2.2.1", 14 | "lockr": "^0.8.4", 15 | "lodash": "^4.17.15", 16 | "moment": "^2.17.1", 17 | "nprogress": "^0.2.0", 18 | "query-string": "^4.3.4", 19 | "vee-validate": "^2.2.15", 20 | "vue": "^2.0.7", 21 | "vuex": "^2.0.0-rc.6" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^6.4.0", 25 | "babel-cli": "^6.18.0", 26 | "babel-core": "^6.0.0", 27 | "babel-eslint": "^7.1.1", 28 | "babel-loader": "^6.2.8", 29 | "babel-plugin-transform-runtime": "^6.0.0", 30 | "babel-polyfill": "^6.20.0", 31 | "babel-preset-es2015": "^6.0.0", 32 | "babel-preset-stage-3": "^6.17.0", 33 | "babel-register": "^6.0.0", 34 | "babel-runtime": "^6.26.0", 35 | "chalk": "^1.1.3", 36 | "connect-history-api-fallback": "^1.1.0", 37 | "css-loader": "^0.26.0", 38 | "eslint": "^3.12.2", 39 | "eslint-config-vue": "^2.0.1", 40 | "eslint-loader": "^1.6.1", 41 | "eslint-plugin-vue": "^1.0.0", 42 | "eventsource-polyfill": "^0.9.6", 43 | "express": "^4.13.3", 44 | "extract-text-webpack-plugin": "^1.0.1", 45 | "file-loader": "^0.9.0", 46 | "function-bind": "^1.0.2", 47 | "html-webpack-plugin": "^2.8.1", 48 | "http-proxy-middleware": "^0.17.2", 49 | "json-loader": "^0.5.4", 50 | "opn": "^4.0.2", 51 | "ora": "^0.3.0", 52 | "semver": "^5.7.1", 53 | "shelljs": "^0.7.4", 54 | "url-loader": "^0.5.7", 55 | "vue-loader": "^9.9.5", 56 | "vue-router": "^2.0.3", 57 | "vue-style-loader": "^1.0.0", 58 | "webpack": "^1.13.2", 59 | "webpack-dev-middleware": "^1.8.3", 60 | "webpack-hot-middleware": "^2.12.2", 61 | "webpack-merge": "^0.17.0" 62 | }, 63 | "engines": { 64 | "node": ">= 4.0.0", 65 | "npm": ">= 3.0.0" 66 | }, 67 | "repository": { 68 | "type": "git", 69 | "url": "https://github.com/WarnerYang/HyperVue/vue" 70 | }, 71 | "keywords": [ 72 | "backoffice", 73 | "hyperf", 74 | "vue" 75 | ], 76 | "author": "https://github.com/WarnerYang", 77 | "license": "MIT" 78 | } -------------------------------------------------------------------------------- /vue/src/components/menus/list.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | -------------------------------------------------------------------------------- /vue/src/components/posts/edit.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /hyperf/app/Controller/BaseController.php: -------------------------------------------------------------------------------- 1 | systemConfigService->getDataList(); 30 | return success($data); 31 | } 32 | 33 | public function login(LoginRequest $request) 34 | { 35 | $validated = $request->validated(); 36 | $username = $this->request->input('username'); 37 | $password = $this->request->input('password'); 38 | $verifyCode = $this->request->input('verifyCode'); 39 | $isRemember = $this->request->input('isRemember', 0); 40 | $data = $this->userService->login($username, $password, $verifyCode, $isRemember); 41 | return success($data); 42 | } 43 | 44 | public function relogin() 45 | { 46 | $rememberKey = $this->request->input('rememberKey'); 47 | $decrypt = decrypt($rememberKey); 48 | $username = $decrypt['username']; 49 | $password = $decrypt['password']; 50 | $data = $this->userService->login($username, $password, '', true, true); 51 | return success($data); 52 | } 53 | 54 | public function logout() 55 | { 56 | $authkey = $this->request->header('authkey'); 57 | $data = $this->userService->removeLoggedInfo($authkey); 58 | return success(); 59 | } 60 | 61 | public function changePwd(ChangePwdRequest $request) 62 | { 63 | $validated = $request->validated(); 64 | $oldPwd = $this->request->input('old_pwd'); 65 | $newPwd = $this->request->input('new_pwd'); 66 | $authKey = $this->request->header('authKey'); 67 | $data = $this->userService->changePwd($authKey, $oldPwd, $newPwd); 68 | return success($data); 69 | } 70 | 71 | public function getVerify() 72 | { 73 | // $captcha = new HonrayVerify(config('captcha')); 74 | // return $captcha->entry(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /vue/src/components/base/changePwd.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | -------------------------------------------------------------------------------- /vue/src/components/structures/edit.vue: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /hyperf/.php_cs: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 14 | ->setRules([ 15 | '@PSR2' => true, 16 | '@Symfony' => true, 17 | '@DoctrineAnnotation' => true, 18 | '@PhpCsFixer' => true, 19 | 'header_comment' => [ 20 | 'commentType' => 'PHPDoc', 21 | 'header' => $header, 22 | 'separate' => 'none', 23 | 'location' => 'after_declare_strict', 24 | ], 25 | 'array_syntax' => [ 26 | 'syntax' => 'short' 27 | ], 28 | 'list_syntax' => [ 29 | 'syntax' => 'short' 30 | ], 31 | 'concat_space' => [ 32 | 'spacing' => 'one' 33 | ], 34 | 'blank_line_before_statement' => [ 35 | 'statements' => [ 36 | 'declare', 37 | ], 38 | ], 39 | 'general_phpdoc_annotation_remove' => [ 40 | 'annotations' => [ 41 | 'author' 42 | ], 43 | ], 44 | 'ordered_imports' => [ 45 | 'imports_order' => [ 46 | 'class', 'function', 'const', 47 | ], 48 | 'sort_algorithm' => 'alpha', 49 | ], 50 | 'single_line_comment_style' => [ 51 | 'comment_types' => [ 52 | ], 53 | ], 54 | 'yoda_style' => [ 55 | 'always_move_variable' => false, 56 | 'equal' => false, 57 | 'identical' => false, 58 | ], 59 | 'phpdoc_align' => [ 60 | 'align' => 'left', 61 | ], 62 | 'multiline_whitespace_before_semicolons' => [ 63 | 'strategy' => 'no_multi_line', 64 | ], 65 | 'class_attributes_separation' => true, 66 | 'combine_consecutive_unsets' => true, 67 | 'declare_strict_types' => true, 68 | 'linebreak_after_opening_tag' => true, 69 | 'lowercase_constants' => true, 70 | 'lowercase_static_reference' => true, 71 | 'no_useless_else' => true, 72 | 'no_unused_imports' => true, 73 | 'not_operator_with_successor_space' => true, 74 | 'not_operator_with_space' => false, 75 | 'ordered_class_elements' => true, 76 | 'php_unit_strict' => false, 77 | 'phpdoc_separation' => false, 78 | 'single_quote' => true, 79 | 'standardize_not_equals' => true, 80 | 'multiline_comment_opening_closing' => true, 81 | ]) 82 | ->setFinder( 83 | PhpCsFixer\Finder::create() 84 | ->exclude('public') 85 | ->exclude('runtime') 86 | ->exclude('vendor') 87 | ->in(__DIR__) 88 | ) 89 | ->setUsingCache(false); 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperVue 2 | 🚀 基于 Hyperf + Vue + ElementUI 前后端分离的通用型管理后台 3 | 4 | - [hyperf 1.1](https://github.com/hyperf/hyperf) 5 | - [vue 2.0](https://github.com/vuejs/vue) 6 | - [element-ui 2.12](https://github.com/ElemeFE/element) 7 | 8 | ## 预览 9 | ![demo_menu](./doc/img/demo_menu.jpg) 10 | ![demo_auth](./doc/img/demo_auth.jpg) 11 | 12 | ## 如何使用 13 | 获取源码 14 | ``` 15 | git clone https://github.com/WarnerYang/HyperVue.git 16 | ``` 17 | 18 | #### hyperf 部署 19 | 20 | ``` 21 | # 下载并运行 hyperf/hyperf 镜像,并将镜像内的项目目录绑定到宿主机的hyperVue下的hyperf目录(windows用powershell) 22 | # 我的目录是 /Users/yhq/wwwroot/learn/HyperVue/hyperf 23 | cd hyperf 24 | 25 | docker run --name hyperVue-hyperf -v /Users/yhq/wwwroot/learn/HyperVue/hyperf:/hyperVue-hyperf -p 9501:9501 -it --entrypoint /bin/sh hyperf/hyperf:7.2-alpine-cli 26 | 27 | ``` 28 | 以下在容器中操作 29 | ``` 30 | # 镜像容器运行后,在容器内安装 Composer 31 | wget https://github.com/composer/composer/releases/download/1.8.6/composer.phar 32 | chmod u+x composer.phar 33 | mv composer.phar /usr/local/bin/composer 34 | 35 | # 将 Composer 镜像设置为阿里云镜像,加速国内下载速度 36 | composer config -g repo.packagist composer https://mirrors.aliyun.com/composer 37 | 38 | # 进入安装好的 Hyperf 项目目录 39 | cd hyperVue-hyperf 40 | 41 | # 安装依赖 42 | composer install 43 | 44 | # 复制env文件 45 | cp .env.example .env 46 | 47 | # 导入数据库 hyperVue.sql 48 | 49 | # 修改env文件里面的相关参数,保存退出 50 | vi .env 51 | 52 | # 启动 Hyperf 53 | php bin/hyperf.php start 54 | ``` 55 | 56 | #### vue 部署 57 | ``` 58 | # 下载并运行node镜像,并将镜像内的项目目录绑定到宿主机的hyperVue下的vue目录 59 | # 我的目录是 /Users/yhq/wwwroot/learn/HyperVue/vue 60 | docker run --name hyperVue-vue -v /Users/yhq/wwwroot/learn/HyperVue/vue:/hyperVue-vue -it --entrypoint /bin/sh node:latest 61 | ``` 62 | 以下在容器中运行 63 | ``` 64 | # 安装vim 65 | apt-get update 66 | apt-get install vim 67 | 68 | # 修改接口地址 69 | vim /hyperVue-vue/build/webpack.base.conf.js 70 | # 改成对应接口地址 71 | var PUB_HOST = JSON.stringify('http://127.0.0.1:9501/') 72 | 73 | # 将 npm 镜像设置为淘宝镜像cnpm,加速国内下载速度 74 | npm install -g cnpm --registry=https://registry.npm.taobao.org 75 | 76 | # 安装依赖 77 | cnpm install 78 | 79 | # 本地调试 80 | npm run dev 81 | 82 | # 构建 83 | npm run build 84 | ``` 85 | 86 | ## 一些常用命令 87 | ``` 88 | #进入容器 89 | docker exec -it hyperVue-vue /bin/sh 90 | 91 | 查看扩展信息 92 | php --ri swoole 93 | 94 | 查看composer包信息 95 | composer info | grep hyperf 96 | 97 | 请求 98 | curl 127.0.0.1:9501/admin/base/getConfigs -w %{http_code} 99 | 100 | 参数: 101 | -w %{http_code} 102 | -X POST 103 | 104 | 自动化测试 105 | https://phpunit.readthedocs.io/zh_CN/latest/writing-tests-for-phpunit.html 106 | # 重新生成代理类 107 | php bin/hyperf.php di:init-proxy 108 | # 运行单元测试 109 | composer test 110 | composer test -- --filter=testFoo 111 | 112 | # 类名: class FooTest extends HttpTestCase 113 | 114 | # 方法名: 115 | public function testFoo(){} 116 | 或者 117 | /** 118 | * @test 119 | */ 120 | public function foo(){} 121 | ``` 122 | > 免责声明: 该项目仅作为学习使用,如投入生产所产生的损失由使用者自己承担!!! -------------------------------------------------------------------------------- /hyperf/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperf/hyperf-skeleton", 3 | "type": "project", 4 | "keywords": [ 5 | "php", 6 | "swoole", 7 | "framework", 8 | "hyperf", 9 | "microservice", 10 | "middleware" 11 | ], 12 | "description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.", 13 | "license": "Apache-2.0", 14 | "require": { 15 | "php": ">=7.2", 16 | "ext-swoole": ">=4.4", 17 | "hyperf/cache": "~1.1.0", 18 | "hyperf/command": "~1.1.0", 19 | "hyperf/config": "~1.1.0", 20 | "hyperf/contract": "~1.1.0", 21 | "hyperf/database": "~1.1.0", 22 | "hyperf/db-connection": "~1.1.0", 23 | "hyperf/devtool": "~1.1.0", 24 | "hyperf/di": "~1.1.0", 25 | "hyperf/dispatcher": "~1.1.0", 26 | "hyperf/event": "~1.1.0", 27 | "hyperf/exception-handler": "~1.1.0", 28 | "hyperf/framework": "~1.1.0", 29 | "hyperf/guzzle": "~1.1.0", 30 | "hyperf/http-server": "~1.1.0", 31 | "hyperf/logger": "~1.1.0", 32 | "hyperf/memory": "~1.1.0", 33 | "hyperf/paginator": "~1.1.0", 34 | "hyperf/pool": "~1.1.0", 35 | "hyperf/process": "~1.1.0", 36 | "hyperf/redis": "~1.1.0", 37 | "hyperf/utils": "~1.1.0", 38 | "hyperf/constants": "~1.1.0", 39 | "hyperf/validation": "^1.1", 40 | "hyperf/translation": "^1.1" 41 | }, 42 | "require-dev": { 43 | "swoft/swoole-ide-helper": "^4.2", 44 | "phpmd/phpmd": "^2.6", 45 | "friendsofphp/php-cs-fixer": "^2.14", 46 | "mockery/mockery": "^1.0", 47 | "doctrine/common": "^2.9", 48 | "phpstan/phpstan": "^0.11.2", 49 | "hyperf/testing": "~1.1.0", 50 | "symfony/var-dumper": "^4.3" 51 | }, 52 | "suggest": { 53 | "ext-openssl": "Required to use HTTPS.", 54 | "ext-json": "Required to use JSON.", 55 | "ext-pdo": "Required to use MySQL Client.", 56 | "ext-pdo_mysql": "Required to use MySQL Client.", 57 | "ext-redis": "Required to use Redis Client." 58 | }, 59 | "autoload": { 60 | "psr-4": { 61 | "App\\": "app/" 62 | }, 63 | "files": [ 64 | "app/Kernel/Functions.php" 65 | ] 66 | }, 67 | "autoload-dev": { 68 | "psr-4": { 69 | "HyperfTest\\": "./test/" 70 | } 71 | }, 72 | "minimum-stability": "dev", 73 | "prefer-stable": true, 74 | "extra": [], 75 | "scripts": { 76 | "post-root-package-install": [ 77 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 78 | ], 79 | "test": "co-phpunit -c phpunit.xml --colors=always", 80 | "cs-fix": "php-cs-fixer fix $1", 81 | "analyze": "phpstan analyse --memory-limit 300M -l 0 -c phpstan.neon ./app ./config", 82 | "start": "php ./bin/hyperf.php start" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /vue/src/components/rules/add.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | -------------------------------------------------------------------------------- /hyperf/config/routes.php: -------------------------------------------------------------------------------- 1 | [PermissionMiddleware::class]]); 26 | 27 | Router::resource('/admin/users', 'App\Controller\UsersController', ['middleware' => [PermissionMiddleware::class]]); 28 | Router::post('/admin/users/deletes', 'App\Controller\UsersController@deletes', ['middleware' => [PermissionMiddleware::class]]); 29 | Router::post('/admin/users/enables', 'App\Controller\UsersController@enables', ['middleware' => [PermissionMiddleware::class]]); 30 | 31 | Router::resource('/admin/groups', 'App\Controller\GroupsController', ['middleware' => [PermissionMiddleware::class]]); 32 | Router::post('/admin/groups/deletes', 'App\Controller\GroupsController@deletes', ['middleware' => [PermissionMiddleware::class]]); 33 | Router::post('/admin/groups/enables', 'App\Controller\GroupsController@enables', ['middleware' => [PermissionMiddleware::class]]); 34 | 35 | Router::resource('/admin/menus', 'App\Controller\MenusController', ['middleware' => [PermissionMiddleware::class]]); 36 | Router::post('/admin/menus/deletes', 'App\Controller\MenusController@deletes', ['middleware' => [PermissionMiddleware::class]]); 37 | Router::post('/admin/menus/enables', 'App\Controller\MenusController@enables', ['middleware' => [PermissionMiddleware::class]]); 38 | 39 | Router::resource('/admin/posts', 'App\Controller\PostsController', ['middleware' => [PermissionMiddleware::class]]); 40 | Router::post('/admin/posts/deletes', 'App\Controller\PostsController@deletes', ['middleware' => [PermissionMiddleware::class]]); 41 | Router::post('/admin/posts/enables', 'App\Controller\PostsController@enables', ['middleware' => [PermissionMiddleware::class]]); 42 | 43 | Router::resource('/admin/rules', 'App\Controller\RulesController', ['middleware' => [PermissionMiddleware::class]]); 44 | Router::post('/admin/rules/deletes', 'App\Controller\RulesController@deletes', ['middleware' => [PermissionMiddleware::class]]); 45 | Router::post('/admin/rules/enables', 'App\Controller\RulesController@enables', ['middleware' => [PermissionMiddleware::class]]); 46 | 47 | Router::resource('/admin/structures', 'App\Controller\StructuresController', ['middleware' => [PermissionMiddleware::class]]); 48 | Router::post('/admin/structures/deletes', 'App\Controller\StructuresController@deletes', ['middleware' => [PermissionMiddleware::class]]); 49 | Router::post('/admin/structures/enables', 'App\Controller\StructuresController@enables', ['middleware' => [PermissionMiddleware::class]]); 50 | -------------------------------------------------------------------------------- /vue/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var projectRoot = path.resolve(__dirname, '../') 6 | 7 | var env = process.env.NODE_ENV 8 | // check env & config/index.js to decide weither to enable CSS Sourcemaps for the 9 | // various preprocessor loaders added to vue-loader at the end of this file 10 | var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap) 11 | var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap) 12 | var useCssSourceMap = cssSourceMapDev || cssSourceMapProd 13 | 14 | // define the different HOST between development and production environment 15 | var DEV_HOST = JSON.stringify('http://127.0.0.1:9501/') 16 | // var DEV_HOST = JSON.stringify('http://hypervue.test/') 17 | var PUB_HOST = JSON.stringify('https://hypervue.yanghuaqiang.com/api/') 18 | 19 | module.exports = { 20 | entry: { 21 | app: './src/main.js' 22 | }, 23 | output: { 24 | path: config.build.assetsRoot, 25 | publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, 26 | filename: '[name].js' 27 | }, 28 | eslint: { 29 | // configFile: './.eslintrc.json' 30 | }, 31 | plugins: [ 32 | new webpack.DefinePlugin({ 33 | HOST: process.env.NODE_ENV === 'production' ? PUB_HOST : DEV_HOST 34 | }) 35 | ], 36 | resolve: { 37 | extensions: ['', '.js', '.vue'], 38 | fallback: [path.join(__dirname, '../node_modules')], 39 | alias: { 40 | 'vue$': 'vue/dist/vue', 41 | 'src': path.resolve(__dirname, '../src'), 42 | 'assets': path.resolve(__dirname, '../src/assets'), 43 | 'components': path.resolve(__dirname, '../src/components') 44 | } 45 | }, 46 | resolveLoader: { 47 | fallback: [path.join(__dirname, '../node_modules')] 48 | }, 49 | module: { 50 | // preLoaders: [ 51 | // { 52 | // test: /\.js$/, 53 | // exclude: /node_modules/, 54 | // loader: 'eslint' 55 | // }, 56 | // { 57 | // test: /\.vue$/, 58 | // exclude: /node_modules/, 59 | // loader: 'eslint' 60 | // } 61 | // ], 62 | loaders: [ 63 | { 64 | test: /\.vue$/, 65 | loader: 'vue' 66 | }, 67 | { 68 | test: /\.js$/, 69 | loader: 'babel', 70 | include: projectRoot, 71 | exclude: /node_modules/, 72 | query: { 73 | presets: ['es2015', 'stage-3'] 74 | } 75 | }, 76 | { 77 | test: /\.json$/, 78 | loader: 'json' 79 | }, 80 | { 81 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 82 | loader: 'url', 83 | query: { 84 | limit: 10000, 85 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 86 | } 87 | }, 88 | { 89 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 90 | loader: 'url', 91 | query: { 92 | limit: 10000, 93 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 94 | } 95 | } 96 | ] 97 | }, 98 | vue: { 99 | loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }), 100 | postcss: [ 101 | require('autoprefixer')({ 102 | browsers: ['last 2 versions'] 103 | }) 104 | ] 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /vue/src/components/common/btnGroup.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /vue/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | var env = config.build.env 10 | 11 | var webpackConfig = merge(baseWebpackConfig, { 12 | module: { 13 | loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) 14 | }, 15 | devtool: config.build.productionSourceMap ? '#source-map' : false, 16 | output: { 17 | path: config.build.assetsRoot, 18 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 19 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 20 | }, 21 | vue: { 22 | loaders: utils.cssLoaders({ 23 | sourceMap: config.build.productionSourceMap, 24 | extract: true 25 | }) 26 | }, 27 | plugins: [ 28 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 29 | new webpack.DefinePlugin({ 30 | 'process.env': env 31 | }), 32 | new webpack.optimize.UglifyJsPlugin({ 33 | compress: { 34 | warnings: false 35 | } 36 | }), 37 | new webpack.optimize.OccurenceOrderPlugin(), 38 | // extract css into its own file 39 | new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')), 40 | // generate dist index.html with correct asset hash for caching. 41 | // you can customize output by editing /index.html 42 | // see https://github.com/ampedandwired/html-webpack-plugin 43 | new HtmlWebpackPlugin({ 44 | filename: config.build.index, 45 | template: 'index.html', 46 | inject: true, 47 | minify: { 48 | removeComments: true, 49 | collapseWhitespace: true, 50 | removeAttributeQuotes: true 51 | // more options: 52 | // https://github.com/kangax/html-minifier#options-quick-reference 53 | }, 54 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 55 | chunksSortMode: 'dependency' 56 | }), 57 | // split vendor js into its own file 58 | new webpack.optimize.CommonsChunkPlugin({ 59 | name: 'vendor', 60 | minChunks: function (module, count) { 61 | // any required modules inside node_modules are extracted to vendor 62 | return ( 63 | module.resource && 64 | /\.js$/.test(module.resource) && 65 | module.resource.indexOf( 66 | path.join(__dirname, '../node_modules') 67 | ) === 0 68 | ) 69 | } 70 | }), 71 | // extract webpack runtime and module manifest to its own file in order to 72 | // prevent vendor hash from being updated whenever app bundle is updated 73 | new webpack.optimize.CommonsChunkPlugin({ 74 | name: 'manifest', 75 | chunks: ['vendor'] 76 | }) 77 | ] 78 | }) 79 | 80 | if (config.build.productionGzip) { 81 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 82 | 83 | webpackConfig.plugins.push( 84 | new CompressionWebpackPlugin({ 85 | asset: '[path].gz[query]', 86 | algorithm: 'gzip', 87 | test: new RegExp( 88 | '\\.(' + 89 | config.build.productionGzipExtensions.join('|') + 90 | ')$' 91 | ), 92 | threshold: 10240, 93 | minRatio: 0.8 94 | }) 95 | ) 96 | } 97 | 98 | module.exports = webpackConfig 99 | -------------------------------------------------------------------------------- /vue/src/components/rules/edit.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | -------------------------------------------------------------------------------- /hyperf/app/Kernel/Http/Router.php: -------------------------------------------------------------------------------- 1 | getAttributes()['Hyperf\HttpServer\Router\Dispatched']->handler->callback; 117 | [$class, $method] = explode('@', $action); 118 | 119 | return ['controller' => $class, 'method' => $method]; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /vue/src/assets/js/filter.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | export default (function () { 3 | Vue.filter('status', function (value) { 4 | if (value == 1) { 5 | return '启用' 6 | } else if (value == 0) { 7 | return '禁用' 8 | } else { 9 | return '未知状态' 10 | } 11 | }) 12 | Vue.filter('tagType', function (value) { 13 | if (value == 1) { 14 | return 'success' 15 | } else if (value == 0) { 16 | return 'danger' 17 | } else { 18 | return '' 19 | } 20 | }) 21 | Vue.filter('rules', function (value) { 22 | return value 23 | }) 24 | Vue.filter('fileLink', function (value) { 25 | const link = window.imgUrl + value 26 | return link 27 | }) 28 | Vue.filter('toolType', function (value) { 29 | let type = '' 30 | if (value == 1) { 31 | type = '系统工具' 32 | } else if (value == 2) { 33 | type = '说明指导' 34 | } 35 | return type 36 | }) 37 | Vue.filter('numToString', function (value) { 38 | const string = value.toString() 39 | return string 40 | }) 41 | Vue.filter('projectState', function (value) { 42 | let string = '' 43 | switch (value) { 44 | case '1': 45 | string = '售前项目' 46 | break 47 | case '2': 48 | string = '服务中项目' 49 | break 50 | case '3': 51 | string = '已结束项目' 52 | break 53 | } 54 | return string 55 | }) 56 | Vue.filter('time', function (value) { 57 | let day = moment.unix(value) 58 | let date = moment(day).format('YYYY/MM/DD H:mm') 59 | return date 60 | }) 61 | Vue.filter('date', function (value) { 62 | let day = moment.unix(value) 63 | let date = moment(day).format('YYYY/MM/DD') 64 | return date 65 | }) 66 | Vue.filter('abstract', function (value) { 67 | let abstract = '' 68 | if (value.length > 70) { 69 | abstract = value.substr(0, 70) + '...' 70 | } else { 71 | abstract = value 72 | } 73 | return abstract 74 | }) 75 | Vue.filter('posStatus', function (value) { 76 | let status = '' 77 | switch (value) { 78 | case 1: 79 | status = '在职' 80 | break 81 | case 2: 82 | status = '待入职' 83 | break 84 | case 3: 85 | status = '离职' 86 | break 87 | } 88 | return status 89 | }) 90 | Vue.filter('template', function (value) { 91 | let template = '' 92 | if (value == '') { 93 | template = '上传' 94 | } else { 95 | template = '上传更新' 96 | } 97 | return template 98 | }) 99 | Vue.filter('menuType', function (value) { 100 | let title = '' 101 | switch (value) { 102 | case 1: 103 | title = '普通菜单' 104 | break 105 | case 2: 106 | title = '外链' 107 | break 108 | } 109 | return title 110 | }) 111 | Vue.filter('ruleLevel', function (value) { 112 | let title = '' 113 | switch (value) { 114 | case 1: 115 | title = '模块' 116 | break 117 | case 2: 118 | title = '控制器' 119 | break 120 | case 3: 121 | title = '操作' 122 | break 123 | } 124 | return title 125 | }) 126 | })() -------------------------------------------------------------------------------- /hyperf/app/Middleware/PermissionMiddleware.php: -------------------------------------------------------------------------------- 1 | request->header('authKey'); 36 | $sessionId = $this->request->header('sessionId'); 37 | 38 | //获取用户信息 39 | $getLoggedInfo = $this->userService->getLoggedInfo($authKey); 40 | if (!$getLoggedInfo) { 41 | throw new BusinessException(ErrorCode::LOGIN_INVALID); 42 | } 43 | 44 | $userInfo = $getLoggedInfo['userInfo']; 45 | $uid = $userInfo['id']; 46 | $username = $userInfo['username']; 47 | // 检查用户状态 48 | if (!$this->userService->isEnable($uid)) { 49 | throw new BusinessException(ErrorCode::USER_DELETED_OR_DISABLED); 50 | } 51 | 52 | // 多处登录校验 53 | if ($getLoggedInfo['sessionId'] !== $sessionId && !config('ALLOW_MULTIPLE_LOGINS')) { 54 | throw new BusinessException(ErrorCode::LOGGED_ELSEWHERE); 55 | } 56 | 57 | // 获取路由名 58 | $ruleName = Router::getCurrentRuleName(); 59 | 60 | // // 用户id 写入日志 61 | // $data['uid'] = $uid; 62 | // $data['name'] = $username; 63 | // Log::record($data, 'admin_param'); 64 | 65 | // 权限校验 66 | $isSupperUser = $this->userService->isSupperUser($uid); 67 | if (!$isSupperUser && !$this->check($ruleName, $uid)) { 68 | throw new BusinessException(ErrorCode::PERMISSION_DENIED); 69 | } 70 | 71 | // 保存用户信息到上下文 72 | Context::set('userInfo', $userInfo); 73 | 74 | return $handler->handle($request); 75 | } 76 | 77 | /** 78 | * 权限校验 79 | * 80 | * @param string|array $name 需要验证的规则列表,支持逗号分隔的权限规则或索引数组 81 | * @param integer $uid 认证用户的id 82 | * @return boolean 通过验证返回true 失败返回false 83 | */ 84 | private function check($name, $uid, $relation = 'or'): bool 85 | { 86 | // 获取用户具有权限的规则列表 87 | $getUserOwnRules = $this->userService->getUserOwnRules($uid); 88 | $rulesList = $getUserOwnRules['rulesList']; 89 | 90 | if (is_string($name)) { 91 | $name = strtolower($name); 92 | if (strpos($name, ',') !== false) { 93 | $name = explode(',', $name); 94 | } else { 95 | $name = array($name); 96 | } 97 | } 98 | if (is_array($name)) { 99 | foreach ($name as $k => $v) { 100 | $name[$k] = strtolower($v); 101 | } 102 | } 103 | 104 | // 保存验证通过的规则名 105 | $list = []; 106 | foreach ($rulesList as $rule) { 107 | if (in_array($rule, $name)) { 108 | $list[] = $rule; 109 | } 110 | } 111 | if ($relation == 'or' && !empty($list)) { 112 | return true; 113 | } 114 | $diff = array_diff($name, $list); 115 | if ($relation == 'and' && empty($diff)) { 116 | return true; 117 | } 118 | return false; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /vue/src/components/menus/add.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | -------------------------------------------------------------------------------- /vue/src/components/users/list.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | -------------------------------------------------------------------------------- /vue/src/routes.js: -------------------------------------------------------------------------------- 1 | import login from './components/base/login' 2 | import refresh from './components/base/refresh' 3 | import home from './components/home' 4 | import menusList from './components/menus/list' 5 | import menusAdd from './components/menus/add' 6 | import menusEdit from './components/menus/edit' 7 | import configs from './components/configs/add' 8 | import rulesList from './components/rules/list' 9 | import rulesAdd from './components/rules/add' 10 | import rulesEdit from './components/rules/edit' 11 | import postsList from './components/posts/list' 12 | import postsAdd from './components/posts/add' 13 | import postsEdit from './components/posts/edit' 14 | import structuresList from './components/structures/list' 15 | import structuresAdd from './components/structures/add' 16 | import structuresEdit from './components/structures/edit' 17 | import groupsList from './components/groups/list' 18 | import groupsAdd from './components/groups/add' 19 | import groupsEdit from './components/groups/edit' 20 | import usersList from './components/users/list' 21 | import usersAdd from './components/users/add' 22 | import usersEdit from './components/users/edit' 23 | 24 | /** 25 | * meta参数解析 26 | * activeMenu 高亮菜单 27 | */ 28 | 29 | const routes = [ 30 | { path: '/', component: login, name: 'login' }, 31 | { 32 | path: '/refresh', 33 | component: home, 34 | children: [ 35 | { path: '/refresh', component: refresh, name: 'refresh' } 36 | ] 37 | }, 38 | { 39 | path: '/home', 40 | component: home, 41 | children: [ 42 | { path: 'menus/list', component: menusList, name: 'menusList', meta: { activeMenu: '/home/menus/list' } }, 43 | { path: 'menus/add', component: menusAdd, name: 'menusAdd', meta: { activeMenu: '/home/menus/list' } }, 44 | { path: 'menus/edit/:id', component: menusEdit, name: 'menusEdit', meta: { activeMenu: '/home/menus/list' } } 45 | ] 46 | }, 47 | { 48 | path: '/home', 49 | component: home, 50 | children: [ 51 | { path: 'configs/add', component: configs, name: 'configs', meta: { activeMenu: '/home/configs/add' } } 52 | ] 53 | }, 54 | 55 | { 56 | path: '/home', 57 | component: home, 58 | children: [ 59 | { path: 'rules/list', component: rulesList, name: 'rulesList', meta: { activeMenu: '/home/rules/list' } }, 60 | { path: 'rules/add', component: rulesAdd, name: 'rulesAdd', meta: { activeMenu: '/home/rules/list' } }, 61 | { path: 'rules/edit/:id', component: rulesEdit, name: 'rulesEdit', meta: { activeMenu: '/home/rules/list' } } 62 | ] 63 | }, 64 | { 65 | path: '/home', 66 | component: home, 67 | children: [ 68 | { path: 'posts/list', component: postsList, name: 'postsList', meta: { activeMenu: '/home/posts/list' } }, 69 | { path: 'posts/add', component: postsAdd, name: 'postsAdd', meta: { activeMenu: '/home/posts/list' } }, 70 | { path: 'posts/edit/:id', component: postsEdit, name: 'postsEdit', meta: { activeMenu: '/home/posts/list' } } 71 | ] 72 | }, 73 | { 74 | path: '/home', 75 | component: home, 76 | children: [ 77 | { path: 'structures/list', component: structuresList, name: 'structuresList', meta: { activeMenu: '/home/structures/list' } }, 78 | { path: 'structures/add', component: structuresAdd, name: 'structuresAdd', meta: { activeMenu: '/home/structures/list' } }, 79 | { path: 'structures/edit/:id', component: structuresEdit, name: 'structuresEdit', meta: { activeMenu: '/home/structures/list' } } 80 | ] 81 | }, 82 | { 83 | path: '/home', 84 | component: home, 85 | children: [ 86 | { path: 'groups/list', component: groupsList, name: 'groupsList', meta: { activeMenu: '/home/groups/list' } }, 87 | { path: 'groups/add', component: groupsAdd, name: 'groupsAdd', meta: { activeMenu: '/home/groups/list' } }, 88 | { path: 'groups/edit/:id', component: groupsEdit, name: 'groupsEdit', meta: { activeMenu: '/home/groups/list' } } 89 | ] 90 | }, 91 | { 92 | path: '/home', 93 | component: home, 94 | children: [ 95 | { path: 'users/list', component: usersList, name: 'usersList', meta: { activeMenu: '/home/users/list' } }, 96 | { path: 'users/add', component: usersAdd, name: 'usersAdd', meta: { activeMenu: '/home/users/list' } }, 97 | { path: 'users/edit/:id', component: usersEdit, name: 'usersEdit', meta: { activeMenu: '/home/users/list' } } 98 | ] 99 | }, 100 | ] 101 | export default routes 102 | -------------------------------------------------------------------------------- /vue/src/components/menus/edit.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | -------------------------------------------------------------------------------- /vue/src/components/configs/add.vue: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /vue/src/components/base/login.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | -------------------------------------------------------------------------------- /vue/src/components/users/add.vue: -------------------------------------------------------------------------------- 1 | 48 | --------------------------------------------------------------------------------