From 5dfbf91fb4213c6e0408c392c898f80872a20eb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=8F=E4=B8=91=E8=B7=AF=E4=BA=BA?= <2278757482@qq.com>
Date: Fri, 15 Jan 2021 23:25:37 +0800
Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E4=BA=8E=20https://gitee.com/clown-pa?=
=?UTF-8?q?sserby-community/laravel-vue-admin=20=E4=BB=93=E5=BA=93?=
=?UTF-8?q?=E5=BC=80=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.editorconfig | 19 +
.env.development | 5 +
.env.example | 60 +
.env.production | 6 +
.gitattributes | 5 +
.gitignore | 10 +
.styleci.yml | 13 +
app/Console/Kernel.php | 57 +
app/Exceptions/Admin/AuthException.php | 28 +
app/Exceptions/Admin/AuthTokenException.php | 28 +
app/Exceptions/Exception.php | 20 +
app/Exceptions/Handler.php | 67 +
app/Exceptions/InternalException.php | 21 +
app/Exceptions/InvalidRequestException.php | 20 +
app/Helper/builtin-functions.php | 292 +
app/Helper/functions.php | 3953 +++
app/Http/Controllers/Controller.php | 13 +
app/Http/Kernel.php | 72 +
app/Http/Middleware/Authenticate.php | 21 +
.../Middleware/ConvertEmptyStringsToNull.php | 32 +
app/Http/Middleware/EncryptCookies.php | 17 +
.../PreventRequestsDuringMaintenance.php | 17 +
.../Middleware/RedirectIfAuthenticated.php | 32 +
app/Http/Middleware/TrimStrings.php | 18 +
app/Http/Middleware/TrustHosts.php | 20 +
app/Http/Middleware/TrustProxies.php | 23 +
app/Http/Middleware/VerifyCsrfToken.php | 17 +
app/Http/Requests/BaseRequest.php | 30 +
app/Library/Http.php | 488 +
app/Library/SystemInfo.php | 191 +
app/Library/cpu_usage.vbs | 3 +
app/Library/memory_usage.vbs | 6 +
app/Listeners/QueryListener.php | 52 +
app/Models/Model.php | 92 +
app/Models/MonthModel.php | 100 +
app/Models/User.php | 43 +
app/Models/YearModel.php | 89 +
app/Modules/Admin/Config/.gitkeep | 0
app/Modules/Admin/Config/config.php | 5 +
app/Modules/Admin/Console/.gitkeep | 0
app/Modules/Admin/Console/AutoTableBuild.php | 77 +
.../Admin/Database/Migrations/.gitkeep | 0
app/Modules/Admin/Database/Seeders/.gitkeep | 0
.../Database/Seeders/AdminDatabaseSeeder.php | 21 +
app/Modules/Admin/Database/factories/.gitkeep | 0
app/Modules/Admin/Entities/.gitkeep | 0
.../Admin/Entities/Article/Article.php | 35 +
.../Entities/Article/ArticleCategory.php | 87 +
.../Admin/Entities/Article/ArticleLabel.php | 11 +
.../Entities/Article/ArticleWithLabel.php | 11 +
app/Modules/Admin/Entities/Log/AdminLog.php | 22 +
.../Admin/Entities/Log/AdminLoginLog.php | 38 +
app/Modules/Admin/Entities/Rabc/Admin.php | 145 +
app/Modules/Admin/Entities/Rabc/AdminInfo.php | 10 +
app/Modules/Admin/Entities/Rabc/AdminMenu.php | 33 +
app/Modules/Admin/Entities/Rabc/AdminRole.php | 48 +
.../Admin/Entities/Rabc/AdminRoleWithMenu.php | 16 +
.../Admin/Entities/Rabc/AdminWithRole.php | 10 +
app/Modules/Admin/Entities/System/Banner.php | 25 +
app/Modules/Admin/Entities/System/Config.php | 92 +
.../Admin/Entities/System/Friendlink.php | 25 +
.../Admin/Entities/System/Protocol.php | 11 +
app/Modules/Admin/Entities/System/Version.php | 11 +
app/Modules/Admin/Http/Controllers/.gitkeep | 0
.../Article/ArticleCategoryController.php | 25 +
.../Controllers/Article/ArticleController.php | 25 +
.../Article/ArticleLabelController.php | 25 +
.../Admin/Http/Controllers/AuthController.php | 64 +
.../Admin/Http/Controllers/BaseController.php | 147 +
.../Http/Controllers/IndexController.php | 78 +
.../Controllers/Log/AdminLogController.php | 14 +
.../Log/AdminLoginLogController.php | 14 +
.../Http/Controllers/Rabc/AdminController.php | 25 +
.../Controllers/Rabc/AdminMenuController.php | 25 +
.../Controllers/Rabc/AdminRoleController.php | 25 +
.../Controllers/System/BannerController.php | 25 +
.../Controllers/System/ConfigController.php | 59 +
.../System/FriendlinkController.php | 25 +
.../Controllers/System/ProtocolController.php | 25 +
.../Controllers/System/VersionController.php | 25 +
.../Http/Controllers/UploadController.php | 31 +
app/Modules/Admin/Http/Middleware/.gitkeep | 0
.../Admin/Http/Middleware/AdminLog.php | 43 +
.../Admin/Http/Middleware/CheckAuth.php | 44 +
.../Admin/Http/Middleware/CheckRabc.php | 64 +
app/Modules/Admin/Http/Requests/.gitkeep | 0
.../Article/ArticleCategoryRequest.php | 42 +
.../Requests/Article/ArticleLabelRequest.php | 39 +
.../Http/Requests/Article/ArticleRequest.php | 47 +
.../Admin/Http/Requests/BaseRequest.php | 52 +
.../Admin/Http/Requests/LoginRequest.php | 27 +
.../Http/Requests/Rabc/AdminMenuRequest.php | 37 +
.../Admin/Http/Requests/Rabc/AdminRequest.php | 52 +
.../Http/Requests/Rabc/AdminRoleRequest.php | 40 +
.../Http/Requests/System/BannerRequest.php | 41 +
.../Http/Requests/System/ConfigRequest.php | 42 +
.../Requests/System/FriendlinkRequest.php | 50 +
.../Http/Requests/System/ProtocolRequest.php | 47 +
.../Http/Requests/System/VersionRequest.php | 55 +
app/Modules/Admin/Providers/.gitkeep | 0
.../Admin/Providers/AdminServiceProvider.php | 112 +
.../Admin/Providers/RouteServiceProvider.php | 69 +
app/Modules/Admin/Resources/assets/.gitkeep | 0
app/Modules/Admin/Resources/assets/js/app.js | 0
.../Admin/Resources/assets/sass/app.scss | 0
app/Modules/Admin/Resources/lang/.gitkeep | 0
app/Modules/Admin/Resources/views/.gitkeep | 0
.../Admin/Resources/views/admin.blade.php | 17 +
.../Admin/Resources/views/index.blade.php | 9 +
.../Resources/views/layouts/master.blade.php | 19 +
.../Admin/Resources/vue-element-admin/App.vue | 11 +
.../vue-element-admin/api/.idea/api.iml | 9 +
.../vue-element-admin/api/.idea/misc.xml | 9 +
.../vue-element-admin/api/.idea/modules.xml | 8 +
.../vue-element-admin/api/.idea/vcs.xml | 6 +
.../vue-element-admin/api/.idea/workspace.xml | 53 +
.../vue-element-admin/api/admin_menus.js | 48 +
.../vue-element-admin/api/admin_roles.js | 48 +
.../vue-element-admin/api/adminloginlogs.js | 17 +
.../vue-element-admin/api/adminlogs.js | 17 +
.../Resources/vue-element-admin/api/admins.js | 57 +
.../api/article_categories.js | 48 +
.../vue-element-admin/api/article_labels.js | 41 +
.../vue-element-admin/api/articles.js | 56 +
.../vue-element-admin/api/banners.js | 41 +
.../Resources/vue-element-admin/api/common.js | 16 +
.../vue-element-admin/api/configs.js | 65 +
.../vue-element-admin/api/friendlinks.js | 41 +
.../Resources/vue-element-admin/api/indexs.js | 42 +
.../Resources/vue-element-admin/api/login.js | 30 +
.../vue-element-admin/api/versions.js | 41 +
.../assets/401_images/401.gif | Bin 0 -> 164227 bytes
.../assets/404_images/404.png | Bin 0 -> 98071 bytes
.../assets/404_images/404_cloud.png | Bin 0 -> 4766 bytes
.../custom-theme/fonts/element-icons.ttf | Bin 0 -> 11028 bytes
.../custom-theme/fonts/element-icons.woff | Bin 0 -> 6124 bytes
.../assets/custom-theme/index.css | 22420 ++++++++++++++
.../components/BackToTop/index.vue | 111 +
.../components/Breadcrumb/index.vue | 86 +
.../components/Charts/Keyboard.vue | 155 +
.../components/Charts/LineMarker.vue | 227 +
.../components/Charts/MixChart.vue | 272 +
.../components/Charts/mixins/resize.js | 56 +
.../components/DndList/index.vue | 166 +
.../components/DragSelect/index.vue | 65 +
.../components/Dropzone/index.vue | 297 +
.../components/ErrorLog/index.vue | 78 +
.../components/GithubCorner/index.vue | 56 +
.../components/Hamburger/index.vue | 44 +
.../components/HeaderSearch/index.vue | 208 +
.../components/ImageCropper/index.vue | 1779 ++
.../ImageCropper/utils/data2blob.js | 19 +
.../ImageCropper/utils/effectRipple.js | 39 +
.../components/ImageCropper/utils/language.js | 232 +
.../components/ImageCropper/utils/mimes.js | 7 +
.../components/JsonEditor/index.vue | 77 +
.../components/Kanban/index.vue | 99 +
.../components/LangSelect/index.vue | 41 +
.../components/MDinput/index.vue | 360 +
.../MarkdownEditor/default-options.js | 31 +
.../components/MarkdownEditor/index.vue | 121 +
.../components/Pagination/index.vue | 101 +
.../components/PanThumb/index.vue | 146 +
.../components/RightPanel/index.vue | 145 +
.../components/Screenfull/index.vue | 60 +
.../components/Share/DropdownMenu.vue | 103 +
.../components/SizeSelect/index.vue | 57 +
.../components/Sticky/index.vue | 91 +
.../components/SvgIcon/index.vue | 62 +
.../components/TextHoverEffect/Mallki.vue | 113 +
.../components/ThemePicker/index.vue | 174 +
.../Tinymce/components/EditorImage.vue | 111 +
.../components/Tinymce/dynamicLoadScript.js | 59 +
.../components/Tinymce/index.vue | 254 +
.../components/Tinymce/plugins.js | 7 +
.../components/Tinymce/toolbar.js | 6 +
.../components/Upload/SingleImage.vue | 134 +
.../components/Upload/SingleImage2.vue | 130 +
.../components/Upload/SingleImage3.vue | 157 +
.../components/UploadExcel/index.vue | 138 +
.../components/Uploads/image/index.vue | 1643 +
.../Uploads/image/utils/data2blob.js | 19 +
.../Uploads/image/utils/effectRipple.js | 39 +
.../Uploads/image/utils/language.js | 232 +
.../components/Uploads/image/utils/mimes.js | 7 +
.../directive/clipboard/clipboard.js | 49 +
.../directive/clipboard/index.js | 13 +
.../directive/el-drag-dialog/drag.js | 77 +
.../directive/el-drag-dialog/index.js | 13 +
.../directive/el-table/adaptive.js | 41 +
.../directive/el-table/index.js | 13 +
.../directive/permission/index.js | 13 +
.../directive/permission/permission.js | 31 +
.../vue-element-admin/directive/sticky.js | 91 +
.../directive/waves/index.js | 13 +
.../directive/waves/waves.css | 26 +
.../directive/waves/waves.js | 72 +
.../vue-element-admin/filters/index.js | 68 +
.../vue-element-admin/icons/index.js | 9 +
.../vue-element-admin/icons/svg/404.svg | 1 +
.../vue-element-admin/icons/svg/admin.svg | 1 +
.../vue-element-admin/icons/svg/bug.svg | 1 +
.../vue-element-admin/icons/svg/chart.svg | 1 +
.../vue-element-admin/icons/svg/clipboard.svg | 1 +
.../vue-element-admin/icons/svg/comment.svg | 1 +
.../vue-element-admin/icons/svg/component.svg | 1 +
.../icons/svg/create-user.svg | 1 +
.../vue-element-admin/icons/svg/dashboard.svg | 1 +
.../icons/svg/documentation.svg | 1 +
.../vue-element-admin/icons/svg/dollar.svg | 1 +
.../vue-element-admin/icons/svg/drag.svg | 1 +
.../vue-element-admin/icons/svg/edit.svg | 1 +
.../vue-element-admin/icons/svg/education.svg | 1 +
.../vue-element-admin/icons/svg/email.svg | 1 +
.../vue-element-admin/icons/svg/example.svg | 1 +
.../vue-element-admin/icons/svg/excel.svg | 1 +
.../icons/svg/exit-fullscreen.svg | 1 +
.../vue-element-admin/icons/svg/eye-open.svg | 1 +
.../vue-element-admin/icons/svg/eye.svg | 1 +
.../vue-element-admin/icons/svg/form.svg | 1 +
.../icons/svg/fullscreen.svg | 1 +
.../vue-element-admin/icons/svg/guide 2.svg | 1 +
.../vue-element-admin/icons/svg/guide.svg | 1 +
.../vue-element-admin/icons/svg/icon.svg | 1 +
.../icons/svg/international.svg | 1 +
.../vue-element-admin/icons/svg/language.svg | 1 +
.../vue-element-admin/icons/svg/layout.svg | 1 +
.../vue-element-admin/icons/svg/like.svg | 1 +
.../vue-element-admin/icons/svg/link.svg | 1 +
.../vue-element-admin/icons/svg/list.svg | 1 +
.../vue-element-admin/icons/svg/lock.svg | 1 +
.../vue-element-admin/icons/svg/message.svg | 1 +
.../vue-element-admin/icons/svg/money.svg | 1 +
.../vue-element-admin/icons/svg/nested.svg | 1 +
.../vue-element-admin/icons/svg/password.svg | 1 +
.../vue-element-admin/icons/svg/pdf.svg | 1 +
.../vue-element-admin/icons/svg/people.svg | 1 +
.../vue-element-admin/icons/svg/peoples.svg | 1 +
.../vue-element-admin/icons/svg/qq.svg | 1 +
.../vue-element-admin/icons/svg/role.svg | 1 +
.../vue-element-admin/icons/svg/search.svg | 1 +
.../vue-element-admin/icons/svg/shopping.svg | 1 +
.../vue-element-admin/icons/svg/size.svg | 1 +
.../vue-element-admin/icons/svg/skill.svg | 1 +
.../vue-element-admin/icons/svg/star.svg | 1 +
.../vue-element-admin/icons/svg/tab.svg | 1 +
.../vue-element-admin/icons/svg/table.svg | 1 +
.../vue-element-admin/icons/svg/theme.svg | 1 +
.../icons/svg/tree-table.svg | 1 +
.../vue-element-admin/icons/svg/tree.svg | 1 +
.../vue-element-admin/icons/svg/user.svg | 1 +
.../vue-element-admin/icons/svg/wechat.svg | 1 +
.../vue-element-admin/icons/svg/zip.svg | 1 +
.../vue-element-admin/icons/svgo.yml | 22 +
.../Resources/vue-element-admin/lang/en.js | 199 +
.../Resources/vue-element-admin/lang/es.js | 199 +
.../Resources/vue-element-admin/lang/index.js | 55 +
.../Resources/vue-element-admin/lang/ja.js | 199 +
.../Resources/vue-element-admin/lang/zh.js | 198 +
.../layout/components/AppMain.vue | 57 +
.../layout/components/Navbar.vue | 167 +
.../layout/components/Settings/index.vue | 145 +
.../layout/components/Sidebar/FixiOSBug.js | 26 +
.../layout/components/Sidebar/Item.vue | 41 +
.../layout/components/Sidebar/Link.vue | 43 +
.../layout/components/Sidebar/Logo.vue | 83 +
.../layout/components/Sidebar/SidebarItem.vue | 98 +
.../layout/components/Sidebar/index.vue | 54 +
.../layout/components/TagsView/ScrollPane.vue | 94 +
.../layout/components/TagsView/index.vue | 294 +
.../layout/components/index.js | 5 +
.../vue-element-admin/layout/index.vue | 102 +
.../layout/mixin/ResizeHandler.js | 45 +
.../Admin/Resources/vue-element-admin/main.js | 56 +
.../Resources/vue-element-admin/permission.js | 74 +
.../vue-element-admin/router/index.js | 178 +
.../router/modules/charts.js | 36 +
.../router/modules/components.js | 78 +
.../Resources/vue-element-admin/settings.js | 42 +
.../vue-element-admin/store/getters.js | 17 +
.../vue-element-admin/store/index.js | 25 +
.../vue-element-admin/store/modules/app.js | 65 +
.../store/modules/errorLog.js | 28 +
.../store/modules/permission.js | 123 +
.../store/modules/settings.js | 36 +
.../store/modules/tagsView.js | 160 +
.../vue-element-admin/store/modules/user.js | 167 +
.../vue-element-admin/styles/btn.scss | 99 +
.../vue-element-admin/styles/common.scss | 22 +
.../vue-element-admin/styles/element-ui.scss | 84 +
.../styles/element-variables.scss | 31 +
.../vue-element-admin/styles/index.scss | 192 +
.../vue-element-admin/styles/mixin.scss | 66 +
.../vue-element-admin/styles/sidebar.scss | 226 +
.../vue-element-admin/styles/transition.scss | 48 +
.../vue-element-admin/styles/variables.scss | 35 +
.../Resources/vue-element-admin/utils/auth.js | 15 +
.../vue-element-admin/utils/clipboard.js | 32 +
.../vue-element-admin/utils/error-log.js | 35 +
.../vue-element-admin/utils/get-page-title.js | 13 +
.../Resources/vue-element-admin/utils/i18n.js | 12 +
.../vue-element-admin/utils/index.js | 374 +
.../vue-element-admin/utils/open-window.js | 25 +
.../vue-element-admin/utils/permission.js | 21 +
.../vue-element-admin/utils/request.js | 127 +
.../vue-element-admin/utils/scroll-to.js | 58 +
.../vue-element-admin/utils/validate.js | 293 +
.../vue-element-admin/vendor/Export2Excel.js | 220 +
.../vue-element-admin/vendor/Export2Zip.js | 24 +
.../views/admin_menus/components/detail.vue | 128 +
.../views/admin_menus/index.vue | 199 +
.../views/admin_roles/index.vue | 489 +
.../views/adminloginlogs/index.vue | 300 +
.../views/adminlogs/index.vue | 283 +
.../views/admins/components/detail.vue | 239 +
.../vue-element-admin/views/admins/index.vue | 366 +
.../article_categories/components/detail.vue | 118 +
.../views/article_categories/index.vue | 179 +
.../article_labels/components/detail.vue | 86 +
.../views/article_labels/index.vue | 199 +
.../articles/components/ArticleDetail.vue | 441 +
.../components/Dropdown/SourceUrl.vue | 38 +
.../articles/components/Dropdown/index.js | 1 +
.../views/articles/detail.vue | 12 +
.../views/articles/index.vue | 466 +
.../views/banners/components/detail.vue | 173 +
.../vue-element-admin/views/banners/index.vue | 369 +
.../views/configs/components/ConfigDetail.vue | 360 +
.../views/configs/detail.vue | 15 +
.../vue-element-admin/views/configs/index.vue | 448 +
.../dashboard/admin/components/BarChart.vue | 102 +
.../dashboard/admin/components/BoxCard.vue | 131 +
.../dashboard/admin/components/LineChart.vue | 139 +
.../dashboard/admin/components/LineMarker.vue | 237 +
.../dashboard/admin/components/PanelGroup.vue | 199 +
.../dashboard/admin/components/PieChart.vue | 122 +
.../dashboard/admin/components/Timeline.vue | 51 +
.../admin/components/mixins/resize.js | 55 +
.../views/dashboard/admin/index.vue | 236 +
.../views/dashboard/index.vue | 28 +
.../views/demo/charts/keyboard.vue | 23 +
.../views/demo/charts/line.vue | 23 +
.../views/demo/charts/mix-chart.vue | 23 +
.../demo/components-demo/avatar-upload.vue | 61 +
.../demo/components-demo/back-to-top.vue | 150 +
.../views/demo/components-demo/count-to.vue | 222 +
.../demo/components-demo/drag-kanban.vue | 66 +
.../demo/components-demo/drag-select.vue | 43 +
.../views/demo/components-demo/dropzone.vue | 29 +
.../demo/components-demo/json-editor.vue | 36 +
.../views/demo/components-demo/markdown.vue | 100 +
.../views/demo/components-demo/mixin.vue | 171 +
.../views/demo/components-demo/tinymce.vue | 36 +
.../views/demo/icons/element-icons.js | 3 +
.../views/demo/icons/index.vue | 101 +
.../views/demo/icons/svg-icons.js | 10 +
.../views/demo/redirect/index.vue | 12 +
.../views/error-page/401.vue | 99 +
.../views/error-page/404.vue | 228 +
.../views/friendlinks/components/detail.vue | 173 +
.../views/friendlinks/index.vue | 367 +
.../views/i18n-demo/index.vue | 171 +
.../views/i18n-demo/local.js | 82 +
.../views/login/auth-redirect.vue | 15 +
.../views/login/components/SocialSignin.vue | 72 +
.../vue-element-admin/views/login/index.vue | 310 +
.../views/profile/components/Account.vue | 66 +
.../views/profile/components/Activity.vue | 188 +
.../views/profile/components/Timeline.vue | 43 +
.../views/profile/components/UserCard.vue | 138 +
.../vue-element-admin/views/profile/index.vue | 68 +
.../views/versions/components/detail.vue | 120 +
.../views/versions/index.vue | 302 +
app/Modules/Admin/Routes/.gitkeep | 0
app/Modules/Admin/Routes/api.php | 18 +
app/Modules/Admin/Routes/web.php | 166 +
app/Modules/Admin/Services/.idea/Services.iml | 9 +
app/Modules/Admin/Services/.idea/misc.xml | 9 +
app/Modules/Admin/Services/.idea/modules.xml | 8 +
app/Modules/Admin/Services/.idea/vcs.xml | 6 +
.../Admin/Services/.idea/workspace.xml | 52 +
.../Admin/Services/AdminLogService.php | 42 +
.../Admin/Services/AdminLoginLogService.php | 47 +
.../Admin/Services/AdminMenuService.php | 31 +
.../Admin/Services/AdminRoleService.php | 135 +
app/Modules/Admin/Services/AdminService.php | 142 +
.../Admin/Services/ArticleCategoryService.php | 63 +
.../Admin/Services/ArticleLabelService.php | 36 +
app/Modules/Admin/Services/ArticleService.php | 104 +
app/Modules/Admin/Services/AuthService.php | 96 +
app/Modules/Admin/Services/BannerService.php | 32 +
app/Modules/Admin/Services/BaseService.php | 153 +
app/Modules/Admin/Services/ConfigService.php | 43 +
.../Admin/Services/FriendlinkService.php | 32 +
app/Modules/Admin/Services/IndexService.php | 229 +
.../Admin/Services/ProtocolService.php | 30 +
app/Modules/Admin/Services/VersionService.php | 28 +
app/Modules/Admin/Tests/Feature/.gitkeep | 0
app/Modules/Admin/Tests/Unit/.gitkeep | 0
app/Modules/Admin/composer.json | 23 +
app/Modules/Admin/module.json | 13 +
app/Modules/Admin/package.json | 17 +
app/Modules/Admin/webpack.admin.config.js | 54 +
app/Modules/Admin/webpack.admin.js | 73 +
app/Modules/Admin/webpack.mix.js | 14 +
app/Providers/AppServiceProvider.php | 29 +
app/Providers/AuthServiceProvider.php | 30 +
app/Providers/BroadcastServiceProvider.php | 21 +
app/Providers/EventServiceProvider.php | 35 +
app/Providers/RouteServiceProvider.php | 63 +
app/Scopes/DeleteScope.php | 31 +
app/Services/Service.php | 37 +
app/Traits/Error.php | 32 +
app/Traits/Instance.php | 34 +
app/Traits/Json.php | 42 +
app/Traits/MysqlTable.php | 58 +
artisan | 53 +
babel.config.js | 14 +
bootstrap/app.php | 55 +
bootstrap/cache/.gitignore | 2 +
composer.json | 73 +
composer.lock | 8424 +++++
config/admin.php | 5 +
config/app.php | 232 +
config/auth.php | 123 +
config/broadcasting.php | 64 +
config/cache.php | 104 +
config/cnpscy.php | 68 +
config/cors.php | 34 +
config/database.php | 147 +
config/debug-server.php | 8 +
config/debugbar.php | 216 +
config/exceptions.php | 117 +
config/filesystems.php | 81 +
config/flare.php | 48 +
config/hashing.php | 52 +
config/ignition.php | 125 +
config/jwt.php | 304 +
config/logging.php | 104 +
config/mail.php | 110 +
config/modules.php | 273 +
config/queue.php | 89 +
config/services.php | 33 +
config/session.php | 201 +
config/tinker.php | 50 +
config/trustedproxy.php | 50 +
config/view.php | 36 +
database/.gitignore | 2 +
database/factories/UserFactory.php | 33 +
database/seeders/DatabaseSeeder.php | 18 +
docker-compose.yml | 74 +
docker/7.4/Dockerfile | 49 +
docker/7.4/php.ini | 4 +
docker/7.4/start-container | 17 +
docker/7.4/supervisord.conf | 11 +
docker/8.0/Dockerfile | 49 +
docker/8.0/php.ini | 4 +
docker/8.0/start-container | 17 +
docker/8.0/supervisord.conf | 11 +
laravel-vue-admin.sql | 13082 ++++++++
modules_statuses.json | 3 +
package-lock.json | 25608 ++++++++++++++++
package.json | 153 +
phpunit.xml | 31 +
public/.htaccess | 21 +
public/demo/home.png | Bin 0 -> 523559 bytes
...6\345\217\262\350\256\260\345\275\225.png" | Bin 0 -> 344816 bytes
...7\347\217\255\347\211\231\350\257\255.png" | Bin 0 -> 164281 bytes
...5\345\277\227\347\273\237\350\256\241.png" | Bin 0 -> 96680 bytes
public/favicon.ico | 0
public/index.php | 55 +
public/robots.txt | 2 +
public/web.config | 28 +
resources/css/app.css | 0
resources/js/app.js | 1 +
resources/js/bootstrap.js | 28 +
resources/lang/en/auth.php | 20 +
resources/lang/en/pagination.php | 19 +
resources/lang/en/passwords.php | 22 +
resources/lang/en/validation.php | 152 +
resources/views/errors/401.blade.php | 5 +
resources/views/errors/403.blade.php | 5 +
resources/views/errors/404.blade.php | 5 +
resources/views/errors/419.blade.php | 5 +
resources/views/errors/429.blade.php | 5 +
resources/views/errors/500.blade.php | 5 +
resources/views/errors/503.blade.php | 5 +
.../views/errors/illustrated-layout.blade.php | 486 +
resources/views/errors/layout.blade.php | 57 +
resources/views/errors/minimal.blade.php | 38 +
.../views/vendor/mail/html/button.blade.php | 19 +
.../views/vendor/mail/html/footer.blade.php | 11 +
.../views/vendor/mail/html/header.blade.php | 11 +
.../views/vendor/mail/html/layout.blade.php | 56 +
.../views/vendor/mail/html/message.blade.php | 27 +
.../views/vendor/mail/html/panel.blade.php | 14 +
.../views/vendor/mail/html/subcopy.blade.php | 7 +
.../views/vendor/mail/html/table.blade.php | 3 +
.../views/vendor/mail/html/themes/default.css | 289 +
.../views/vendor/mail/text/button.blade.php | 1 +
.../views/vendor/mail/text/footer.blade.php | 1 +
.../views/vendor/mail/text/header.blade.php | 1 +
.../views/vendor/mail/text/layout.blade.php | 9 +
.../views/vendor/mail/text/message.blade.php | 27 +
.../views/vendor/mail/text/panel.blade.php | 1 +
.../views/vendor/mail/text/subcopy.blade.php | 1 +
.../views/vendor/mail/text/table.blade.php | 1 +
.../vendor/notifications/email.blade.php | 62 +
.../vendor/pagination/bootstrap-4.blade.php | 46 +
.../views/vendor/pagination/default.blade.php | 46 +
.../vendor/pagination/semantic-ui.blade.php | 36 +
.../pagination/simple-bootstrap-4.blade.php | 27 +
.../pagination/simple-default.blade.php | 19 +
.../pagination/simple-tailwind.blade.php | 25 +
.../vendor/pagination/tailwind.blade.php | 102 +
resources/views/welcome.blade.php | 132 +
routes/api.php | 19 +
routes/channels.php | 18 +
routes/console.php | 19 +
routes/web.php | 18 +
server.php | 21 +
storage/app/.gitignore | 3 +
storage/app/public/.gitignore | 2 +
storage/debugbar/.gitignore | 2 +
storage/framework/.gitignore | 9 +
storage/framework/cache/.gitignore | 3 +
storage/framework/cache/data/.gitignore | 2 +
storage/framework/sessions/.gitignore | 2 +
storage/framework/testing/.gitignore | 2 +
storage/framework/views/.gitignore | 2 +
storage/logs/.gitignore | 2 +
tests/CreatesApplication.php | 22 +
tests/Feature/ExampleTest.php | 21 +
tests/TestCase.php | 10 +
tests/Unit/ExampleTest.php | 18 +
webpack.mix.js | 24 +
536 files changed, 110397 insertions(+)
create mode 100644 .editorconfig
create mode 100644 .env.development
create mode 100644 .env.example
create mode 100644 .env.production
create mode 100644 .gitattributes
create mode 100644 .styleci.yml
create mode 100644 app/Console/Kernel.php
create mode 100644 app/Exceptions/Admin/AuthException.php
create mode 100644 app/Exceptions/Admin/AuthTokenException.php
create mode 100644 app/Exceptions/Exception.php
create mode 100644 app/Exceptions/Handler.php
create mode 100644 app/Exceptions/InternalException.php
create mode 100644 app/Exceptions/InvalidRequestException.php
create mode 100644 app/Helper/builtin-functions.php
create mode 100644 app/Helper/functions.php
create mode 100644 app/Http/Controllers/Controller.php
create mode 100644 app/Http/Kernel.php
create mode 100644 app/Http/Middleware/Authenticate.php
create mode 100644 app/Http/Middleware/ConvertEmptyStringsToNull.php
create mode 100644 app/Http/Middleware/EncryptCookies.php
create mode 100644 app/Http/Middleware/PreventRequestsDuringMaintenance.php
create mode 100644 app/Http/Middleware/RedirectIfAuthenticated.php
create mode 100644 app/Http/Middleware/TrimStrings.php
create mode 100644 app/Http/Middleware/TrustHosts.php
create mode 100644 app/Http/Middleware/TrustProxies.php
create mode 100644 app/Http/Middleware/VerifyCsrfToken.php
create mode 100644 app/Http/Requests/BaseRequest.php
create mode 100644 app/Library/Http.php
create mode 100644 app/Library/SystemInfo.php
create mode 100644 app/Library/cpu_usage.vbs
create mode 100644 app/Library/memory_usage.vbs
create mode 100644 app/Listeners/QueryListener.php
create mode 100644 app/Models/Model.php
create mode 100644 app/Models/MonthModel.php
create mode 100644 app/Models/User.php
create mode 100644 app/Models/YearModel.php
create mode 100644 app/Modules/Admin/Config/.gitkeep
create mode 100644 app/Modules/Admin/Config/config.php
create mode 100644 app/Modules/Admin/Console/.gitkeep
create mode 100644 app/Modules/Admin/Console/AutoTableBuild.php
create mode 100644 app/Modules/Admin/Database/Migrations/.gitkeep
create mode 100644 app/Modules/Admin/Database/Seeders/.gitkeep
create mode 100644 app/Modules/Admin/Database/Seeders/AdminDatabaseSeeder.php
create mode 100644 app/Modules/Admin/Database/factories/.gitkeep
create mode 100644 app/Modules/Admin/Entities/.gitkeep
create mode 100644 app/Modules/Admin/Entities/Article/Article.php
create mode 100644 app/Modules/Admin/Entities/Article/ArticleCategory.php
create mode 100644 app/Modules/Admin/Entities/Article/ArticleLabel.php
create mode 100644 app/Modules/Admin/Entities/Article/ArticleWithLabel.php
create mode 100644 app/Modules/Admin/Entities/Log/AdminLog.php
create mode 100644 app/Modules/Admin/Entities/Log/AdminLoginLog.php
create mode 100644 app/Modules/Admin/Entities/Rabc/Admin.php
create mode 100644 app/Modules/Admin/Entities/Rabc/AdminInfo.php
create mode 100644 app/Modules/Admin/Entities/Rabc/AdminMenu.php
create mode 100644 app/Modules/Admin/Entities/Rabc/AdminRole.php
create mode 100644 app/Modules/Admin/Entities/Rabc/AdminRoleWithMenu.php
create mode 100644 app/Modules/Admin/Entities/Rabc/AdminWithRole.php
create mode 100644 app/Modules/Admin/Entities/System/Banner.php
create mode 100644 app/Modules/Admin/Entities/System/Config.php
create mode 100644 app/Modules/Admin/Entities/System/Friendlink.php
create mode 100644 app/Modules/Admin/Entities/System/Protocol.php
create mode 100644 app/Modules/Admin/Entities/System/Version.php
create mode 100644 app/Modules/Admin/Http/Controllers/.gitkeep
create mode 100644 app/Modules/Admin/Http/Controllers/Article/ArticleCategoryController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Article/ArticleController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Article/ArticleLabelController.php
create mode 100644 app/Modules/Admin/Http/Controllers/AuthController.php
create mode 100644 app/Modules/Admin/Http/Controllers/BaseController.php
create mode 100644 app/Modules/Admin/Http/Controllers/IndexController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Log/AdminLogController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Log/AdminLoginLogController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Rabc/AdminController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Rabc/AdminMenuController.php
create mode 100644 app/Modules/Admin/Http/Controllers/Rabc/AdminRoleController.php
create mode 100644 app/Modules/Admin/Http/Controllers/System/BannerController.php
create mode 100644 app/Modules/Admin/Http/Controllers/System/ConfigController.php
create mode 100644 app/Modules/Admin/Http/Controllers/System/FriendlinkController.php
create mode 100644 app/Modules/Admin/Http/Controllers/System/ProtocolController.php
create mode 100644 app/Modules/Admin/Http/Controllers/System/VersionController.php
create mode 100644 app/Modules/Admin/Http/Controllers/UploadController.php
create mode 100644 app/Modules/Admin/Http/Middleware/.gitkeep
create mode 100644 app/Modules/Admin/Http/Middleware/AdminLog.php
create mode 100644 app/Modules/Admin/Http/Middleware/CheckAuth.php
create mode 100644 app/Modules/Admin/Http/Middleware/CheckRabc.php
create mode 100644 app/Modules/Admin/Http/Requests/.gitkeep
create mode 100644 app/Modules/Admin/Http/Requests/Article/ArticleCategoryRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/Article/ArticleLabelRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/Article/ArticleRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/BaseRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/LoginRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/Rabc/AdminMenuRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/Rabc/AdminRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/Rabc/AdminRoleRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/System/BannerRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/System/ConfigRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/System/FriendlinkRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/System/ProtocolRequest.php
create mode 100644 app/Modules/Admin/Http/Requests/System/VersionRequest.php
create mode 100644 app/Modules/Admin/Providers/.gitkeep
create mode 100644 app/Modules/Admin/Providers/AdminServiceProvider.php
create mode 100644 app/Modules/Admin/Providers/RouteServiceProvider.php
create mode 100644 app/Modules/Admin/Resources/assets/.gitkeep
create mode 100644 app/Modules/Admin/Resources/assets/js/app.js
create mode 100644 app/Modules/Admin/Resources/assets/sass/app.scss
create mode 100644 app/Modules/Admin/Resources/lang/.gitkeep
create mode 100644 app/Modules/Admin/Resources/views/.gitkeep
create mode 100644 app/Modules/Admin/Resources/views/admin.blade.php
create mode 100644 app/Modules/Admin/Resources/views/index.blade.php
create mode 100644 app/Modules/Admin/Resources/views/layouts/master.blade.php
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/App.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/.idea/api.iml
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/.idea/misc.xml
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/.idea/modules.xml
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/.idea/vcs.xml
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/.idea/workspace.xml
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/admin_menus.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/admin_roles.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/adminloginlogs.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/adminlogs.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/admins.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/article_categories.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/article_labels.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/articles.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/banners.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/common.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/configs.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/friendlinks.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/indexs.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/login.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/api/versions.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/assets/401_images/401.gif
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/assets/404_images/404.png
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/assets/404_images/404_cloud.png
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/assets/custom-theme/fonts/element-icons.ttf
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/assets/custom-theme/fonts/element-icons.woff
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/assets/custom-theme/index.css
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/BackToTop/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Breadcrumb/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Charts/Keyboard.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Charts/LineMarker.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Charts/MixChart.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Charts/mixins/resize.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/DndList/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/DragSelect/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Dropzone/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ErrorLog/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/GithubCorner/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Hamburger/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/HeaderSearch/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ImageCropper/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ImageCropper/utils/data2blob.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ImageCropper/utils/effectRipple.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ImageCropper/utils/language.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ImageCropper/utils/mimes.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/JsonEditor/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Kanban/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/LangSelect/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/MDinput/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/MarkdownEditor/default-options.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/MarkdownEditor/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Pagination/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/PanThumb/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/RightPanel/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Screenfull/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Share/DropdownMenu.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/SizeSelect/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Sticky/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/SvgIcon/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/TextHoverEffect/Mallki.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/ThemePicker/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Tinymce/components/EditorImage.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Tinymce/dynamicLoadScript.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Tinymce/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Tinymce/plugins.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Tinymce/toolbar.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Upload/SingleImage.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Upload/SingleImage2.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Upload/SingleImage3.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/UploadExcel/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Uploads/image/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Uploads/image/utils/data2blob.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Uploads/image/utils/effectRipple.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Uploads/image/utils/language.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/components/Uploads/image/utils/mimes.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/clipboard/clipboard.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/clipboard/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/el-drag-dialog/drag.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/el-drag-dialog/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/el-table/adaptive.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/el-table/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/permission/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/permission/permission.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/sticky.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/waves/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/waves/waves.css
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/directive/waves/waves.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/filters/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/404.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/admin.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/bug.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/chart.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/clipboard.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/comment.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/component.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/create-user.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/dashboard.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/documentation.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/dollar.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/drag.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/edit.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/education.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/email.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/example.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/excel.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/exit-fullscreen.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/eye-open.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/eye.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/form.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/fullscreen.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/guide 2.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/guide.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/icon.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/international.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/language.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/layout.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/like.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/link.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/list.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/lock.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/message.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/money.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/nested.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/password.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/pdf.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/people.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/peoples.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/qq.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/role.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/search.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/shopping.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/size.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/skill.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/star.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/tab.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/table.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/theme.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/tree-table.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/tree.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/user.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/wechat.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svg/zip.svg
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/icons/svgo.yml
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/lang/en.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/lang/es.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/lang/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/lang/ja.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/lang/zh.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/AppMain.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Navbar.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Settings/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Sidebar/FixiOSBug.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Sidebar/Item.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Sidebar/Link.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Sidebar/Logo.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Sidebar/SidebarItem.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/Sidebar/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/TagsView/ScrollPane.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/TagsView/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/components/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/layout/mixin/ResizeHandler.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/main.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/permission.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/router/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/router/modules/charts.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/router/modules/components.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/settings.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/getters.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/modules/app.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/modules/errorLog.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/modules/permission.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/modules/settings.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/modules/tagsView.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/store/modules/user.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/btn.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/common.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/element-ui.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/element-variables.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/index.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/mixin.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/sidebar.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/transition.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/styles/variables.scss
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/auth.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/clipboard.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/error-log.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/get-page-title.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/i18n.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/open-window.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/permission.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/request.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/scroll-to.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/utils/validate.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/vendor/Export2Excel.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/vendor/Export2Zip.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/admin_menus/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/admin_menus/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/admin_roles/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/adminloginlogs/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/adminlogs/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/admins/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/admins/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/article_categories/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/article_categories/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/article_labels/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/article_labels/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/articles/components/ArticleDetail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/articles/components/Dropdown/SourceUrl.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/articles/components/Dropdown/index.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/articles/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/articles/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/banners/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/banners/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/configs/components/ConfigDetail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/configs/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/configs/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/BarChart.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/BoxCard.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/LineChart.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/LineMarker.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/PanelGroup.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/PieChart.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/Timeline.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/components/mixins/resize.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/admin/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/dashboard/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/charts/keyboard.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/charts/line.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/charts/mix-chart.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/avatar-upload.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/back-to-top.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/count-to.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/drag-kanban.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/drag-select.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/dropzone.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/json-editor.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/markdown.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/mixin.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/components-demo/tinymce.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/icons/element-icons.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/icons/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/icons/svg-icons.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/demo/redirect/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/error-page/401.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/error-page/404.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/friendlinks/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/friendlinks/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/i18n-demo/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/i18n-demo/local.js
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/login/auth-redirect.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/login/components/SocialSignin.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/login/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/profile/components/Account.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/profile/components/Activity.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/profile/components/Timeline.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/profile/components/UserCard.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/profile/index.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/versions/components/detail.vue
create mode 100644 app/Modules/Admin/Resources/vue-element-admin/views/versions/index.vue
create mode 100644 app/Modules/Admin/Routes/.gitkeep
create mode 100644 app/Modules/Admin/Routes/api.php
create mode 100644 app/Modules/Admin/Routes/web.php
create mode 100644 app/Modules/Admin/Services/.idea/Services.iml
create mode 100644 app/Modules/Admin/Services/.idea/misc.xml
create mode 100644 app/Modules/Admin/Services/.idea/modules.xml
create mode 100644 app/Modules/Admin/Services/.idea/vcs.xml
create mode 100644 app/Modules/Admin/Services/.idea/workspace.xml
create mode 100644 app/Modules/Admin/Services/AdminLogService.php
create mode 100644 app/Modules/Admin/Services/AdminLoginLogService.php
create mode 100644 app/Modules/Admin/Services/AdminMenuService.php
create mode 100644 app/Modules/Admin/Services/AdminRoleService.php
create mode 100644 app/Modules/Admin/Services/AdminService.php
create mode 100644 app/Modules/Admin/Services/ArticleCategoryService.php
create mode 100644 app/Modules/Admin/Services/ArticleLabelService.php
create mode 100644 app/Modules/Admin/Services/ArticleService.php
create mode 100644 app/Modules/Admin/Services/AuthService.php
create mode 100644 app/Modules/Admin/Services/BannerService.php
create mode 100644 app/Modules/Admin/Services/BaseService.php
create mode 100644 app/Modules/Admin/Services/ConfigService.php
create mode 100644 app/Modules/Admin/Services/FriendlinkService.php
create mode 100644 app/Modules/Admin/Services/IndexService.php
create mode 100644 app/Modules/Admin/Services/ProtocolService.php
create mode 100644 app/Modules/Admin/Services/VersionService.php
create mode 100644 app/Modules/Admin/Tests/Feature/.gitkeep
create mode 100644 app/Modules/Admin/Tests/Unit/.gitkeep
create mode 100644 app/Modules/Admin/composer.json
create mode 100644 app/Modules/Admin/module.json
create mode 100644 app/Modules/Admin/package.json
create mode 100644 app/Modules/Admin/webpack.admin.config.js
create mode 100644 app/Modules/Admin/webpack.admin.js
create mode 100644 app/Modules/Admin/webpack.mix.js
create mode 100644 app/Providers/AppServiceProvider.php
create mode 100644 app/Providers/AuthServiceProvider.php
create mode 100644 app/Providers/BroadcastServiceProvider.php
create mode 100644 app/Providers/EventServiceProvider.php
create mode 100644 app/Providers/RouteServiceProvider.php
create mode 100644 app/Scopes/DeleteScope.php
create mode 100644 app/Services/Service.php
create mode 100644 app/Traits/Error.php
create mode 100644 app/Traits/Instance.php
create mode 100644 app/Traits/Json.php
create mode 100644 app/Traits/MysqlTable.php
create mode 100644 artisan
create mode 100644 babel.config.js
create mode 100644 bootstrap/app.php
create mode 100644 bootstrap/cache/.gitignore
create mode 100644 composer.json
create mode 100644 composer.lock
create mode 100644 config/admin.php
create mode 100644 config/app.php
create mode 100644 config/auth.php
create mode 100644 config/broadcasting.php
create mode 100644 config/cache.php
create mode 100644 config/cnpscy.php
create mode 100644 config/cors.php
create mode 100644 config/database.php
create mode 100644 config/debug-server.php
create mode 100644 config/debugbar.php
create mode 100644 config/exceptions.php
create mode 100644 config/filesystems.php
create mode 100644 config/flare.php
create mode 100644 config/hashing.php
create mode 100644 config/ignition.php
create mode 100644 config/jwt.php
create mode 100644 config/logging.php
create mode 100644 config/mail.php
create mode 100644 config/modules.php
create mode 100644 config/queue.php
create mode 100644 config/services.php
create mode 100644 config/session.php
create mode 100644 config/tinker.php
create mode 100644 config/trustedproxy.php
create mode 100644 config/view.php
create mode 100644 database/.gitignore
create mode 100644 database/factories/UserFactory.php
create mode 100644 database/seeders/DatabaseSeeder.php
create mode 100644 docker-compose.yml
create mode 100644 docker/7.4/Dockerfile
create mode 100644 docker/7.4/php.ini
create mode 100644 docker/7.4/start-container
create mode 100644 docker/7.4/supervisord.conf
create mode 100644 docker/8.0/Dockerfile
create mode 100644 docker/8.0/php.ini
create mode 100644 docker/8.0/start-container
create mode 100644 docker/8.0/supervisord.conf
create mode 100644 laravel-vue-admin.sql
create mode 100644 modules_statuses.json
create mode 100644 package-lock.json
create mode 100644 package.json
create mode 100644 phpunit.xml
create mode 100644 public/.htaccess
create mode 100644 public/demo/home.png
create mode 100644 "public/demo/\347\211\210\346\234\254\345\216\206\345\217\262\350\256\260\345\275\225.png"
create mode 100644 "public/demo/\350\245\277\347\217\255\347\211\231\350\257\255.png"
create mode 100644 "public/demo/\350\257\267\346\261\202\346\227\245\345\277\227\347\273\237\350\256\241.png"
create mode 100644 public/favicon.ico
create mode 100644 public/index.php
create mode 100644 public/robots.txt
create mode 100644 public/web.config
create mode 100644 resources/css/app.css
create mode 100644 resources/js/app.js
create mode 100644 resources/js/bootstrap.js
create mode 100644 resources/lang/en/auth.php
create mode 100644 resources/lang/en/pagination.php
create mode 100644 resources/lang/en/passwords.php
create mode 100644 resources/lang/en/validation.php
create mode 100644 resources/views/errors/401.blade.php
create mode 100644 resources/views/errors/403.blade.php
create mode 100644 resources/views/errors/404.blade.php
create mode 100644 resources/views/errors/419.blade.php
create mode 100644 resources/views/errors/429.blade.php
create mode 100644 resources/views/errors/500.blade.php
create mode 100644 resources/views/errors/503.blade.php
create mode 100644 resources/views/errors/illustrated-layout.blade.php
create mode 100644 resources/views/errors/layout.blade.php
create mode 100644 resources/views/errors/minimal.blade.php
create mode 100644 resources/views/vendor/mail/html/button.blade.php
create mode 100644 resources/views/vendor/mail/html/footer.blade.php
create mode 100644 resources/views/vendor/mail/html/header.blade.php
create mode 100644 resources/views/vendor/mail/html/layout.blade.php
create mode 100644 resources/views/vendor/mail/html/message.blade.php
create mode 100644 resources/views/vendor/mail/html/panel.blade.php
create mode 100644 resources/views/vendor/mail/html/subcopy.blade.php
create mode 100644 resources/views/vendor/mail/html/table.blade.php
create mode 100644 resources/views/vendor/mail/html/themes/default.css
create mode 100644 resources/views/vendor/mail/text/button.blade.php
create mode 100644 resources/views/vendor/mail/text/footer.blade.php
create mode 100644 resources/views/vendor/mail/text/header.blade.php
create mode 100644 resources/views/vendor/mail/text/layout.blade.php
create mode 100644 resources/views/vendor/mail/text/message.blade.php
create mode 100644 resources/views/vendor/mail/text/panel.blade.php
create mode 100644 resources/views/vendor/mail/text/subcopy.blade.php
create mode 100644 resources/views/vendor/mail/text/table.blade.php
create mode 100644 resources/views/vendor/notifications/email.blade.php
create mode 100644 resources/views/vendor/pagination/bootstrap-4.blade.php
create mode 100644 resources/views/vendor/pagination/default.blade.php
create mode 100644 resources/views/vendor/pagination/semantic-ui.blade.php
create mode 100644 resources/views/vendor/pagination/simple-bootstrap-4.blade.php
create mode 100644 resources/views/vendor/pagination/simple-default.blade.php
create mode 100644 resources/views/vendor/pagination/simple-tailwind.blade.php
create mode 100644 resources/views/vendor/pagination/tailwind.blade.php
create mode 100644 resources/views/welcome.blade.php
create mode 100644 routes/api.php
create mode 100644 routes/channels.php
create mode 100644 routes/console.php
create mode 100644 routes/web.php
create mode 100644 server.php
create mode 100644 storage/app/.gitignore
create mode 100644 storage/app/public/.gitignore
create mode 100644 storage/debugbar/.gitignore
create mode 100644 storage/framework/.gitignore
create mode 100644 storage/framework/cache/.gitignore
create mode 100644 storage/framework/cache/data/.gitignore
create mode 100644 storage/framework/sessions/.gitignore
create mode 100644 storage/framework/testing/.gitignore
create mode 100644 storage/framework/views/.gitignore
create mode 100644 storage/logs/.gitignore
create mode 100644 tests/CreatesApplication.php
create mode 100644 tests/Feature/ExampleTest.php
create mode 100644 tests/TestCase.php
create mode 100644 tests/Unit/ExampleTest.php
create mode 100644 webpack.mix.js
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..df52c32
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,19 @@
+root = true
+
+[*]
+; 设定文件编码
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+; 使用空格来做缩进
+indent_style = space
+; 缩进长度为两个空格
+indent_size = 4
+; 移除文件尾部多余的空格
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+indent_size = 2
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..de583d0
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,5 @@
+# just a flag
+ENV = 'development'
+
+# base api
+VUE_APP_BASE_API = '/dev-api'
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..7492cb4
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,60 @@
+APP_NAME=Laravel
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=true
+APP_URL=http://localhost
+
+LOG_CHANNEL=daily
+LOG_LEVEL=debug
+
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=laravel
+DB_USERNAME=root
+DB_PASSWORD=
+DB_PREFIX=cnpscy_
+
+BROADCAST_DRIVER=log
+CACHE_DRIVER=file
+QUEUE_CONNECTION=sync
+SESSION_DRIVER=file
+SESSION_LIFETIME=120
+
+# MEMCACHED_HOST=127.0.0.1
+MEMCACHED_HOST=memcached
+
+# REDIS_HOST=127.0.0.1
+REDIS_HOST=redis
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_MAILER=smtp
+MAIL_HOST=mailhog
+MAIL_PORT=1025
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_ENCRYPTION=null
+MAIL_FROM_ADDRESS=null
+MAIL_FROM_NAME="${APP_NAME}"
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+
+PUSHER_APP_ID=
+PUSHER_APP_KEY=
+PUSHER_APP_SECRET=
+PUSHER_APP_CLUSTER=mt1
+
+MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
+MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+
+#filesystem
+FILESYSTEM_DRIVER=public
+
+APP_TIMEZONE=Asia/Shanghai
+APP_LOCALE=zh-CN
+
+JWT_SECRET=uuKSCoJpNKumzY7xOdjfpAxF0MMuI5j98iaO4uk3JVwbFCUapvsB7XGohKOBjd92
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..80c8103
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,6 @@
+# just a flag
+ENV = 'production'
+
+# base api
+VUE_APP_BASE_API = '/prod-api'
+
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..967315d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+* text=auto
+*.css linguist-vendored
+*.scss linguist-vendored
+*.js linguist-vendored
+CHANGELOG.md export-ignore
diff --git a/.gitignore b/.gitignore
index 6552ddf..1501035 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,13 @@ storage/*.key
Homestead.yaml
Homestead.json
/.vagrant
+
+
+# vue-webpack
+public/css
+public/fonts
+public/images
+public/js
+
+# other
+demos-blog
\ No newline at end of file
diff --git a/.styleci.yml b/.styleci.yml
new file mode 100644
index 0000000..9231873
--- /dev/null
+++ b/.styleci.yml
@@ -0,0 +1,13 @@
+php:
+ preset: laravel
+ disabled:
+ - no_unused_imports
+ finder:
+ not-name:
+ - index.php
+ - server.php
+js:
+ finder:
+ not-name:
+ - webpack.mix.js
+css: true
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
new file mode 100644
index 0000000..c24df94
--- /dev/null
+++ b/app/Console/Kernel.php
@@ -0,0 +1,57 @@
+command('inspire')->hourly();
+
+ // var_dump('schedule:' . date('Y-m-d H:i:s'));
+
+ // 每月1号调用:按月分表自动生成
+ $schedule->command('autotablebuild')->monthlyOn();
+ }
+
+ /**
+ * Register the commands for the application.
+ *
+ * @return void
+ */
+ protected function commands()
+ {
+ $this->load(__DIR__.'/Commands');
+
+ /**
+ * 自动加载多模块的自定义命令行
+ */
+ $modules_path = config('modules.paths.modules');
+ if ($dirs = get_dir_files($modules_path)){
+ foreach ($dirs as $dir){
+ if (is_dir($console_path = $modules_path . '/' . $dir . '/Console'))
+ $this->load($console_path = $modules_path . '/' . $dir . '/Console');
+ }
+ }
+
+ require base_path('routes/console.php');
+ }
+}
diff --git a/app/Exceptions/Admin/AuthException.php b/app/Exceptions/Admin/AuthException.php
new file mode 100644
index 0000000..dbac672
--- /dev/null
+++ b/app/Exceptions/Admin/AuthException.php
@@ -0,0 +1,28 @@
+admin_id = $admin_id;
+ }
+
+ public function render(Request $request)
+ {
+ if ($request->expectsJson()) {
+ // 登录日志
+ AdminLoginLog::getInstance()->add($this->admin_id, 0, $this->msg);
+
+ $this->setHttpCode(401);
+ return $this->errorJson($this->msg);
+ }
+ }
+}
diff --git a/app/Exceptions/Admin/AuthTokenException.php b/app/Exceptions/Admin/AuthTokenException.php
new file mode 100644
index 0000000..70d58bd
--- /dev/null
+++ b/app/Exceptions/Admin/AuthTokenException.php
@@ -0,0 +1,28 @@
+admin_id = $admin_id;
+ }
+
+ public function render(Request $request)
+ {
+ if ($request->expectsJson()) {
+ // 登录日志
+ AdminLoginLog::getInstance()->add($this->admin_id, 0, $this->msg);
+
+ $this->setHttpCode(401);
+ return $this->errorJson($this->msg);
+ }
+ }
+}
diff --git a/app/Exceptions/Exception.php b/app/Exceptions/Exception.php
new file mode 100644
index 0000000..a8bf4b2
--- /dev/null
+++ b/app/Exceptions/Exception.php
@@ -0,0 +1,20 @@
+msg = $message;
+ }
+}
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
new file mode 100644
index 0000000..48a5099
--- /dev/null
+++ b/app/Exceptions/Handler.php
@@ -0,0 +1,67 @@
+reportable(function (Throwable $e) {
+
+ });
+
+ }
+
+ /**
+ * Render an exception into an HTTP response.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Throwable $exception
+ * @return \Symfony\Component\HttpFoundation\Response
+ *
+ * @throws \Throwable
+ */
+ public function render($request, Throwable $exception)
+ {
+ // 验证器类的错误监听
+ if($exception instanceof \Illuminate\Validation\ValidationException){
+ return $this->errorJson($exception->validator->errors()->first());
+ }
+
+ return parent::render($request, $exception);
+ }
+}
diff --git a/app/Exceptions/InternalException.php b/app/Exceptions/InternalException.php
new file mode 100644
index 0000000..b9429b2
--- /dev/null
+++ b/app/Exceptions/InternalException.php
@@ -0,0 +1,21 @@
+msg = $msg;
+ }
+
+ public function render(Request $request)
+ {
+ if ($request->expectsJson()) {
+ return response()->json(['msg' => $this->msg], $this->code);
+ }
+ }
+}
diff --git a/app/Exceptions/InvalidRequestException.php b/app/Exceptions/InvalidRequestException.php
new file mode 100644
index 0000000..954e53a
--- /dev/null
+++ b/app/Exceptions/InvalidRequestException.php
@@ -0,0 +1,20 @@
+expectsJson()) {
+ return $this->errorJson($this->msg);
+ }
+ }
+}
diff --git a/app/Helper/builtin-functions.php b/app/Helper/builtin-functions.php
new file mode 100644
index 0000000..664c1b8
--- /dev/null
+++ b/app/Helper/builtin-functions.php
@@ -0,0 +1,292 @@
+roles;
+ $rabc = $menu_lists = [];
+ foreach ($roles as $key => $role) {
+ $menus = $role->{$with_func}->toArray();
+ $rabc[$role->role_id] = array_flip(array_unique(array_column($menus, 'menu_url')));
+ $menu_lists = array_merge($menu_lists, $menus);
+ }
+ return ['rabc' => $rabc, 'menu' => $menu_lists];
+}
+
+function get_request_post()
+{
+ return request()->isMethod('post');
+}
+
+function cnpscy_config(string $config_name = '', string $default = '')
+{
+ return config('cnpscy.' . $config_name, $default);
+}
+
+//快速修改.env文件
+function modifyEnv(array $data)
+{
+ $envPath = base_path() . DIRECTORY_SEPARATOR . '.env';
+ $contentArray = collect(file($envPath, FILE_IGNORE_NEW_LINES));
+ $contentArray->transform(function ($item) use ($data)
+ {
+ foreach ($data as $key => $value) {
+ if (str_contains($item, $key)) {
+ return $key . '=' . $value;
+ }
+ }
+ return $item;
+ });
+ $content = implode($contentArray->toArray(), "\n");
+ File::put($envPath, $content);
+}
+
+/*************** 缓存函数 开始 ***************/
+
+/**
+ * [set_cache]
+ *
+ * @param [type] $key [description]
+ * @param [type] $data [description]
+ * @param [type] $minutes [description]$minutes = 7*24*60*60
+ *
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :设置缓存
+ * @englishAnnotation :
+ */
+function set_cache($key, $data, $minutes = 1 * 60)
+{
+ return \Cache::put($key, $data, $minutes);
+}
+
+/**
+ * [get_cache]
+ *
+ * @param [type] $key [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :获取缓存的数据
+ * @englishAnnotation :
+ */
+function get_cache($key)
+{
+ return \Cache::get($key) ?? '';
+}
+
+/**
+ * [has_cache]
+ *
+ * @param [type] $key [description]
+ *
+ * @return boolean [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :是否存在该key的缓存
+ * @englishAnnotation :
+ */
+function has_cache($key)
+{
+ return \Cache::has($key) ? true : false;
+}
+
+/**
+ * [del_cache]
+ *
+ * @param [type] $key [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :删除缓存
+ * @englishAnnotation :
+ */
+function del_cache($key)
+{
+ return \Cache::forget($key) ?? false;
+}
+
+/*************** 缓存函数 结束 ***************/
+
+
+/**
+ * [logs_data]
+ *
+ * @param [type] $data [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :日志数据的过滤
+ * @englishAnnotation :
+ */
+function logs_data($data, $ip_agent)
+{
+ $data['data'] = json_encode(request()->all());
+ $data['action'] = request()->route()->getActionName();
+ $data['description'] = $data['description'] ?? '';
+ $data['add_time'] = $data['add_time'] ?? time();
+ $data['ip'] = $ip_agent['ip'] ?? get_ip();
+ $data['browser_type'] = $ip_agent['agent'] ?? $_SERVER['HTTP_USER_AGENT'];
+ return $data;
+}
+
+/**
+ * 刷新用户权限、角色
+ * 多角色管理
+ */
+if (!function_exists('setAdminRabc')) {
+ function setAdminRabc()
+ {
+ $admin = auth('admin')->user();
+ $roles = $menus = [];
+ $rolesResources = $admin->roles()->orderBy('role_id', 'ASC')->get();
+ foreach ($rolesResources as $key => $roleResources) {
+ if ($key == 0) $role_id = $roleResources->role_id;
+ $roles[$roleResources->role_id] = $roleResources;
+ $menus[$roleResources->role_id] = array_filter($roleResources->left_menus()->get()->pluck('api_url')->toArray());
+ }
+
+ //默认使用第一个角色
+ if (empty($admin->use_role)) \App\Models\BasicAdmin\Admin::where('admin_id', $admin->admin_id)->update(['use_role' => $role_id]);
+
+ // 缓存用户权限
+ cache()->forever('admin_rabc_' . $admin->admin_id, [
+ 'roles' => $roles,
+ 'menus' => $menus
+ ]);
+ }
+}
+
+/**
+ * 获取当前用户-当前角色-权限
+ */
+if (!function_exists('getAdminRabc')) {
+ function getAdminRabc()
+ {
+ $admin = auth('admin')->user();
+ $key = 'admin_rabc_' . $admin->admin_id;
+ if (cache()->has($key)) {
+ $admin_rabc = cache($key);
+ return [
+ 'roles' => empty($admin->use_role) ? [] : $admin_rabc['roles'][$admin->use_role],
+ 'menus' => empty($admin->use_role) ? [] : $admin_rabc['menus'][$admin->use_role],
+ ];
+ }
+ setAdminRabc();
+ return cache($key);
+ }
+}
+
+
+function getWebUserHome(int $user_id = 0)
+{
+ return url('user/' . $user_id);
+}
+
+function route_class()
+{
+ return str_replace('.', '-', Route::currentRouteName());
+}
+
+function category_nav_active($category_id)
+{
+ return active_class((if_route('categories.show') && if_route_param('category', $category_id)));
+}
+
+function make_excerpt($value, $length = 200)
+{
+ $excerpt = trim(preg_replace('/\r\n|\r|\n+/', ' ', strip_tags($value)));
+ return Str::limit($excerpt, $length);
+}
+
+function model_admin_link($title, $model)
+{
+ return model_link($title, $model, 'admin');
+}
+
+function model_link($title, $model, $prefix = '')
+{
+ // 获取数据模型的复数蛇形命名
+ $model_name = model_plural_name($model);
+
+ // 初始化前缀
+ $prefix = $prefix ? "/$prefix/" : '/';
+
+ // 使用站点 URL 拼接全量 URL
+ $url = config('app.url') . $prefix . $model_name . '/' . $model->id;
+
+ // 拼接 HTML A 标签,并返回
+ return '' . $title . '';
+}
+
+function model_plural_name($model)
+{
+ // 从实体中获取完整类名,例如:App\Models\User
+ $full_class_name = get_class($model);
+
+ // 获取基础类名,例如:传参 `App\Models\User` 会得到 `User`
+ $class_name = class_basename($full_class_name);
+
+ // 蛇形命名,例如:传参 `User` 会得到 `user`, `FooBar` 会得到 `foo_bar`
+ $snake_case_name = Str::snake($class_name);
+
+ // 获取子串的复数形式,例如:传参 `user` 会得到 `users`
+ return Str::plural($snake_case_name);
+}
+
+function get_web_admin_url(string $method, string $controller = '', int $is_compel = 0): string
+{
+ if (empty($method)) return '';
+ $prefix = ltrim(request()->route()->getPrefix(), '/');
+ // 如果后台访问地址对不上,关闭下行的注释
+ // $prefix = str_replace(head(explode('/', $prefix)), cnpscy_config('web_admin_prefix'), $prefix);
+ if (empty($controller) && $is_compel == 0) {
+ $url = $prefix . '/' . ltrim($method, '/');
+ } else {
+ $url = str_replace(last(explode('/', $prefix)), $controller, $prefix) . (empty($controller) ? '' : '/') . ltrim($method, '/');
+ }
+ return url($url);
+}
+
+function get_api_admin_url(string $method, string $controller = '', int $is_compel = 0)
+{
+ if (empty($method)) return '';
+ $prefix = ltrim(request()->route()->getPrefix(), '/');
+ $prefix = str_replace(head(explode('/', $prefix)), cnpscy_config('api_admin_prefix'), $prefix);
+ if (empty($controller) && $is_compel == 0) {
+ $url = 'api/' . $prefix . '/' . ltrim($method, '/');
+ } else {
+ $url = 'api/' . str_replace(last(explode('/', $prefix)), $controller, $prefix) . (empty($controller) ? '' : '/') . ltrim($method, '/');
+ }
+ return url($url);
+}
+
+function get_model_url(string $method, string $controller = '', int $is_compel = 0)
+{
+ if (empty($method)) return url('/');
+ $prefix = ltrim(request()->route()->getPrefix(), '/');
+ if (empty($controller) && $is_compel == 0) {
+ $url = $prefix . '/' . ltrim($method, '/');
+ } else {
+ $url = str_replace(last(explode('/', $prefix)), $controller, $prefix) . (empty($controller) ? '' : '/') . $method;
+ }
+ return url($url);
+}
+
+function getControllerAndFunction()
+{
+ $action = \Route::current()->getActionName();
+ list($class, $method) = explode('@', $action);
+ $class = substr(strrchr($class, '\\'), 1);
+ return ['controller' => $class, 'method' => $method];
+}
+
+function getControllerRoutePrefix()
+{
+ $controller = getControllerAndFunction()['controller'] ?? '';
+ return strtolower(str_replace('Controller', '', $controller));
+}
diff --git a/app/Helper/functions.php b/app/Helper/functions.php
new file mode 100644
index 0000000..6148d07
--- /dev/null
+++ b/app/Helper/functions.php
@@ -0,0 +1,3953 @@
+= $maxLimit ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+
+if ( !function_exists('psQty_num') ) {
+ /**
+ * 获取指定进程的数量
+ *
+ * @param string $search
+ *
+ * @return int
+ */
+ function psQty_num(string $search)
+ {
+ return count(psQty_res($search));
+ }
+}
+
+if ( !function_exists('run_exec') ) {
+ function run_exec(string $command_name)
+ {
+ $command = 'php ' . $command_name;
+
+ $output = 0;
+
+ $return_val = '';
+
+ exec($command, $output, $return_val);
+
+ return $output;
+ }
+}
+
+if ( !function_exists('make_signature') ) {
+ /**
+ * 生成签名
+ *
+ * @param array $args
+ * @param string $key
+ *
+ * @return string
+ */
+ function make_signature(array $args, string $key)
+ {
+ //排序
+ ksort($args);
+ //生成sign
+ $str = urldecode(http_build_query($args)) . '&key=' . $key;
+ $sign = strtoupper(md5($str));
+ return $sign;
+ }
+}
+
+if ( !function_exists('get_difference_hours') ) {
+ /**
+ * 计算两个时间戳之间相差的小时
+ *
+ * @param int $start_time 开始时间戳
+ * @param int $end_time 结束时间戳
+ *
+ * @return int
+ */
+ function get_difference_hours(int $start_time, int $end_time):float
+ {
+ if ( $start_time < $end_time ) {
+ $starttime = $start_time;
+ $endtime = $end_time;
+ } else {
+ $starttime = $end_time;
+ $endtime = $start_time;
+ }
+ //计算小时
+ $timediff = $endtime - $starttime;
+ return floatval($timediff / 3600);
+ }
+}
+
+if ( !function_exists('array_merge_multiple') ) {
+ /**
+ * 多维数组合并
+ *
+ * @param $array1
+ * @param $array2
+ *
+ * @return array
+ */
+ function array_merge_multiple($array1, $array2)
+ {
+ $merge = $array1 + $array2;
+ $data = [];
+ foreach ($merge as $key => $val) {
+ if ( isset($array1[$key]) && is_array($array1[$key]) && isset($array2[$key]) && is_array($array2[$key]) ) {
+ $data[$key] = array_merge_multiple($array1[$key], $array2[$key]);
+ } else {
+ $data[$key] = isset($array2[$key]) ? $array2[$key] : $array1[$key];
+ }
+ }
+ return $data;
+ }
+}
+
+if ( !function_exists('export_excel') ) {
+ /**
+ * 数据导出到excel(csv文件)
+ *
+ * @param $fileName
+ * @param array $tileArray
+ * @param array $dataArray
+ */
+ function export_excel($fileName, $tileArray = [], $dataArray = [])
+ {
+ ini_set('memory_limit', '1024M');
+ ini_set('max_execution_time', 0);
+ ob_end_clean();
+ ob_start();
+ header("Content-Type: text/csv");
+ header("Content-Disposition:filename=" . $fileName);
+ $fp = fopen('php://output', 'w');
+ fwrite($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));// 转码 防止乱码(比如微信昵称)
+ fputcsv($fp, $tileArray);
+ $index = 0;
+ foreach ($dataArray as $item) {
+ if ( $index == 1000 ) {
+ $index = 0;
+ ob_flush();
+ flush();
+ }
+ $index++;
+ fputcsv($fp, $item);
+ }
+ ob_flush();
+ flush();
+ ob_end_clean();
+ }
+}
+
+if ( !function_exists('is_json') ) {
+ /**
+ * 判断字符串是否为 Json 格式
+ *
+ * @param string $data Json 字符串
+ * @param bool $assoc 是否返回关联数组。默认返回对象
+ *
+ * @return array|bool|object 成功返回转换后的对象或数组,失败返回 false
+ */
+ function is_json($data = '', $assoc = false)
+ {
+ if ( PHP_VERSION > 5.3 ) {
+ json_decode($data);
+ return (json_last_error() == JSON_ERROR_NONE);
+ } else {
+ $data = json_decode($data, $assoc);
+ if ( ($data && is_object($data)) || (is_array($data) && !empty($data)) ) {
+ return $data;
+ }
+ return false;
+ }
+ }
+}
+
+if ( function_exists('get_request_method') ) {
+ function get_request_method() : string
+ {
+ return strtoupper($_SERVER['REQUEST_METHOD'] ?? '');
+ }
+}
+
+
+if (!function_exists('my_json_encode') ) {
+ /**
+ * 统一的json_encode
+ *
+ * @param array $data
+ * @param string $options
+ *
+ * @return false|string
+ */
+ function my_json_encode($data, string $options = '')
+ {
+ //$data = is_object($data) ? (array)$data : $data;
+ return json_encode($data, empty($options) ? (JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $options);
+ }
+}
+
+
+if ( !function_exists('my_json_decode') ) {
+ /**
+ * 统一的 json_decode
+ *
+ * @param string $string
+ * @param bool $assoc
+ *
+ * @return mixed
+ */
+ function my_json_decode(string $string, bool $assoc = true)
+ {
+ return json_decode($string, $assoc);
+ }
+}
+
+/**
+ * 输出xml字符
+ */
+function ToXml($arr = [])
+{
+ if ( !is_array($arr) || count($arr) <= 0 ) {
+ exception("数组数据异常!");
+ }
+
+ $xml = "";
+ foreach ($arr as $key => $val) {
+ if ( is_numeric($val) ) {
+ $xml .= "<" . $key . ">" . $val . "" . $key . ">";
+ } else {
+ $xml .= "<" . $key . ">" . $key . ">";
+ }
+ }
+ $xml .= "";
+ return $xml;
+}
+
+/**
+ * 将xml转为array
+ */
+function xml_to_array($xml)
+{
+ if ( !$xml ) {
+ \Exception("xml数据异常!");
+ }
+
+ // 解决部分json数据误入的问题
+ $arr = json_decode($xml, true);
+ if ( is_array($arr) && !empty($arr) ) {
+ return $arr;
+ }
+ // 将XML转为array
+ $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
+ return $arr;
+}
+
+// 生成签名
+function make_sign($paraMap = [], $partner_key = '')
+{
+ $buff = "";
+ ksort($paraMap);
+ $paraMap['key'] = $partner_key;
+ foreach ($paraMap as $k => $v) {
+ if ( null != $v && "null" != $v && '' != $v && "sign" != $k ) {
+ $buff .= $k . "=" . $v . "&";
+ }
+ }
+ $reqPar = '';
+ if ( strlen($buff) > 0 ) {
+ $reqPar = substr($buff, 0, strlen($buff) - 1);
+ }
+
+ return strtoupper(md5($reqPar));
+}
+
+/**
+ * 前端获取后端的数据结果集
+ *
+ * @param $data
+ *
+ * @return string
+ */
+function html_get_res_from_admin($data)
+{
+ return addslashes(my_json_encode($data));
+}
+
+if ( !function_exists('axios_request') ) {
+ /**
+ * 跨域问题设置
+ */
+ function axios_request()
+ {
+ $http_origin = !isset($_SERVER['HTTP_ORIGIN']) ? "*" : $_SERVER['HTTP_ORIGIN'];
+
+ $http_origin = (empty($http_origin) || $http_origin == null || $http_origin == 'null') ? '*' : $http_origin;
+
+ $_SERVER['HTTP_ORIGIN'] = $http_origin;
+
+ //if(strtoupper($_SERVER['REQUEST_METHOD'] ?? "") == 'OPTIONS'){ //vue 的 axios 发送 OPTIONS 请求,进行验证
+ // exit;
+ //}
+
+ header('Access-Control-Allow-Origin: ' . $http_origin);// . $http_origin
+ header('Access-Control-Allow-Credentials: true');//【如果请求方存在域名请求,那么为true;否则为false】
+ header('Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Access-Control-Allow-Headers, x-xsrf-token, Accept, x-file-name, x-frame-options, X-Requested-With');
+ header('Access-Control-Allow-Methods: *');
+ header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, PATCH');
+ //header('X-Frame-Options:SAMEORIGIN');
+ }
+}
+
+if ( !function_exists('string_underscore_lowercase') ) {
+ /**
+ * 字符串如果存在大小,那么自动转换成 _小写
+ *
+ * @param $string
+ * @param string $format
+ *
+ * @return string|string[]|null
+ */
+ function string_underscore_lowercase($string, $format = '_')
+ {
+ return preg_replace('/((?<=[a-z])(?=[A-Z]))/', $format, $string);
+ }
+}
+
+if ( !function_exists('convert_underline') ) {
+ // 下划线的字符串转骆驼峰
+ function convert_underline($str, $ucfirst = true)
+ {
+ $str = ucwords(str_replace('_', ' ', $str));
+ $str = str_replace(' ', '', lcfirst($str));
+ return $ucfirst ? ucfirst($str) : $str;
+ }
+}
+
+if ( !function_exists('get_string_pluralize') ) {
+ /**
+ * 获得指定字符串的复数
+ *
+ * @param $string
+ *
+ * @return mixed|string|string[]|null
+ */
+ function get_string_pluralize(string $string)
+ {
+ $plural = [
+ [
+ '/(quiz)$/i',
+ "$1zes",
+ ],
+ [
+ '/^(ox)$/i',
+ "$1en",
+ ],
+ [
+ '/([m|l])ouse$/i',
+ "$1ice",
+ ],
+ [
+ '/(matr|vert|ind)ix|ex$/i',
+ "$1ices",
+ ],
+ [
+ '/(x|ch|ss|sh)$/i',
+ "$1es",
+ ],
+ [
+ '/([^aeiouy]|qu)y$/i',
+ "$1ies",
+ ],
+ [
+ '/([^aeiouy]|qu)ies$/i',
+ "$1y",
+ ],
+ [
+ '/(hive)$/i',
+ "$1s",
+ ],
+ [
+ '/(?:([^f])fe|([lr])f)$/i',
+ "$1$2ves",
+ ],
+ [
+ '/sis$/i',
+ "ses",
+ ],
+ [
+ '/([ti])um$/i',
+ "$1a",
+ ],
+ [
+ '/(buffal|tomat)o$/i',
+ "$1oes",
+ ],
+ [
+ '/(bu)s$/i',
+ "$1ses",
+ ],
+ [
+ '/(alias|status)$/i',
+ "$1es",
+ ],
+ [
+ '/(octop|vir)us$/i',
+ "$1i",
+ ],
+ [
+ '/(ax|test)is$/i',
+ "$1es",
+ ],
+ [
+ '/s$/i',
+ "s",
+ ],
+ [
+ '/$/',
+ "s",
+ ],
+ ];
+
+ $singular = [
+ [
+ "/s$/",
+ "",
+ ],
+ [
+ "/(n)ews$/",
+ "$1ews",
+ ],
+ [
+ "/([ti])a$/",
+ "$1um",
+ ],
+ [
+ "/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/",
+ "$1$2sis",
+ ],
+ [
+ "/(^analy)ses$/",
+ "$1sis",
+ ],
+ [
+ "/([^f])ves$/",
+ "$1fe",
+ ],
+ [
+ "/(hive)s$/",
+ "$1",
+ ],
+ [
+ "/(tive)s$/",
+ "$1",
+ ],
+ [
+ "/([lr])ves$/",
+ "$1f",
+ ],
+ [
+ "/([^aeiouy]|qu)ies$/",
+ "$1y",
+ ],
+ [
+ "/(s)eries$/",
+ "$1eries",
+ ],
+ [
+ "/(m)ovies$/",
+ "$1ovie",
+ ],
+ [
+ "/(x|ch|ss|sh)es$/",
+ "$1",
+ ],
+ [
+ "/([m|l])ice$/",
+ "$1ouse",
+ ],
+ [
+ "/(bus)es$/",
+ "$1",
+ ],
+ [
+ "/(o)es$/",
+ "$1",
+ ],
+ [
+ "/(shoe)s$/",
+ "$1",
+ ],
+ [
+ "/(cris|ax|test)es$/",
+ "$1is",
+ ],
+ [
+ "/([octop|vir])i$/",
+ "$1us",
+ ],
+ [
+ "/(alias|status)es$/",
+ "$1",
+ ],
+ [
+ "/^(ox)en/",
+ "$1",
+ ],
+ [
+ "/(vert|ind)ices$/",
+ "$1ex",
+ ],
+ [
+ "/(matr)ices$/",
+ "$1ix",
+ ],
+ [
+ "/(quiz)zes$/",
+ "$1",
+ ],
+ ];
+
+ $irregular = [
+ [
+ 'move',
+ 'moves',
+ ],
+ [
+ 'sex',
+ 'sexes',
+ ],
+ [
+ 'child',
+ 'children',
+ ],
+ [
+ 'man',
+ 'men',
+ ],
+ [
+ 'person',
+ 'people',
+ ],
+ ];
+
+ $uncountable = [
+ 'sheep',
+ 'fish',
+ 'series',
+ 'species',
+ 'money',
+ 'rice',
+ 'information',
+ 'equipment',
+ ];
+
+ if ( in_array(strtolower($string), $uncountable) ) return $string;
+
+ foreach ($irregular as $noun) {
+ if ( strtolower($string) == $noun[0] ) return $noun[1];
+ }
+ foreach ($plural as $pattern) {
+ $str = preg_replace($pattern[0], $pattern[1], $string);
+ if ( $str !== null && $str != $string ) return $str;
+ }
+ }
+}
+
+if ( !function_exists('get_month_range') ) {
+ /**
+ * 指定日期范围之内的所有月份
+ *
+ * @param string $start_date 开始日期
+ * @param string $end_date 结束日期
+ * @param string $format 返回格式
+ *
+ * @return array
+ */
+ function get_month_range(string $start_date, string $end_date, string $format = 'Y-m')
+ {
+ $end = date($format, strtotime($end_date)); // 转换为月
+ $range = [];
+ $i = 0;
+ do {
+ $month = date($format, strtotime($start_date . ' + ' . $i . ' month'));
+ $range[] = $month;
+ $i++;
+ } while ( $month < $end );
+ return $range;
+ }
+}
+
+if ( !function_exists('get_year_range') ) {
+ function get_year_range(string $start_date, string $end_date, string $format = 'Y')
+ {
+ $end = date($format, strtotime($end_date)); // 转换为月
+ $range = [];
+ $i = 0;
+ do {
+ $month = date($format, strtotime($start_date . ' + ' . $i . ' year'));
+ $range[] = $month;
+ $i++;
+ } while ( $month < $end );
+ return $range;
+ }
+}
+
+if ( !function_exists('load_vendor') ) {
+ /**
+ * 加载composer的包 - vendor文件目录下
+ */
+ function load_vendor()
+ {
+ /*
+ |--------------------------------------------------------------------------
+ | Register The Auto Loader
+ |--------------------------------------------------------------------------
+ |
+ | Composer provides a convenient, automatically generated class loader for
+ | our application. We just need to utilize it! We'll simply require it
+ | into the script here so that we don't have to worry about manual
+ | loading any of our classes later on. It feels great to relax.
+ |
+ */
+ require ROOT . '/vendor/autoload.php';
+ }
+}
+
+if ( !function_exists('data_get') ) {
+ /**
+ * Get an item from an array or object using "dot" notation.
+ *
+ * @param mixed $target
+ * @param string|array|int $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ function data_get($target, $key, $default = null)
+ {
+ if ( is_null($key) ) {
+ return $target;
+ }
+
+ $key = is_array($key) ? $key : explode('.', $key);
+
+ while ( !is_null($segment = array_shift($key)) ) {
+ if ( $segment === '*' ) {
+ if ( $target instanceof Collection ) {
+ $target = $target->all();
+ } elseif ( !is_array($target) ) {
+ return ($default);
+ }
+
+ $result = [];
+
+ foreach ($target as $item) {
+ $result[] = data_get($item, $key);
+ }
+
+ return in_array('*', $key) ? \system\exp\Cnpscy\Library\Arr::collapse($result) : $result;
+ }
+
+ if ( \system\exp\Cnpscy\Library\Arr::accessible($target) && \system\exp\Cnpscy\Library\Arr::exists($target, $segment) ) {
+ $target = $target[$segment];
+ } elseif ( is_object($target) && isset($target->{$segment}) ) {
+ $target = $target->{$segment};
+ } else {
+ return ($default);
+ }
+ }
+
+ return $target;
+ }
+}
+
+if ( !function_exists('data_set') ) {
+ /**
+ * Set an item on an array or object using dot notation.
+ *
+ * @param mixed $target
+ * @param string|array $key
+ * @param mixed $value
+ * @param bool $overwrite
+ *
+ * @return mixed
+ */
+ function data_set(&$target, $key, $value, $overwrite = true)
+ {
+ $segments = is_array($key) ? $key : explode('.', $key);
+
+ if ( ($segment = array_shift($segments)) === '*' ) {
+ if ( !Arr::accessible($target) ) {
+ $target = [];
+ }
+
+ if ( $segments ) {
+ foreach ($target as &$inner) {
+ data_set($inner, $segments, $value, $overwrite);
+ }
+ } elseif ( $overwrite ) {
+ foreach ($target as &$inner) {
+ $inner = $value;
+ }
+ }
+ } elseif ( Arr::accessible($target) ) {
+ if ( $segments ) {
+ if ( !Arr::exists($target, $segment) ) {
+ $target[$segment] = [];
+ }
+
+ data_set($target[$segment], $segments, $value, $overwrite);
+ } elseif ( $overwrite || !Arr::exists($target, $segment) ) {
+ $target[$segment] = $value;
+ }
+ } elseif ( is_object($target) ) {
+ if ( $segments ) {
+ if ( !isset($target->{$segment}) ) {
+ $target->{$segment} = [];
+ }
+
+ data_set($target->{$segment}, $segments, $value, $overwrite);
+ } elseif ( $overwrite || !isset($target->{$segment}) ) {
+ $target->{$segment} = $value;
+ }
+ } else {
+ $target = [];
+
+ if ( $segments ) {
+ data_set($target[$segment], $segments, $value, $overwrite);
+ } elseif ( $overwrite ) {
+ $target[$segment] = $value;
+ }
+ }
+
+ return $target;
+ }
+}
+
+if ( !function_exists('config') ) {
+ /**
+ * Get / set the specified configuration value.
+ *
+ * If an array is passed as the key, we will assume you want to set an array of values.
+ *
+ * @param array|string|null $key
+ * @param mixed $default
+ *
+ * @return mixed|\Illuminate\Config\Repository
+ */
+ function config($key = null, $default = null)
+ {
+ if ( is_null($key) ) {
+ return app('config');
+ }
+
+ if ( is_array($key) ) {
+ return app('config')->set($key);
+ }
+ return app('config')->get($key, $default);
+ }
+}
+
+if ( !function_exists('abort') ) {
+ /**
+ * Throw an HttpException with the given data.
+ *
+ * @param $code
+ * @param string $msg
+ * @param array $headers
+ *
+ * @return mixed
+ */
+ function abort($code, $msg = '', array $headers = [])
+ {
+ return \Cnpscy\Embedded\Router::throwException($msg, $code);
+ exit;
+ //return http_response_code($code);
+
+ try {
+ if ( $code instanceof Response ) {
+ throw new \Cnpscy\Exceptions\HttpResponseException($code);
+ } elseif ( $code instanceof Responsable ) {
+ throw new \Cnpscy\Exceptions\HttpResponseException($code->toResponse(request()));
+ }
+ if ( $code == 404 ) {
+ throw new \Cnpscy\Exceptions\NotFoundHttpException($msg);
+ }
+ throw new \Cnpscy\Exceptions\HttpException($code, $msg, null, $headers);
+ } catch (\Cnpscy\Exceptions\HttpExceptionInterface $e) {
+ http_response_code($e->getStatusCode());
+
+ \Cnpscy\Embedded\Response::new()
+ ->failMsg(\app\lib\code::EMAIL_NO_EXIST, $e->getMessage() ?? $msg);
+ }
+ }
+}
+
+if ( !function_exists('http_response_code') ) {
+ function http_response_code($code = null)
+ {
+ if ( $code !== null ) {
+ switch ($code) {
+ case 100:
+ $text = 'Continue';
+ break;
+ case 101:
+ $text = 'Switching Protocols';
+ break;
+ case 200:
+ $text = 'OK';
+ break;
+ case 201:
+ $text = 'Created';
+ break;
+ case 202:
+ $text = 'Accepted';
+ break;
+ case 203:
+ $text = 'Non-Authoritative Information';
+ break;
+ case 204:
+ $text = 'No Content';
+ break;
+ case 205:
+ $text = 'Reset Content';
+ break;
+ case 206:
+ $text = 'Partial Content';
+ break;
+ case 300:
+ $text = 'Multiple Choices';
+ break;
+ case 301:
+ $text = 'Moved Permanently';
+ break;
+ case 302:
+ $text = 'Moved Temporarily';
+ break;
+ case 303:
+ $text = 'See Other';
+ break;
+ case 304:
+ $text = 'Not Modified';
+ break;
+ case 305:
+ $text = 'Use Proxy';
+ break;
+ case 400:
+ $text = 'Bad Request';
+ break;
+ case 401:
+ $text = 'Unauthorized';
+ break;
+ case 402:
+ $text = 'Payment Required';
+ break;
+ case 403:
+ $text = 'Forbidden';
+ break;
+ case 404:
+ $text = 'Not Found';
+ break;
+ case 405:
+ $text = 'Method Not Allowed';
+ break;
+ case 406:
+ $text = 'Not Acceptable';
+ break;
+ case 407:
+ $text = 'Proxy Authentication Required';
+ break;
+ case 408:
+ $text = 'Request Time-out';
+ break;
+ case 409:
+ $text = 'Conflict';
+ break;
+ case 410:
+ $text = 'Gone';
+ break;
+ case 411:
+ $text = 'Length Required';
+ break;
+ case 412:
+ $text = 'Precondition Failed';
+ break;
+ case 413:
+ $text = 'Request Entity Too Large';
+ break;
+ case 414:
+ $text = 'Request-URI Too Large';
+ break;
+ case 415:
+ $text = 'Unsupported Media Type';
+ break;
+ case 500:
+ $text = 'Internal Server Error';
+ break;
+ case 501:
+ $text = 'Not Implemented';
+ break;
+ case 502:
+ $text = 'Bad Gateway';
+ break;
+ case 503:
+ $text = 'Service Unavailable';
+ break;
+ case 504:
+ $text = 'Gateway Time-out';
+ break;
+ case 505:
+ $text = 'HTTP Version not supported';
+ break;
+ default:
+ exit('Unknown http status code "' . htmlentities($code) . '"');
+ break;
+ }
+
+ $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
+
+ header($protocol . ' ' . $code . ' ' . $text);
+
+ $GLOBALS['http_response_code'] = $code;
+ } else {
+
+ $code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
+ }
+ return $code;
+ }
+}
+
+if ( !function_exists('filtering_html_tags') ) {
+ /**
+ * 过滤HTML的标签,只保留文本
+ *
+ * @param $string
+ *
+ * @return string
+ */
+ function filtering_html_tags($string)
+ {
+ return strip_tags(htmlspecialchars_decode($string), '');
+ }
+}
+
+if ( !function_exists('hash_make') ) {
+ /**
+ * hash加密
+ *
+ * @param string $password
+ *
+ * @return string
+ */
+ function hash_make(string $password = '123456') : string
+ {
+ return hash_encryption($password);
+ }
+}
+
+if ( !function_exists('hash_encryption') ) {
+ /**
+ * [hash_encryption]
+ *
+ * @param string $pass [description]
+ *
+ * @return string
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :hash加密
+ * @englishAnnotation :
+ * @version :1.0
+ */
+ function hash_encryption($pass = '123456') : string
+ {
+ return password_hash($pass, PASSWORD_DEFAULT);
+ }
+}
+
+if ( !function_exists('hash_verify') ) {
+ /**
+ * [hash_verify]
+ *
+ * @param string $pass [description]
+ * @param string $hash_pass [description]
+ *
+ * @return bool
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :hash解密
+ * @englishAnnotation :
+ *
+ * @version :1.0
+ */
+ function hash_verify(string $pass, string $hash_pass) : bool
+ {
+ return password_verify($pass, $hash_pass);
+ }
+}
+
+/**
+ * 数组按照指定字段进行分组
+ *
+ * @param array $array
+ * @param string $field
+ *
+ * @return array
+ */
+function array_field_group(array $array = [], string $field = '') : array
+{
+ $new_ary = [];
+ foreach ($array as $k => $val) $new_ary[$val[$field]][] = $val;
+ return $new_ary;
+}
+
+//计算中奖概率
+function get_random_probability($proArr)
+{
+ $rs = ''; //z中奖结果
+ $proSum = array_sum($proArr); //概率数组的总概率精度
+ //概率数组循环
+ foreach ($proArr as $key => $proCur) {
+ $randNum = mt_rand(0, $proSum);
+ if ( $randNum <= $proCur ) {
+ $rs = $key;
+ break;
+ } else $proSum -= $proCur;
+ }
+ unset($proArr);
+ return $rs;
+}
+
+/**
+ * 对比数组的不同:
+ * 第二个数组对于第一个数组的不同数据返回
+ * 并不是array_diff数组的效果,找两个数组之间的差集
+ *
+ * @param $array1
+ * @param $array2
+ *
+ * @return array
+ */
+function get_array_diff($array1, $array2)
+{
+ if ( empty($array2) ) return $array1;
+ $diff = [];
+ foreach ($array1 as $key => $val) {
+ if ( !isset($array2[$val]) ) $diff[$val] = $val;
+ }
+ return $diff;
+}
+
+function list_to_tree($list, $primary_key = 'menu_id', $pid = 'parent_id', $child = '_child', $root = 0) : array
+{
+ $tree = [];
+ if ( is_array($list) ) {
+ $refer = [];
+ foreach ($list as $key => $data) $refer[$data[$primary_key]] =& $list[$key];
+ foreach ($list as $key => $data) {
+ $parentId = $data[$pid];
+ if ( $root == $parentId ) $tree[] =& $list[$key]; else {
+ if ( isset($refer[$parentId]) ) {
+ $parent =& $refer[$parentId];
+ $parent[$child][] =& $list[$key];
+ }
+ }
+ }
+ }
+ return $tree;
+}
+
+/**
+ * [login_token]
+ *
+ * @param integer $length [description]
+ *
+ * @return string
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:登录token值
+ * @englishAnnotation:
+ */
+function login_token($length = 60) : string
+{
+ //, '#', '%', '&', '+', '^', '`' --- 会影响参数获取,参数获取不全
+ $chars = [
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm',
+ 'n',
+ 'o',
+ 'p',
+ 'q',
+ 'r',
+ 's',
+ 't',
+ 'u',
+ 'v',
+ 'w',
+ 'x',
+ 'y',
+ 'z',
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ '0',
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ '!',
+ '@',
+ '$',
+ '*',
+ '(',
+ ')',
+ '-',
+ '_',
+ '[',
+ ']',
+ '{',
+ '}',
+ '<',
+ '>',
+ '~',
+ '=',
+ ',',
+ '.',
+ ';',
+ ':',
+ '/',
+ '?',
+ '|',
+ ];
+ $array = rand_array_fill($chars, $length);
+ $rand = '';
+ for ($i = 0; $i < $length; $i++) $rand .= $chars[$array[$i]];
+
+ unset($length, $array, $chars);
+
+ return $rand;
+}
+
+function rand_array_fill(&$ary, $length)
+{
+ $count = count($ary);
+ if ( $count < $length ) {
+ $new_ary = [];
+ for ($i = 0; $i < ceil($length / $count); $i++) {
+ for ($j = 0; $j < count($ary); $j++) {
+ array_push($new_ary, $ary[$j]);
+ }
+ }
+ $ary = $new_ary;
+ return array_rand($new_ary, $length);
+ } else return array_rand($ary, $length);
+}
+
+if ( !function_exists('get_client_info') ) {
+ /**
+ * 获取IP与浏览器信息、语言
+ *
+ * @return array
+ */
+ function get_client_info() : array
+ {
+ if ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) ) {
+ $XFF = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ $client_pos = strpos($XFF, ', ');
+ $client_ip = false !== $client_pos ? substr($XFF, 0, $client_pos) : $XFF;
+ unset($XFF, $client_pos);
+ } else $client_ip = $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? $_SERVER['LOCAL_ADDR'] ?? '0.0.0.0';
+ $client_lang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 5) : '';
+ $client_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ return [
+ 'ip' => &$client_ip,
+ 'lang' => &$client_lang,
+ 'agent' => &$client_agent,
+ ];
+ }
+}
+
+if ( !function_exists('get_ip') ) {
+ function get_ip() : string
+ {
+ $data = get_client_info();
+ return $data['ip'] ?? '';
+ }
+}
+
+if ( !function_exists('get_this_url') ) {
+ function get_this_url()
+ {
+ return 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?' . $_SERVER['QUERY_STRING'];
+ }
+}
+
+if ( !function_exists('get_request_url') ) {
+ function get_request_url()
+ {
+ return $_SERVER['REQUEST_URI'];
+ }
+}
+
+/**
+ * [request_post 模拟post进行url请求]
+ *
+ * @Author :cnpscy——<[2278757482@qq.com]>
+ * @DateTime :2017-09-25
+ * @chineseAnnotation:模拟post进行url请求
+ * @englishAnnotation:Simulate post for URL requests
+ *
+ * @param string $url [url地址]
+ * @param array $post_data [提交的数据]
+ * @param boolean $ispost [是否是post请求]
+ * @param string $type [返回格式]
+ *
+ * @return array [description]
+ */
+function request_post(string $url = '', array $post_data = [], $ispost = true, $type = 'json')
+{
+ @header("Content-type: text/html; charset=utf-8");
+ if ( empty($url) ) return false;
+ $o = "";
+ if ( !empty($post_data) ) {
+ foreach ($post_data as $k => $v) $o .= "$k=" . urlencode($v) . "&";
+ $post_data = substr($o, 0, -1);
+ $key = md5(base64_encode($post_data));
+ } else $key = 'key';
+ if ( $ispost ) {
+ $url = $url;
+ $curlPost = $post_data;
+ } else {
+ $url = $url . '?' . implode(',', $post_data);
+ $curlPost = 'key=' . $key;
+ }
+ $ch = curl_init();//初始化curl
+ curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
+ curl_setopt($ch, CURLOPT_HEADER, 0);//设置header
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
+ if ( $ispost ) {
+ curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
+ }
+ $data = curl_exec($ch);//运行curl
+ curl_close($ch);
+ $object = json_decode($data);
+ $return = object_to_array($object);
+ return $return;
+}
+
+/**
+ * [object_to_array 对象转为数组]
+ *
+ * @Author :cnpscy——<[2278757482@qq.com]>
+ * @DateTime :2017-09-26
+ * @chineseAnnotation:对象转为数组
+ * @englishAnnotation:The object is converted to an array
+ *
+ * @param object $array [需要转换的对象]
+ *
+ * @return array [description]
+ */
+function object_to_array($array)
+{
+ if ( is_object($array) ) $array = (array)$array;
+ if ( is_array($array) ) {
+ foreach ($array as $key => $value) $array[$key] = object_to_array($value);
+ }
+ return $array;
+}
+
+/**
+ * [config_array_analysis]
+ *
+ * @param [type] $data [需要解析的数组]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :配置多维数组的解析
+ * @englishAnnotation :
+ */
+function config_array_analysis($data)
+{
+ $value_extra = preg_split('/[,;\r\n]+/', trim($data, ",;\r\n"));
+ if ( strpos($data, ':') ) {
+ $array = [];
+ foreach ($value_extra as $val) {
+ [
+ $k,
+ $v,
+ ] = explode(':', $val);
+ $array[$k] = $v;
+ }
+ } else $array = $value_extra;
+ return $array ?? [];
+}
+
+/**
+ * [is_base64]
+ *
+ * @param [type] $str [description]
+ *
+ * @return boolean [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :检测是否为base64位编码
+ * @englishAnnotation :
+ */
+function is_base64($str)
+{
+ //这里多了个纯字母和纯数字的正则判断
+ if ( @preg_match('/^[0-9]*$/', $str) || @preg_match('/^[a-zA-Z]*$/', $str) ) return false; elseif ( is_utf8(base64_decode($str)) && base64_decode($str) != '' ) return true;
+ return false;
+}
+
+/**
+ * [is_utf8]
+ *
+ * @param [type] $str [description]
+ *
+ * @return boolean [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :判断否为UTF-8编码
+ * @englishAnnotation :
+ */
+function is_utf8($str)
+{
+ $len = strlen($str);
+ for ($i = 0; $i < $len; $i++) {
+ $c = ord($str[$i]);
+ if ( $c > 128 ) {
+ if ( ($c > 247) ) return false; elseif ( $c > 239 ) $bytes = 4;
+ elseif ( $c > 223 ) $bytes = 3;
+ elseif ( $c > 191 ) $bytes = 2;
+ else return false;
+ if ( ($i + $bytes) > $len ) return false;
+ while ( $bytes > 1 ) {
+ $i++;
+ $b = ord($str[$i]);
+ if ( $b < 128 || $b > 191 ) return false;
+ $bytes--;
+ }
+ }
+ }
+ return true;
+}
+
+function getMillisecond()
+{
+ [
+ $t1,
+ $t2,
+ ] = explode(' ', microtime());
+ return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
+}
+
+/**
+ * [check_url]
+ *
+ * @param string $_url [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :检测URL地址格式
+ * @englishAnnotation :
+ * @version :1.0
+ */
+if ( !function_exists('check_url') ) {
+ function check_url(string $url) : bool
+ {
+ $str = "/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/";
+ if ( !preg_match($str, $url) ) return false; else return true;
+ }
+}
+
+if ( !function_exists('filter_url') ) {
+ function filter_url(string $url) : bool
+ {
+ return filter_var($url, FILTER_VALIDATE_URL) !== false ? false : true;
+ }
+}
+
+/**
+ * [array_ksort_to_string]
+ *
+ * @param [type] $data [description]
+ *
+ * @return string
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :数组升序转成字符串
+ * @englishAnnotation :
+ * @version :1.0
+ */
+function array_ksort_to_string($data) : string
+{
+ if ( is_string($data) ) return $data;
+ ksort($data);
+ $tmps = [];
+ foreach ($data as $k => $v) $tmps[] = $k . $v;
+ return implode('', $tmps);
+}
+
+/**
+ * [mobile_web]
+ *
+ * @return boolean [description]
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:是否为手机端访问
+ * @englishAnnotation:
+ */
+function mobile_web() : bool
+{
+ $useragent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ $useragent_commentsblock = preg_match('|\(.*?\)|', $useragent, $matches) > 0 ? $matches[0] : '';
+
+ $mobile_os_list = [
+ 'Google Wireless Transcoder',
+ 'Windows CE',
+ 'WindowsCE',
+ 'Symbian',
+ 'Android',
+ 'armv6l',
+ 'armv5',
+ 'Mobile',
+ 'CentOS',
+ 'mowser',
+ 'AvantGo',
+ 'Opera Mobi',
+ 'J2ME/MIDP',
+ 'Smartphone',
+ 'Go.Web',
+ 'Palm',
+ 'iPAQ',
+ ];
+ $mobile_token_list = [
+ 'Profile/MIDP',
+ 'Configuration/CLDC-',
+ '160×160',
+ '176×220',
+ '240×240',
+ '240×320',
+ '320×240',
+ 'UP.Browser',
+ 'UP.Link',
+ 'SymbianOS',
+ 'PalmOS',
+ 'PocketPC',
+ 'SonyEricsson',
+ 'Nokia',
+ 'BlackBerry',
+ 'Vodafone',
+ 'BenQ',
+ 'Novarra-Vision',
+ 'Iris',
+ 'NetFront',
+ 'HTC_',
+ 'Xda_',
+ 'SAMSUNG-SGH',
+ 'Wapaka',
+ 'DoCoMo',
+ 'iPhone',
+ 'iPod',
+ ];
+
+ $found_mobile = CheckSubstrs($mobile_os_list, $useragent_commentsblock) || CheckSubstrs($mobile_token_list, $useragent);
+
+ if ( $found_mobile ) return true; else return false;
+}
+
+function CheckSubstrs($substrs, $text)
+{
+ foreach ($substrs as $substr) {
+ if ( false !== strpos($text, $substr) ) return true;
+ }
+ return false;
+}
+
+/**
+ * [is_app]
+ *
+ * @return boolean [description]
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:检测是否为App
+ * @englishAnnotation:
+ */
+function is_app()
+{
+ if ( isset ($_SERVER['HTTP_X_WAP_PROFILE']) ) return true;// 如果有HTTP_X_WAP_PROFILE则一定是移动设备
+ // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
+ if ( isset ($_SERVER['HTTP_VIA']) ) return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;// 找不到为flase,否则为true
+ // 脑残法,判断手机发送的客户端标志,兼容性有待提高
+ if ( isset ($_SERVER['HTTP_USER_AGENT']) ) {
+ $clientkeywords = [
+ 'nokia',
+ 'sony',
+ 'ericsson',
+ 'mot',
+ 'samsung',
+ 'htc',
+ 'sgh',
+ 'lg',
+ 'sharp',
+ 'sie-',
+ 'philips',
+ 'panasonic',
+ 'alcatel',
+ 'lenovo',
+ 'iphone',
+ 'ipod',
+ 'blackberry',
+ 'meizu',
+ 'android',
+ 'netfront',
+ 'symbian',
+ 'ucweb',
+ 'windowsce',
+ 'palm',
+ 'operamini',
+ 'operamobi',
+ 'openwave',
+ 'nexusone',
+ 'cldc',
+ 'midp',
+ 'wap',
+ 'mobile',
+ ];
+ // 从HTTP_USER_AGENT中查找手机浏览器的关键字
+ if ( preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT'])) ) return true;
+ }
+ // 协议法,因为有可能不准确,放到最后判断
+ if ( isset ($_SERVER['HTTP_ACCEPT']) ) {
+ // 如果只支持wml并且不支持html那一定是移动设备
+ // 如果支持wml和html但是wml在html之前则是移动设备
+ if ( (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html'))) ) return true;
+ }
+ return false;
+}
+
+/**
+ * 检测身份证号码
+ *
+ * @param string $id
+ *
+ * @return bool
+ */
+function check_idcard(string $id) : bool
+{
+ $id = strtoupper($id);
+ $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
+ $arr_split = [];
+ if ( !preg_match($regx, $id) ) return false;
+ if ( 15 == strlen($id) ) { //检查15位
+ $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";
+ @preg_match($regx, $id, $arr_split);
+ //检查生日日期是否正确
+ $dtm_birth = "19" . $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
+ if ( !strtotime($dtm_birth) ) return false; else return true;
+ } else { //检查18位
+ $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
+ @preg_match($regx, $id, $arr_split);
+ $dtm_birth = $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
+ if ( !strtotime($dtm_birth) ) return false;//检查生日日期是否正确
+ else {
+ //检验18位身份证的校验码是否正确。
+ //校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。
+ $arr_int = [
+ 7,
+ 9,
+ 10,
+ 5,
+ 8,
+ 4,
+ 2,
+ 1,
+ 6,
+ 3,
+ 7,
+ 9,
+ 10,
+ 5,
+ 8,
+ 4,
+ 2,
+ ];
+ $arr_ch = [
+ '1',
+ '0',
+ 'X',
+ '9',
+ '8',
+ '7',
+ '6',
+ '5',
+ '4',
+ '3',
+ '2',
+ ];
+ $sign = 0;
+ for ($i = 0; $i < 17; $i++) {
+ $b = (int)$id{$i};
+ $w = $arr_int[$i];
+ $sign += $b * $w;
+ }
+ $n = $sign % 11;
+ $val_num = $arr_ch[$n];
+ if ( $val_num != substr($id, 17, 1) ) return false; else return true;//phpfensi.com
+ }
+ }
+}
+
+/**
+ * [round_down_decimal]
+ *
+ * @param float|integer $money_num [description]
+ * @param int|integer $length [description]
+ *
+ * @return float
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:保留几位小数,向下取,就是直接截断 3 为小数即可。
+ * @englishAnnotation:
+ */
+function round_down_decimal(float $money_num = 0, int $length = 2) : float
+{
+ return substr($money_num, 0, strlen($money_num) - _getFloatLength($money_num) + $length);
+}
+
+/**
+ * [_getFloatLength]
+ *
+ * @param [type] $num [description]
+ *
+ * @return int
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :计算小数部分的长度
+ * @englishAnnotation :
+ * @version :1.0
+ */
+function _getFloatLength($num) : int
+{
+ $count = 0;
+ $temp = explode('.', $num);
+ if ( sizeof($temp) > 1 ) {
+ $decimal = end($temp);
+ $count = strlen($decimal);
+ }
+ return $count;
+}
+
+/**
+ * [set_money_conversion]
+ *
+ * @param float|integer $money [description]
+ * @param int|integer $length [description]
+ *
+ * @return float
+ *
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:金额插入数据库的时候,单位转换为“分”【具体根据$length长度而定】
+ * @englishAnnotation:
+ * @version :1.0
+ */
+function set_money_conversion(float $money = 0, int $length = 2) : float
+{
+ return floatval($money) * pow(10, $length);
+}
+
+/**
+ * [get_money_conversion]
+ *
+ * @param float|integer $money [description]
+ * @param int|integer $length [description]
+ *
+ * @return [type] [description]
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :金额取出的时候,由“分”转化为“元”【具体根据$length长度而定】
+ * @englishAnnotation :
+ */
+function get_money_conversion(float $money = 0, int $length = 2) : float
+{
+ return floatval($money) / pow(10, $length);
+}
+
+/**
+ * [is_date]
+ *
+ * @param string $date [description]
+ *
+ * @return boolean [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:是否为日期格式
+ * @englishAnnotation:
+ * @version :1.0
+ */
+function is_date(string $date) : bool
+{
+ $ret = strtotime($date);
+ return $ret !== false && $ret != -1;
+
+ // if (date('Y-m-d H:i:s', strtotime($date)) === $date) return true;
+ // else return false;
+}
+
+/**
+ * [month_list]
+ *
+ * @param int $start [description]
+ * @param int $end [description]
+ *
+ * @return [type] [description]
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :
+ * @englishAnnotation :
+ */
+function month_list(int $start, int $end) : array
+{
+ if ( !is_numeric($start) || !is_numeric($end) || ($end <= $start) ) return '';
+ $start = date('Y-m', $start);
+ $end = date('Y-m', $end);
+ //转为时间戳
+ $start = strtotime($start . '-01');
+ $end = strtotime($end . '-01');
+ $i = 0;
+ $d = [];
+ while ( $start <= $end ) {
+ //这里累加每个月的的总秒数 计算公式:上一月1号的时间戳秒数减去当前月的时间戳秒数
+ $d[$i] = trim(date('Y-m', $start), ' ');
+ $start += strtotime('+1 month', $start) - $start;
+ $i++;
+ }
+ return $d;
+}
+
+function get_errors_list(array $data = []) : string
+{
+ $html = '';
+ if ( !empty($data) ) {
+ foreach ($data as $k => $v) $html .= $k + 1 . '.' . $v . PHP_EOL;
+ }
+ return $html;
+}
+
+//内存占用空间
+function memory_usage()
+{
+ $memory = ( !function_exists('memory_get_usage')) ? '0' : round(memory_get_usage() / 1024 / 1024, 2) . 'MB';
+ return $memory;
+}
+
+/**
+ * 参数说明
+ * $string 欲截取的字符串
+ * $sublen 截取的长度
+ * $start 从第几个字节截取,默认为0
+ * $code 字符编码,默认UTF-8
+ */
+function cut_str(string $string, int $sublen = 100, int $start = 0, $code = 'UTF-8')
+{
+ if ( $code == 'UTF-8' ) {
+ $pa = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/";
+ preg_match_all($pa, $string, $t_string);
+ if ( count($t_string[0]) - $start > $sublen ) return join('', array_slice($t_string[0], $start, $sublen)) . ".....";
+ return join('', array_slice($t_string[0], $start, $sublen));
+ } else {
+ $start = $start * 2;
+ $sublen = $sublen * 2;
+ $strlen = strlen($string);
+ $tmpstr = '';
+ for ($i = 0; $i < $strlen; $i++) {
+ if ( $i >= $start && $i < ($start + $sublen) ) {
+ if ( ord(substr($string, $i, 1)) > 129 ) {
+ $tmpstr .= substr($string, $i, 2);
+ } else {
+ $tmpstr .= substr($string, $i, 1);
+ }
+ }
+ if ( ord(substr($string, $i, 1)) > 129 ) $i++;
+ }
+ if ( strlen($tmpstr) < $strlen ) $tmpstr .= "";
+ return $tmpstr;
+ }
+}
+
+/**
+ * [strToHex]
+ *
+ * @param [type] $string [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :字符串转16进制
+ * @englishAnnotation :
+ * @version :1.0
+ */
+function strToHex($string)
+{
+ $this_i = $hex = "";
+ for ($i = 0; $i < strlen($string); $i++) {
+ $this_i = dechex(ord($string[$i]));
+ if ( strlen($this_i) == 0 ) $this_i = '00'; elseif ( strlen($this_i) == 1 ) $this_i = '0' . $this_i;
+ $hex .= $this_i;
+ }
+ $hex = strtoupper($hex);
+ return $hex;
+}
+
+/**
+ * [hexToStr]
+ *
+ * @param [type] $hex [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :16进制转字符串
+ * @englishAnnotation :
+ * @version :1.0
+ */
+function hexToStr($hex)
+{
+ $sendStrArray = str_split(str_replace(' ', '', $hex), 2); // 将16进制数据转换成两个一组的数组
+ $send_info = '';
+ for ($j = 0; $j < count($sendStrArray); $j++) {
+ $send_info .= chr(hexdec($sendStrArray[$j]));
+ }
+ return $send_info;
+}
+
+function crc16($string, $start_reverse = 0)
+{
+ $string = pack('H*', $string);
+ $crc = 0xFFFF;
+ for ($x = 0; $x < strlen($string); $x++) {
+ $crc = $crc ^ ord($string[$x]);
+ for ($y = 0; $y < 8; $y++) {
+ if ( ($crc & 0x0001) == 0x0001 ) {
+ $crc = (($crc >> 1) ^ 0xA001);
+ } else {
+ $crc = $crc >> 1;
+ }
+ }
+ }
+ $more_data = strlen(dechex(floor($crc % 256))) < 2 ? '0' . dechex($crc % 256) : dechex($crc % 256);
+ $less_data = strlen(dechex(floor($crc / 256))) < 2 ? '0' . dechex($crc / 256) : dechex($crc / 256);
+ return strtoupper($start_reverse == 0 ? ($more_data . $less_data) : ($less_data . $more_data));
+}
+
+function gencrc16($string)
+{
+ $crc = 0xFFFF;
+ for ($x = 0; $x < strlen($string); $x++) {
+ $crc = $crc ^ ord($string[$x]);
+ for ($y = 0; $y < 8; $y++) {
+ if ( ($crc & 0x0001) == 0x0001 ) {
+ $crc = (($crc >> 1) ^ 0xA001);
+ } else {
+ $crc = $crc >> 1;
+ }
+ }
+ }
+ return strtoupper($crc);
+}
+
+/**
+ * @param $lat1
+ * @param $lng1
+ * @param $lat2
+ * @param $lng2
+ *
+ * @return int
+ *
+ * 经纬度计算两点之间的距离
+ */
+function getDistance($lat1, $lng1, $lat2, $lng2)
+{
+
+ // 将角度转为狐度
+ $radLat1 = deg2rad($lat1);// deg2rad()函数将角度转换为弧度
+ $radLat2 = deg2rad($lat2);
+ $radLng1 = deg2rad($lng1);
+ $radLng2 = deg2rad($lng2);
+
+ $a = $radLat1 - $radLat2;
+ $b = $radLng1 - $radLng2;
+
+ $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137;
+
+ return $s;
+}
+
+/**
+ * @param $lat1
+ * @param $lon1
+ * @param $lat2
+ * @param $lon2
+ * @param float $radius 星球半径 KM
+ *
+ * @return float
+ *
+ * 经纬度计算两点之间的距离
+ */
+function distance($lat1, $lon1, $lat2, $lon2, $radius = 6378.137)
+{
+ $rad = floatval(M_PI / 180.0);
+
+ $lat1 = floatval($lat1) * $rad;
+ $lon1 = floatval($lon1) * $rad;
+ $lat2 = floatval($lat2) * $rad;
+ $lon2 = floatval($lon2) * $rad;
+
+ $theta = $lon2 - $lon1;
+
+ $dist = acos(sin($lat1) * sin($lat2) + cos($lat1) * cos($lat2) * cos($theta));
+
+ if ( $dist < 0 ) {
+ $dist += M_PI;
+ }
+ return $dist = $dist * $radius;
+}
+
+function request_function(string $url = '', array $post_data = [], $ispost = true, $json_conversion = 1)
+{
+ if ( empty($url) ) return false;
+ $o = "";
+ if ( !empty($post_data) ) {
+ foreach ($post_data as $k => $v) $o .= "$k=" . urlencode($v) . "&";
+ $post_data = substr($o, 0, -1);
+ $key = md5(base64_encode($post_data));
+ } else $key = 'key';
+ if ( $ispost ) {
+ $url = $url;
+ $curlPost = $post_data;
+ } else {
+ $url = $url . '?' . $post_data;
+ $curlPost = 'key=' . $key;
+ }
+ $ch = curl_init();//初始化curl
+ curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
+ curl_setopt($ch, CURLOPT_HEADER, 0);//设置header
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //禁止 cURL 验证对等证书
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //是否检测服务器的域名与证书上的是否一致
+ if ( $ispost ) {
+ curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
+ }
+ $data = curl_exec($ch);//运行curl
+ curl_close($ch);
+ return $json_conversion ? json_decode($data, true) : $data;
+}
+
+/**
+ * [set_field_filtering]
+ *
+ * @param string $field [数据]
+ * @param string $field_type [数据的类型]
+ * @param string $default_val [默认值]
+ *
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:数据类型过滤
+ * @englishAnnotation:
+ */
+function set_field_filtering($field = '', $field_type = 'string', $default_val = '')
+{
+ if ( isset($field) || $field == null ) return $field;
+ $field_type = strtolower(trim($field_type));
+ if ( in_array($field_type, [
+ 'str',
+ 'string',
+ 'varchar',
+ ]) ) return trim($field ?? $default_val); elseif ( in_array($field_type, [
+ 'int',
+ 'intval',
+ 'number',
+ ]) ) return intval($field ?? $default_val);
+ elseif ( in_array($field_type, [
+ 'double',
+ 'float',
+ 'floatval',
+ ]) ) return floatval($field ?? $default_val);
+}
+
+function get_server_url() : string
+{
+ $pact = isset($_SERVER['HTTPS']) && 'off' !== $_SERVER['HTTPS'] ? 'https://' : 'http://';
+ return $pact . ($_SERVER['SERVER_NAME'] ?? '');
+}
+
+function set_server_url($str) : string
+{
+ return get_server_url() . $str;
+ // return get_server_url() . $str;
+}
+
+function remove_server_url(string $img) : string
+{
+ return str_replace(get_server_url(), "", $img);
+}
+
+//生成随机数
+function get_rand($sum = 6)
+{
+ $rand = '';
+ for ($i = 1; $i <= $sum; $i++) $rand .= rand(0, 9);
+ return $rand;
+}
+
+/**
+ * 获取随机字符串
+ *
+ * @param int $randLength 长度
+ * @param int $create_time 是否加入当前时间戳
+ * @param int $includenumber 是否包含数字
+ *
+ * @return string
+ */
+if ( !function_exists('rand_str') ) {
+ function rand_str($randLength = 6, $create_time = 1, $includenumber = 1)
+ {
+ if ( $includenumber ) {
+ $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789';
+ } else {
+ $chars = 'abcdefghijklmnopqrstuvwxyz';
+ }
+ $len = strlen($chars);
+ $randStr = '';
+ for ($i = 0; $i < $randLength; $i++) {
+ $randStr .= $chars[mt_rand(0, $len - 1)];
+ }
+ $tokenvalue = $randStr;
+ if ( $create_time ) {
+ $tokenvalue = $randStr . '-' . time();
+ }
+ return $tokenvalue;
+ }
+}
+
+/**
+ * 倒计时 转化成 天-时分秒 展示
+ *
+ * @param $time
+ *
+ * @return string
+ */
+function countdown_conversion_time($time)
+{
+ if ( empty($time) ) return '';
+ $day = intval($time / (60 * 60 * 24));
+ $hour = intval($time / (60 * 60) - $day * 24);
+ $minute = intval($time / 60 - $day * 24 * 60 - $hour * 60);
+ $second = intval($time - $day * 24 * 60 * 60 - $hour * 60 * 60 - $minute * 60);
+
+ $day = (intval($day) <= 0) ? '' : $day . '天 ';
+ if ( intval($hour) <= 9 ) $hour = '0' . $hour;
+ if ( intval($minute) <= 9 ) $minute = '0' . $minute;
+ if ( intval($second) <= 9 ) $second = '0' . $second;
+ return $day . $hour . '时' . $minute . '分' . $second . '秒';
+}
+
+function amount_conversion($money, $length = 2)
+{
+ return round(floatval($money), $length);
+}
+
+/**
+ * 金额格式化
+ *
+ * @param [float] $value [金额]
+ * @param [int] $decimals [保留的位数]
+ * @param [string] $dec_point [保留小数分隔符]
+ */
+function PriceNumberFormat($value, $decimals = 2, $dec_point = '.')
+{
+ return number_format($value, $decimals, $dec_point, '');
+}
+
+function getWxPayMoeny($moeny)
+{
+ return $moeny * 100;//以分为单位
+}
+
+/*
+ * 检测银行卡
+ 16-19 位卡号校验位采用 Luhm 校验方法计算:
+ 1,将未带校验位的 15 位卡号从右依次编号 1 到 15,位于奇数位号上的数字乘以 2
+ 2,将奇位乘积的个十位全部相加,再加上所有偶数位上的数字
+ 3,将加法和加上校验位能被 10 整除。
+*/
+function check_bank_card($bankCardNo)
+{
+ $strlen = strlen($bankCardNo);
+ if ( $strlen < 15 || $strlen > 19 ) {
+ return false;
+ }
+ if ( !preg_match("/^\d{15}$/i", $bankCardNo) && !preg_match("/^\d{16}$/i", $bankCardNo) && !preg_match("/^\d{17}$/i", $bankCardNo) && !preg_match("/^\d{18}$/i", $bankCardNo) && !preg_match("/^\d{19}$/i", $bankCardNo) ) {
+ return false;
+ }
+ $arr_no = str_split($bankCardNo);
+ $last_n = $arr_no[count($arr_no) - 1];
+ krsort($arr_no);
+ $i = 1;
+ $total = 0;
+ foreach ($arr_no as $n) {
+ if ( $i % 2 == 0 ) {
+ $ix = $n * 2;
+ if ( $ix >= 10 ) {
+ $nx = 1 + ($ix % 10);
+ $total += $nx;
+ } else {
+ $total += $ix;
+ }
+ } else {
+ $total += $n;
+ }
+ $i++;
+ }
+ $total -= $last_n;
+ $x = 10 - ($total % 10);
+ if ( $x != $last_n ) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * 生成混合code
+ *
+ * @param integer $length [description]
+ *
+ * @return [type] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :登录token值
+ * @englishAnnotation :
+ */
+function make_blend_code($length = 20) : string
+{
+ $chars = [
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i',
+ 'j',
+ 'k',
+ 'l',
+ 'm',
+ 'n',
+ 'o',
+ 'p',
+ 'q',
+ 'r',
+ 's',
+ 't',
+ 'u',
+ 'v',
+ 'w',
+ 'x',
+ 'y',
+ 'z',
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F',
+ 'G',
+ 'H',
+ 'I',
+ 'J',
+ 'K',
+ 'L',
+ 'M',
+ 'N',
+ 'O',
+ 'P',
+ 'Q',
+ 'R',
+ 'S',
+ 'T',
+ 'U',
+ 'V',
+ 'W',
+ 'X',
+ 'Y',
+ 'Z',
+ '0',
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ ];
+ $array = array_rand($chars, $length);
+ $rand = '';
+ for ($i = 0; $i < $length; $i++) $rand .= $chars[$array[$i]];
+ return $rand;
+}
+
+/**
+ * 生成UUID
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+if ( !function_exists('make_uuid') ) {
+ /**
+ * 生成UUID
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ function make_uuid(string $string = '') : string
+ {
+ $string = '' === $string ? uniqid(mt_rand(), true) : (0 === (int)preg_match('/[A-Z]/', $string) ? $string : mb_strtolower($string, 'UTF-8'));
+ $code = hash('sha1', $string . ':UUID');
+ $uuid = substr($code, 0, 10);
+ $uuid .= substr($code, 10, 4);
+ $uuid .= substr($code, 16, 4);
+ $uuid .= substr($code, 22, 4);
+ $uuid .= substr($code, 28, 12);
+ $uuid = strtoupper($uuid);
+ unset($string, $code);
+ return $uuid;
+ }
+}
+
+/**
+ * Generate UUID (string hash based)
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+function get_uuid(string $string = '') : string
+{
+ if ( '' === $string ) {
+ //Create random string
+ $string = uniqid(microtime() . getmypid() . mt_rand(), true);
+ }
+
+ $start = 0;
+ $codes = [];
+ $length = [
+ 8,
+ 4,
+ 4,
+ 4,
+ 12,
+ ];
+ $string = hash('md5', $string);
+
+ foreach ($length as $len) {
+ $codes[] = substr($string, $start, $len);
+ $start += $len;
+ }
+
+ $uuid = implode('-', $codes);
+
+ unset($string, $start, $codes, $length, $len);
+ return $uuid;
+}
+
+function getFirstChar($s)
+{
+ $s0 = mb_substr($s, 0, 3); //获取名字的姓
+ $s = iconv('UTF-8', 'gb2312', $s0); //将UTF-8转换成GB2312编码
+ if ( ord($s0) > 128 ) { //汉字开头,汉字没有以U、V开头的
+ $asc = ord($s{0}) * 256 + ord($s{1}) - 65536;
+ if ( $asc >= -20319 and $asc <= -20284 ) return "A";
+ if ( $asc >= -20283 and $asc <= -19776 ) return "B";
+ if ( $asc >= -19775 and $asc <= -19219 ) return "C";
+ if ( $asc >= -19218 and $asc <= -18711 ) return "D";
+ if ( $asc >= -18710 and $asc <= -18527 ) return "E";
+ if ( $asc >= -18526 and $asc <= -18240 ) return "F";
+ if ( $asc >= -18239 and $asc <= -17760 ) return "G";
+ if ( $asc >= -17759 and $asc <= -17248 ) return "H";
+ if ( $asc >= -17247 and $asc <= -17418 ) return "I";
+ if ( $asc >= -17417 and $asc <= -16475 ) return "J";
+ if ( $asc >= -16474 and $asc <= -16213 ) return "K";
+ if ( $asc >= -16212 and $asc <= -15641 ) return "L";
+ if ( $asc >= -15640 and $asc <= -15166 ) return "M";
+ if ( $asc >= -15165 and $asc <= -14923 ) return "N";
+ if ( $asc >= -14922 and $asc <= -14915 ) return "O";
+ if ( $asc >= -14914 and $asc <= -14631 ) return "P";
+ if ( $asc >= -14630 and $asc <= -14150 ) return "Q";
+ if ( $asc >= -14149 and $asc <= -14091 ) return "R";
+ if ( $asc >= -14090 and $asc <= -13319 ) return "S";
+ if ( $asc >= -13318 and $asc <= -12839 ) return "T";
+ if ( $asc >= -12838 and $asc <= -12557 ) return "W";
+ if ( $asc >= -12556 and $asc <= -11848 ) return "X";
+ if ( $asc >= -11847 and $asc <= -11056 ) return "Y";
+ if ( $asc >= -11055 and $asc <= -10247 ) return "Z";
+ } elseif ( ord($s) >= 48 and ord($s) <= 57 ) { //数字开头
+ switch (iconv_substr($s, 0, 1, 'utf-8')) {
+ case 1:
+ return "Y";
+ case 2:
+ return "E";
+ case 3:
+ return "S";
+ case 4:
+ return "S";
+ case 5:
+ return "W";
+ case 6:
+ return "L";
+ case 7:
+ return "Q";
+ case 8:
+ return "B";
+ case 9:
+ return "J";
+ case 0:
+ return "L";
+ }
+ } elseif ( ord($s) >= 65 and ord($s) <= 90 ) { //大写英文开头
+ return substr($s, 0, 1);
+ } elseif ( ord($s) >= 97 and ord($s) <= 122 ) { //小写英文开头
+ return strtoupper(substr($s, 0, 1));
+ } else {
+ return iconv_substr($s0, 0, 1, 'utf-8');
+ //中英混合的词语,不适合上面的各种情况,因此直接提取首个字符即可
+ }
+}
+
+function strip_html_tags($tags, $str)
+{
+ $html = [];
+ foreach ($tags as $tag) $html[] = "/(<(?:\/" . $tag . "|" . $tag . ")[^>]*>)/i";
+ $data = preg_replace($html, '', $str);
+ return $data;
+}
+
+//获取本地存储的文件
+function GetLocalFileByPath($path)
+{
+ return asset('uploads') . '/' . $path;
+}
+
+//参数1:访问的URL,参数2:post数据(不填则为GET),参数3:提交的$cookies,参数4:是否返回$cookies
+function curl_request($url, $post = '', $json = false, $header = [])
+{
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $url);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
+ curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 0);
+ curl_setopt($curl, CURLOPT_AUTOREFERER, 0);
+ //curl_setopt($curl, CURLOPT_REFERER, "http://XXX");
+ if ( $json ) {
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array_merge([
+ 'Content-Type: application/json; charset=utf-8',
+ //伪造IP
+ //'CLIENT-IP:85.25.105.77','X-FORWARDED-FOR:85.25.105.77',//此处可以改为任意假IP
+ 'Content-Length: ' . (empty($post) ? 0 : strlen(http_build_query($post))),
+ ], $header));
+ }
+ curl_setopt($curl, CURLOPT_TIMEOUT, 60);
+ //不输出头信息
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ $data = curl_exec($curl);
+ if ( curl_errno($curl) ) {
+ return false;
+ //throw new Exception("Error:".curl_error($curl));
+ }
+ curl_close($curl);
+ return $data;
+}
+
+function curlRequest($url, $params = [], $method = 'POST', $header = [], $type = 'json', $options = [])
+{
+ if (empty($options)) {
+ $options = [CURLOPT_CONNECTTIMEOUT => 30, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_HTTPHEADER => ['X-REQUESTED-WITH: XMLHttpRequest']];
+ }
+
+ $method = strtoupper($method);
+ $protocol = substr($url, 0, 5);
+
+ if ($type == 'json' && is_array($params)) {
+ $query_string = json_encode($params, JSON_UNESCAPED_UNICODE);
+ }else{
+ $query_string = is_array($params) ? http_build_query($params) : $params;
+ }
+
+ $ch = curl_init();
+ $defaults = [];
+ if ('GET' == $method) {
+ $geturl = $query_string ? $url . (stripos($url, '?') !== false ? '&' : '?') . $query_string : $url;
+ $defaults[CURLOPT_URL] = $geturl;
+ } else {
+ $defaults[CURLOPT_URL] = $url;
+ if ($method == 'POST') {
+ $defaults[CURLOPT_POST] = 1;
+ } else {
+ $defaults[CURLOPT_CUSTOMREQUEST] = $method;
+ }
+ $defaults[CURLOPT_POSTFIELDS] = $query_string;
+ }
+
+ $defaults[CURLOPT_HEADER] = false;
+ $defaults[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36';
+ $defaults[CURLOPT_FOLLOWLOCATION] = true;
+ $defaults[CURLOPT_RETURNTRANSFER] = true;
+ $defaults[CURLOPT_CONNECTTIMEOUT] = 3;
+ $defaults[CURLOPT_TIMEOUT] = 3;
+
+ // disable 100-continue
+ curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);
+
+ if ('https' == $protocol) {
+ $defaults[CURLOPT_SSL_VERIFYPEER] = false;
+ $defaults[CURLOPT_SSL_VERIFYHOST] = false;
+ }
+
+ curl_setopt_array($ch, (array)$options + $defaults);
+
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge([
+ 'Content-Type: application/json; charset=utf-8',
+ //伪造IP
+ 'CLIENT-IP:85.25.105.77',
+ 'X-FORWARDED-FOR:85.25.105.77',//此处可以改为任意假IP
+ 'Content-Length: ' . strlen($query_string),
+ ], $header)
+ );
+
+ $ret = curl_exec($ch);
+ $err = curl_error($ch);
+
+ curl_close($ch);
+
+ return json_decode($ret, true);
+}
+
+//过滤值为空的数组
+function filterEmptyArr($array)
+{
+
+ if ( !is_array($array) ) return false;
+
+ $return_arr = [];
+ foreach ($array as $k => $v) {
+ if ( is_array($v) ) {
+
+ foreach ($v as $k1 => $v1) {
+ if ( $v1 ) {
+ $return_arr[$k] = $v;
+ }
+ }
+ } elseif ( $v ) {
+ $return_arr[$k] = $v;
+ }
+ }
+
+ return $return_arr;
+}
+
+//验证数组是否为空,除了指定的key
+function CheckArrIsEmpty($arr, $except = [])
+{
+ if ( !is_array($arr) ) return false;
+ foreach ($arr as $k => $value) {
+ if ( is_array($value) ) {
+ CheckArrIsEmpty($value);
+ } elseif ( !$value && $value != 0 ) {
+ if ( !in_array($k, $except) ) {
+ throw new Exception($k . " is empty.", 40000);
+ }
+ }
+ }
+}
+
+/**
+ * 获取客户端浏览器信息
+ *
+ * @param null
+ *
+ * @return string
+ * @author huang
+ */
+function get_broswer()
+{
+ $sys = $_SERVER['HTTP_USER_AGENT']; //获取用户代理字符串
+ if ( stripos($sys, "Firefox/") > 0 ) {
+ preg_match("/Firefox\/([^;)]+)+/i", $sys, $b);
+ $exp[0] = "Firefox";
+ $exp[1] = $b[1]; //获取火狐浏览器的版本号
+ } elseif ( stripos($sys, "Maxthon") > 0 ) {
+ preg_match("/Maxthon\/([\d\.]+)/", $sys, $aoyou);
+ $exp[0] = "傲游";
+ $exp[1] = $aoyou[1];
+ } elseif ( stripos($sys, "MSIE") > 0 ) {
+ preg_match("/MSIE\s+([^;)]+)+/i", $sys, $ie);
+ $exp[0] = "IE";
+ $exp[1] = $ie[1]; //获取IE的版本号
+ } elseif ( stripos($sys, "OPR") > 0 ) {
+ preg_match("/OPR\/([\d\.]+)/", $sys, $opera);
+ $exp[0] = "Opera";
+ $exp[1] = $opera[1];
+ } elseif ( stripos($sys, "Edge") > 0 ) {
+ //win10 Edge浏览器 添加了chrome内核标记 在判断Chrome之前匹配
+ preg_match("/Edge\/([\d\.]+)/", $sys, $Edge);
+ $exp[0] = "Edge";
+ $exp[1] = $Edge[1];
+ } elseif ( stripos($sys, "Chrome") > 0 ) {
+ preg_match("/Chrome\/([\d\.]+)/", $sys, $google);
+ $exp[0] = "Chrome";
+ $exp[1] = $google[1]; //获取google chrome的版本号
+ } elseif ( stripos($sys, 'rv:') > 0 && stripos($sys, 'Gecko') > 0 ) {
+ preg_match("/rv:([\d\.]+)/", $sys, $IE);
+ $exp[0] = "IE";
+ $exp[1] = $IE[1];
+ } else {
+ $exp[0] = "未知浏览器";
+ $exp[1] = "";
+ }
+ return $exp[0] . '(' . $exp[1] . ')';
+}
+
+/**
+ * 获取客户端操作系统信息,包括win10
+ *
+ * @param null
+ *
+ * @return string
+ * @author huang
+ */
+function get_os()
+{
+
+ $agent = $_SERVER['HTTP_USER_AGENT'];
+ $os = false;
+
+ if ( preg_match('/win/i', $agent) && strpos($agent, '95') ) {
+ $os = 'Windows 95';
+ } elseif ( preg_match('/win 9x/i', $agent) && strpos($agent, '4.90') ) {
+ $os = 'Windows ME';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/98/i', $agent) ) {
+ $os = 'Windows 98';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt 6.0/i', $agent) ) {
+ $os = 'Windows Vista';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt 6.1/i', $agent) ) {
+ $os = 'Windows 7';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt 6.2/i', $agent) ) {
+ $os = 'Windows 8';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt 10.0/i', $agent) ) {
+ $os = 'Windows 10';#添加win10判断
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt 5.1/i', $agent) ) {
+ $os = 'Windows XP';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt 5/i', $agent) ) {
+ $os = 'Windows 2000';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/nt/i', $agent) ) {
+ $os = 'Windows NT';
+ } elseif ( preg_match('/win/i', $agent) && preg_match('/32/i', $agent) ) {
+ $os = 'Windows 32';
+ } elseif ( preg_match('/linux/i', $agent) ) {
+ $os = 'Linux';
+ } elseif ( preg_match('/unix/i', $agent) ) {
+ $os = 'Unix';
+ } elseif ( preg_match('/sun/i', $agent) && preg_match('/os/i', $agent) ) {
+ $os = 'SunOS';
+ } elseif ( preg_match('/ibm/i', $agent) && preg_match('/os/i', $agent) ) {
+ $os = 'IBM OS/2';
+ } elseif ( preg_match('/Mac/i', $agent) && preg_match('/PC/i', $agent) ) {
+ $os = 'Macintosh';
+ } elseif ( preg_match('/PowerPC/i', $agent) ) {
+ $os = 'PowerPC';
+ } elseif ( preg_match('/AIX/i', $agent) ) {
+ $os = 'AIX';
+ } elseif ( preg_match('/HPUX/i', $agent) ) {
+ $os = 'HPUX';
+ } elseif ( preg_match('/NetBSD/i', $agent) ) {
+ $os = 'NetBSD';
+ } elseif ( preg_match('/BSD/i', $agent) ) {
+ $os = 'BSD';
+ } elseif ( preg_match('/OSF1/i', $agent) ) {
+ $os = 'OSF1';
+ } elseif ( preg_match('/IRIX/i', $agent) ) {
+ $os = 'IRIX';
+ } elseif ( preg_match('/FreeBSD/i', $agent) ) {
+ $os = 'FreeBSD';
+ } elseif ( preg_match('/teleport/i', $agent) ) {
+ $os = 'teleport';
+ } elseif ( preg_match('/flashget/i', $agent) ) {
+ $os = 'flashget';
+ } elseif ( preg_match('/webzip/i', $agent) ) {
+ $os = 'webzip';
+ } elseif ( preg_match('/offline/i', $agent) ) {
+ $os = 'offline';
+ } else {
+ $os = '未知操作系统';
+ }
+ return $os;
+}
+
+//生成树形结构数据
+
+function listToTree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0)
+{
+ $tree = [];
+ if ( is_array($list) ) {
+ $refer = [];
+ foreach ($list as $key => $data) {
+ $refer[$data[$pk]] = &$list[$key];
+ }
+
+ foreach ($list as $key => $data) {
+ // 判断是否存在parent
+ $parentId = $data[$pid];
+
+ if ( $root == $parentId ) {
+ $tree[$data[$pk]] = &$list[$key];
+ } else {
+ if ( isset($refer[$parentId]) ) {
+ $parent = &$refer[$parentId];
+ $parent[$child][$data[$pk]] = &$list[$key];
+ }
+ }
+ }
+ }
+
+ return $tree;
+}
+
+//获取本地翻译语言
+function getTranslateByKey($key)
+{
+ //echo (__("admin.zh.index_allow_curl"));
+ echo __(session("admin_current_language")["shortcode"] . "." . $key);
+}
+
+function getHomeByKey($key)
+{
+ echo __(session("home_current_language")["shortcode"] . "." . $key);
+}
+
+//获取上一页的URL
+function getPreUrl()
+{
+ return url()->previous();
+}
+
+function excelTime($date, $time = false)
+{
+ if ( function_exists('GregorianToJD') ) {
+ if ( is_numeric($date) ) {
+ $jd = GregorianToJD(1, 1, 1970);
+ $gregorian = JDToGregorian($jd + intval($date) - 25569);
+ $date = explode('/', $gregorian);
+ $date_str = str_pad($date [2], 4, '0', STR_PAD_LEFT) . "-" . str_pad($date [0], 2, '0', STR_PAD_LEFT) . "-" . str_pad($date [1], 2, '0', STR_PAD_LEFT) . ($time ? " 00:00:00" : '');
+ return $date_str;
+ }
+ } else {
+ $date = $date > 25568 ? $date + 1 : 25569;
+ /*There was a bug if Converting date before 1-1-1970 (tstamp 0)*/
+ $ofs = (70 * 365 + 17 + 2) * 86400;
+ $date = date("Y-m-d", ($date * 86400) - $ofs) . ($time ? " 00:00:00" : '');
+ }
+ return $date;
+}
+
+//判断是否是微信浏览器
+function is_weixin()
+{
+
+ if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false ) {
+ return true;
+ }
+ return false;
+}
+
+//是否为手机号码
+if ( !function_exists('is_mobile') ) {
+ function is_mobile(string $text)
+ {
+ $search = '/^0?1[3|4|5|6|7|8][0-9]\d{8}$/';
+ if ( preg_match($search, $text) ) return true; else return false;
+ }
+}
+
+//手机号码 中间4位加密
+if ( !function_exists('get_encryption_mobile') ) {
+ function get_encryption_mobile($tel)
+ {
+ $new_tel = preg_replace('/(\d{3})\d{4}(\d{4})/', '$1****$2', $tel);
+ return $new_tel;
+ }
+}
+
+//API统一的数据返回格式
+if ( !function_exists('return_api_format') ) {
+ function return_api_format($return = [])
+ {
+ $return['data'] = !isset($return['data']) ? [] : $return['data'];
+ $return['msg'] = !isset($return['msg']) ? '获取成功' : $return['msg'];
+ $return['status'] = !isset($return['status']) ? (empty($return['data']) ? 40000 : 200) : $return['status'];
+ return response()->json($return);
+ }
+}
+
+if ( !function_exists('del_dir_files') ) {
+ /**
+ * 删除文件夹与下方的所有文件
+ *
+ * @param $dirName 文件夹名称
+ * @param int $delete_dir 是否删除文件夹【1.删除;0.不删除】
+ */
+ function del_dir_files($dirName, $delete_dir = 1)
+ {
+ if ( $handle = @opendir($dirName) ) {
+ while ( false !== ($item = @readdir($handle)) ) {
+ if ( $item != '.' && $item != '..' ) {
+ if ( is_dir($dirName . '/' . $item) ) del_dir_files($dirName . '/' . $item); else @unlink($dirName . '/' . $item);
+ }
+ }
+ @closedir($handle);
+ }
+ if ( $delete_dir == 1 ) @rmdir($dirName);
+ }
+}
+
+if ( !function_exists('get_file_filtering') ) {
+ /**
+ * 获取指定格式的文件
+ *
+ * @param array $array
+ * @param array $format
+ *
+ * @return array
+ */
+ function get_file_filtering($array = [], $format = [])
+ {
+ $return = [];
+ if ( empty($array) || empty($format) ) return $return;
+ foreach ($array as $key => $value) {
+ $arr = pathinfo($value);
+ if ( !empty($arr['extension']) && in_array($arr['extension'], $format) ) $return[] = $value;
+ }
+ return $return;
+ }
+}
+
+if ( !function_exists('write_lock_file') ) {
+ /**
+ * 写入锁文件
+ *
+ * @param $path
+ */
+ function write_lock_file($path, $content = '')
+ {
+ $lock_file = fopen($path . '/lock', 'w+');//创建 锁文件
+ fwrite($lock_file, empty($content) ? date('Y-m-d H:i:s') : $content);//写入
+ }
+}
+
+if ( !function_exists('del_dir_files') ) {
+ /**
+ * 删除文件夹与下方的所有文件
+ *
+ * @param $dirName 文件夹名称
+ * @param int $delete_dir 是否删除文件夹【1.删除;0.不删除】
+ */
+ function del_dir_files($dirName, $delete_dir = 1)
+ {
+ if ( $handle = @opendir($dirName) ) {
+ while ( false !== ($item = @readdir($handle)) ) {
+ if ( $item != '.' && $item != '..' ) {
+ if ( is_dir($dirName . '/' . $item) ) del_dir_files($dirName . '/' . $item); else @unlink($dirName . '/' . $item);
+ }
+ }
+ @closedir($handle);
+ }
+ if ( $delete_dir == 1 ) @rmdir($dirName);
+ }
+}
+
+if ( !function_exists('get_client_info') ) {
+ /**
+ * 获取IP与浏览器信息、语言
+ */
+ function get_client_info() : array
+ {
+ if ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) ) {
+ $XFF = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ $client_pos = strpos($XFF, ', ');
+ $client_ip = false !== $client_pos ? substr($XFF, 0, $client_pos) : $XFF;
+ unset($XFF, $client_pos);
+ } else $client_ip = $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? $_SERVER['LOCAL_ADDR'] ?? '0.0.0.0';
+ $client_lang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 5) : '';
+ $client_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ return [
+ 'ip' => &$client_ip,
+ 'lang' => &$client_lang,
+ 'agent' => &$client_agent,
+ ];
+ }
+}
+
+if ( !function_exists('get_ip') ) {
+ function get_ip() : string
+ {
+ $data = get_client_info();
+ return empty($data['ip']) ? '' : $data['ip'];
+ }
+}
+
+if ( !function_exists('get_month_days') ) {
+ /**
+ * 获取某月份的所有日期列表
+ *
+ * @param string $time
+ * @param string $format
+ *
+ * @return array
+ */
+ function get_month_days($time = '', $format = 'Y-m-d')
+ {
+ $time = $time != '' ? $time : time();
+ //获取当前周几
+ $week = date('d', $time);
+ $date = [];
+ for ($i = 1; $i <= date('t', $time); $i++) {
+ $date[$i] = date($format, strtotime('+' . $i - $week . ' days', $time));
+ }
+ return $date;
+ }
+}
+
+if ( !function_exists('get_month_range') ) {
+ /**
+ * 指定日期范围之内的所有月份
+ *
+ * @param string $start_date 开始日期
+ * @param string $end_date 结束日期
+ * @param string $format 返回格式
+ *
+ * @return array
+ */
+ function get_month_range(string $start_date, string $end_date, string $format = 'Y-m')
+ {
+ $end = date($format, strtotime($end_date)); // 转换为月
+ $range = [];
+ $i = 0;
+ do {
+ $month = date($format, strtotime($start_date . ' + ' . $i . ' month'));
+ $range[] = $month;
+ $i++;
+ } while ( $month < $end );
+ return $range;
+ }
+}
+
+if ( !function_exists('get_days_range') ) {
+ /**
+ * 指定日期范围之内的所有天
+ *
+ * @param string $start_date 开始日期
+ * @param string $end_date 结束日期
+ * @param string $format 返回格式
+ *
+ * @return array
+ */
+ function get_days_range(string $start_date, string $end_date, string $format = 'Y-m-d')
+ {
+ $end = date($format, strtotime($end_date)); // 转换为月
+ $range = [];
+ $i = 0;
+ do {
+ $day = date($format, strtotime($start_date . ' + ' . $i . ' day'));
+ $range[] = $day;
+ $i++;
+ } while ( $day < $end );
+ return $range;
+ }
+}
+
+if ( !function_exists('get_years_range') ) {
+ /**
+ * 指定日期范围之内的所有年份
+ *
+ * @param string $start_date 开始日期
+ * @param string $end_date 结束日期
+ * @param string $format 返回格式
+ *
+ * @return array
+ */
+ function get_years_range(string $start_date, string $end_date, string $format = 'Y')
+ {
+ $start_date = date('Y-m-d', strtotime($start_date . '-01-01'));
+ $end = date($format, strtotime($end_date)); // 转换为年
+ $range = [];
+ $i = 0;
+ do {
+ $year = date($format, strtotime($start_date . ' + ' . $i . ' year'));
+ $range[] = $year;
+ $i++;
+ } while ( $year < $end );
+ return $range;
+ }
+}
+
+if ( !function_exists('set_month_format') ) {
+ /**
+ * 设置 月份 的格式统一
+ *
+ * @param $month
+ *
+ * @return string
+ */
+ function set_month_format($month)
+ {
+ return (string)(strlen($month) == 1 ? '0' . $month : $month);
+ }
+}
+
+if ( !function_exists('get_mail_template_message') ) {
+ /**
+ * 获取模板消息内容
+ *
+ * @param $content
+ * @param string $change 也可以是数组
+ * @param string $code 对应数组
+ *
+ * @return mixed
+ */
+ function get_mail_template_message($content, $change = '', $code = '{$code}')
+ {
+ return str_replace($code, $change, $content);
+ }
+}
+
+/**
+ * 获取当前host域名
+ *
+ * @return string
+ */
+function get_host()
+{
+ if ( isset($_SERVER['HTTP_HOST']) ) {
+ $scheme = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
+ $SCRIPT_NAME = $_SERVER['SCRIPT_NAME'];
+ $arr = explode('/', $SCRIPT_NAME);
+ $url = $scheme . $_SERVER['HTTP_HOST'] . substr($SCRIPT_NAME, 0, -strlen($arr[count($arr) - 2])) . '/';
+ return $url;
+ //$scheme = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
+ //$SCRIPT_NAME = rtrim($_SERVER['REQUEST_URI'], '/');
+ //$arr = explode('/', $SCRIPT_NAME);
+ //$url = $scheme . $_SERVER['HTTP_HOST'] . substr($SCRIPT_NAME, 0, -strlen($arr[count($arr) - 2]));
+ //return $url;
+ } else {
+ if ( empty($baseUrl) ) {
+ $request = \think\facade\Request::instance();
+ $subDir = str_replace('\\', '/', dirname($request->server('PHP_SELF')));
+ $baseUrl = $request->scheme() . '://' . $request->host() . $subDir . ($subDir === '/' ? '' : '/');
+ }
+
+ return trim($baseUrl, '/');
+ }
+}
+
+/**
+ * [is_email]
+ *
+ * @param string $email [description]
+ *
+ * @return boolean [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:检测邮箱格式
+ * @englishAnnotation:
+ * @version :1.0
+ */
+function is_email(string $email) : bool
+{
+ // '/^[a-z0-9]+([._-][a-z0-9]+)*@([0-9a-z]+\.[a-z]{2,14}(\.[a-z]{2})?)$/i';
+ $checkmail = "/\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/";//定义正则表达式
+ if ( preg_match($checkmail, $email) ) return true; else return false;
+}
+
+/**
+ * [is_mobile]
+ *
+ * @param [type] $text [description]
+ *
+ * @return bool|boolean [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :检测手机号格式是否正确
+ * @englishAnnotation :
+ * @version :1.0
+ */
+function is_mobile(string $text) : bool
+{
+ $search = '/^0?1[3|4|5|6|7|8][0-9]\d{8}$/';
+ if ( preg_match($search, $text) ) return true; else return false;
+}
+
+/**
+ * [check_url]
+ *
+ * @param string $_url [description]
+ *
+ * @return bool
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:检测URL地址格式
+ * @englishAnnotation:
+ * @version :1.0
+ */
+function check_url(string $_url) : bool
+{
+ $str = "/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/";
+ if ( !preg_match($str, $_url) ) return false; else return true;
+}
+
+/**
+ * [roundDownDecimal]
+ *
+ * @param float|integer $money_num [description]
+ * @param int|integer $length [description]
+ *
+ * @return float
+ * @version :1.0
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:保留几位小数,向下取,就是直接截断 3 为小数即可。
+ * @englishAnnotation:
+ */
+function roundDownDecimal(float $money_num = 0, int $length = 2) : float
+{
+ return substr($money_num, 0, strlen($money_num) - _getFloatLength($money_num) + $length);
+}
+
+if ( !function_exists('writeLog') ) {
+ function writeLog($msg, $file_name, $path = __DIR__)
+ {
+ /**
+ * 第一部分路径
+ */
+ $dirPath = $path . '/logs';
+ if ( !is_dir($dirPath) ) @mkdir($dirPath);
+ $dirPath .= '/' . date('Y');
+ if ( !is_dir($dirPath) ) @mkdir($dirPath);
+ $dirPath .= '/' . date('n');
+ if ( !is_dir($dirPath) ) @mkdir($dirPath);
+ /**
+ * 第二部分
+ */
+ file_put_contents($dirPath . '/' . (empty($file_name) ? date('j') : $file_name) . '.txt', "\n\n" . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
+ file_put_contents($dirPath . '/' . (empty($file_name) ? date('j') : $file_name) . '.txt', print_r($msg, true), FILE_APPEND);
+ }
+}
+
+/**
+ * 通过省市区的名称获取对应的经纬度
+ *
+ * @param string $area_name
+ *
+ * @return string
+ */
+function getNameAcquisitionLongitudeAndLatitude($area_name = '')
+{
+ if ( empty($area_name) ) return '';
+ $res = request_function('https://restapi.amap.com/v3/geocode/geo', [
+ 'address' => $area_name,
+ 'key' => MyC('common_lbsamap_apikey'),
+ 'output' => 'JSON',
+ ], false);
+ if ( $res['status'] == 1 ) return empty($res['geocodes']) ? '' : $res['geocodes'][0]['location'];
+ return '';
+}
+
+/**
+ * 倒计时 转化成 天-时分秒 展示
+ *
+ * @param $time
+ *
+ * @return string
+ */
+function countdownConversionTime($time)
+{
+ if ( empty($time) ) return '';
+ $day = intval($time / (60 * 60 * 24));
+ $hour = intval($time / (60 * 60) - $day * 24);
+ $minute = intval($time / 60 - $day * 24 * 60 - $hour * 60);
+ $second = intval($time - $day * 24 * 60 * 60 - $hour * 60 * 60 - $minute * 60);
+
+ $day = (intval($day) <= 0) ? '' : $day . '天 ';
+ if ( intval($hour) <= 9 ) $hour = '0' . $hour;
+ if ( intval($minute) <= 9 ) $minute = '0' . $minute;
+ if ( intval($second) <= 9 ) $second = '0' . $second;
+ return $day . $hour . ':' . $minute . ':' . $second . '';
+}
+
+if ( !function_exists('returnLastPage') ) {
+ function returnLastPage()
+ {
+ echo '';
+ }
+}
+
+if ( !function_exists('array_key_first') ) {
+ /**
+ * Gets the first key of an array
+ *
+ * @param array $array
+ *
+ * @return mixed
+ */
+ function array_key_first(array $array)
+ {
+ if ( count($array) ) {
+ reset($array);
+ return key($array);
+ }
+ return null;
+ }
+}
+
+function hidden_mobile(string $text) : string
+{
+ $start = substr($text, 0, 3);
+ $end = substr($text, -4, 4);
+ return $start . ' **** ' . $end;
+}
+
+function msubstr($str, $start = 0, $length, $charset = "utf-8", $suffix = true)
+{
+ if ( function_exists("mb_substr") ) {
+ if ( $suffix ) return mb_substr($str, $start, $length, $charset) . "..."; else
+ return mb_substr($str, $start, $length, $charset);
+ } elseif ( function_exists('iconv_substr') ) {
+ if ( $suffix ) return iconv_substr($str, $start, $length, $charset) . "..."; else
+ return iconv_substr($str, $start, $length, $charset);
+ }
+ $re['utf-8'] = "/[x01-x7f]|[xc2-xdf][x80-xbf]|[xe0-xef]
+[x80-xbf]{2}|[xf0-xff][x80-xbf]{3}/";
+ $re['gb2312'] = "/[x01-x7f]|[xb0-xf7][xa0-xfe]/";
+ $re['gbk'] = "/[x01-x7f]|[x81-xfe][x40-xfe]/";
+ $re['big5'] = "/[x01-x7f]|[x81-xfe]([x40-x7e]|xa1-xfe])/";
+ preg_match_all($re[$charset], $str, $match);
+ $slice = join("", array_slice($match[0], $start, $length));
+ if ( $suffix ) return $slice . "…";
+ return $slice;
+}
+
+// function hidden_bank(string $text, $start = 1): string
+// {
+// $start = substr($text, 0, 6);
+// $end = substr($text, -4, 4);
+// return $start . ($start == 1 ? ' ' : '') . '········' . ($start == 1 ? ' ' : '') . $end;
+// return $start . ($start == 1 ? ' ' : '') . '**** ****' . ($start == 1 ? ' ' : '') . $end;
+// }
+
+function hidden_bank(string $text) : string
+{
+ $start = substr($text, 0, 4);
+ $end = substr($text, -4, 4);
+ return $start . ' **** **** ' . $end;
+}
+
+function get_month_first_day()
+{
+ return date('Y-m-01', strtotime(date("Y-m-d", time())));
+}
+
+function get_month_last_day()
+{
+ $date = date('Y-m-01', strtotime(date("Y-m-d", time())));
+ return date('Y-m-d', strtotime("$date +1 month -1 day"));
+}
+
+/**
+ * [Xml_Array xml转数组]
+ *
+ * @param [xml] $xmlstring [xml数据]
+ *
+ * @return [array] [array数组]
+ */
+function Xml_Array($xmlstring)
+{
+ return json_decode(json_encode((array)simplexml_load_string($xmlstring)), true);
+}
+
+/**
+ * [IsMobile 是否是手机访问]
+ *
+ * @return [boolean] [手机访问true, 则false]
+ */
+function IsMobile()
+{
+ /* 如果有HTTP_X_WAP_PROFILE则一定是移动设备 */
+ if ( isset($_SERVER['HTTP_X_WAP_PROFILE']) ) return true;
+
+ /* 此条摘自TPM智能切换模板引擎,适合TPM开发 */
+ if ( isset($_SERVER['HTTP_CLIENT']) && 'PhoneClient' == $_SERVER['HTTP_CLIENT'] ) return true;
+
+ /* 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息 */
+ if ( isset($_SERVER['HTTP_VIA']) && stristr($_SERVER['HTTP_VIA'], 'wap') !== false ) return true;
+
+ /* 判断手机发送的客户端标志,兼容性有待提高 */
+ if ( isset($_SERVER['HTTP_USER_AGENT']) ) {
+ $clientkeywords = [
+ 'nokia',
+ 'sony',
+ 'ericsson',
+ 'mot',
+ 'samsung',
+ 'htc',
+ 'sgh',
+ 'lg',
+ 'sharp',
+ 'sie-',
+ 'philips',
+ 'panasonic',
+ 'alcatel',
+ 'lenovo',
+ 'iphone',
+ 'ipad',
+ 'ipod',
+ 'blackberry',
+ 'meizu',
+ 'android',
+ 'netfront',
+ 'symbian',
+ 'ucweb',
+ 'windowsce',
+ 'palm',
+ 'operamini',
+ 'operamobi',
+ 'openwave',
+ 'nexusone',
+ 'cldc',
+ 'midp',
+ 'wap',
+ 'mobile',
+ ];
+ /* 从HTTP_USER_AGENT中查找手机浏览器的关键字 */
+ if ( preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT'])) ) {
+ return true;
+ }
+ }
+
+ /* 协议法,因为有可能不准确,放到最后判断 */
+ if ( isset($_SERVER['HTTP_ACCEPT']) ) {
+ /* 如果只支持wml并且不支持html那一定是移动设备 */
+ /* 如果支持wml和html但是wml在html之前则是移动设备 */
+ if ( (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html'))) ) return true;
+ }
+ return false;
+}
+
+/**
+ * [EmptyDir 清空目录下所有文件]
+ *
+ * @param [string] $dir_path [目录地址]
+ *
+ * @return [boolean] [成功true, 失败false]
+ */
+function EmptyDir($dir_path)
+{
+ if ( is_dir($dir_path) ) {
+ $dn = @opendir($dir_path);
+ if ( $dn !== false ) {
+ while ( false !== ($file = readdir($dn)) ) {
+ if ( $file != '.' && $file != '..' ) {
+ if ( !unlink($dir_path . $file) ) {
+ return false;
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * [FileSizeByteToUnit 文件大小转常用单位]
+ *
+ * @param [int] $bit [字节数]
+ *
+ * @return string
+ */
+function FileSizeByteToUnit($bit) : string
+{
+ //单位每增大1024,则单位数组向后移动一位表示相应的单位
+ $type = [
+ 'Bytes',
+ 'KB',
+ 'MB',
+ 'GB',
+ 'TB',
+ ];
+ for ($i = 0; $bit >= 1024; $i++) {
+ $bit /= 1024;
+ }
+
+ //floor是取整函数,为了防止出现一串的小数,这里取了两位小数
+ return (floor($bit * 100) / 100) . $type[$i];
+}
+
+/**
+ * json带格式输出
+ *
+ * @param [array] $data [数据]
+ * @param [string] $indent [缩进字符,默认4个空格 ]
+ *
+ * @return string
+ */
+function JsonFormat($data, $indent = null) : string
+{
+ // json encode
+ $data = json_encode($data, JSON_UNESCAPED_UNICODE);
+
+ // 缩进处理
+ $ret = '';
+ $pos = 0;
+ $length = strlen($data);
+ $indent = isset($indent) ? $indent : ' ';
+ $newline = "\n";
+ $prevchar = '';
+ $outofquotes = true;
+
+ for ($i = 0; $i <= $length; $i++) {
+ $char = substr($data, $i, 1);
+
+ if ( $char == '"' && $prevchar != '\\' ) {
+ $outofquotes = !$outofquotes;
+ } elseif ( ($char == '}' || $char == ']') && $outofquotes ) {
+ $ret .= $newline;
+ $pos--;
+ for ($j = 0; $j < $pos; $j++) {
+ $ret .= $indent;
+ }
+ }
+
+ $ret .= $char;
+
+ if ( ($char == ',' || $char == '{' || $char == '[') && $outofquotes ) {
+ $ret .= $newline;
+ if ( $char == '{' || $char == '[' ) {
+ $pos++;
+ }
+
+ for ($j = 0; $j < $pos; $j++) {
+ $ret .= $indent;
+ }
+ }
+
+ $prevchar = $char;
+ }
+
+ return $ret;
+}
+
+/**
+ * 根据身份证号码得到年龄
+ *
+ * @param $id
+ *
+ * @return float|int|string
+ */
+function getAgeByID(string $id)
+{ //过了这年的生日才算多了1周岁
+
+ if ( empty($id) ) return '';
+
+ $date = strtotime(substr($id, 6, 8)); //获得出生年月日的时间戳
+
+ $today = strtotime('today'); //获得今日的时间戳
+
+ $diff = floor(($today - $date) / 86400 / 365); //得到两个日期相差的大体年数
+
+ //strtotime加上这个年数后得到那日的时间戳后与今日的时间戳相比
+
+ $age = strtotime(substr($id, 6, 8) . '+' . $diff . 'years') > $today ? ($diff + 1) : $diff;
+
+ return $age;
+}
+
+/**
+ * [api_url]
+ *
+ * @return [string] [URL]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :API请求地址
+ * @englishAnnotation :API request URL address
+ */
+function web_url()
+{
+ return http_type() . $_SERVER['HTTP_HOST'];
+}
+
+/**
+ * [http_type]
+ *
+ * @return [string] [description]
+ * @author :cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation :获取http类型:http\https
+ * @englishAnnotation :Get the HTTP type: http\https
+ */
+function http_type() : string
+{
+ return $http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
+}
+
+/**
+ * 格式化字节大小
+ *
+ * @param number $size 字节数
+ * @param string $delimiter 数字和单位分隔符
+ *
+ * @return string 格式化后的带单位的大小
+ */
+function format_bytes($size, $delimiter = '')
+{
+ $units = [
+ 'B',
+ 'KB',
+ 'MB',
+ 'GB',
+ 'TB',
+ 'PB',
+ ];
+ for ($i = 0; $size >= 1024 && $i < 5; $i++) {
+ $size /= 1024;
+ }
+ return $size . $delimiter . $units[$i];
+}
+
+/**
+ * CURL发送Request请求,含POST和REQUEST
+ *
+ * @param string $url 请求的链接
+ * @param mixed $params 传递的参数
+ * @param string $method 请求的方法
+ * @param mixed $options CURL的参数
+ *
+ * @return array
+ */
+function send_request($url, $params = [], $method = 'POST', $type = 'json', $options = [])
+{
+ if ( empty($options) ) {
+ $options = [
+ CURLOPT_CONNECTTIMEOUT => 30,
+ CURLOPT_TIMEOUT => 30,
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_HTTPHEADER => ['X-REQUESTED-WITH: XMLHttpRequest'],
+ ];
+ }
+
+ $params = array_merge([
+ 'origin_host' => get_host(),
+ 'origin_url' => $_SERVER['REQUEST_URI'],
+ 'origin_ip' => get_ip(),
+ ], $params);
+ $method = strtoupper($method);
+ $protocol = substr($url, 0, 5);
+ if ( $type == 'json' && is_array($params) ) {
+ $params = json_encode($params, JSON_UNESCAPED_UNICODE);
+ }
+ $query_string = is_array($params) ? http_build_query($params) : $params;
+
+ $ch = curl_init();
+ $defaults = [];
+ if ( 'GET' == $method ) {
+ $geturl = $query_string ? $url . (stripos($url, '?') !== false ? '&' : '?') . $query_string : $url;
+ $defaults[CURLOPT_URL] = $geturl;
+ } else {
+ $defaults[CURLOPT_URL] = $url;
+ if ( $method == 'POST' ) {
+ $defaults[CURLOPT_POST] = 1;
+ } else {
+ $defaults[CURLOPT_CUSTOMREQUEST] = $method;
+ }
+ $defaults[CURLOPT_POSTFIELDS] = $query_string;
+ }
+
+ $defaults[CURLOPT_HEADER] = false;
+ $defaults[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36';
+ $defaults[CURLOPT_FOLLOWLOCATION] = true;
+ $defaults[CURLOPT_RETURNTRANSFER] = true;
+ $defaults[CURLOPT_CONNECTTIMEOUT] = 3;
+ $defaults[CURLOPT_TIMEOUT] = 3;
+
+ // disable 100-continue
+ curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);
+
+ if ( 'https' == $protocol ) {
+ $defaults[CURLOPT_SSL_VERIFYPEER] = false;
+ $defaults[CURLOPT_SSL_VERIFYHOST] = false;
+ }
+
+ curl_setopt_array($ch, (array)$options + $defaults);
+
+ $ret = curl_exec($ch);
+ $err = curl_error($ch);
+
+ if ( false === $ret || !empty($err) ) {
+ $errno = curl_errno($ch);
+ $info = curl_getinfo($ch);
+ curl_close($ch);
+ return [
+ 'ret' => false,
+ 'errno' => $errno,
+ 'msg' => $err,
+ 'info' => $info,
+ ];
+ }
+ curl_close($ch);
+
+ return json_decode($ret, true);
+}
+
+//随机验证码
+if ( !function_exists('random_verification_code') ) {
+ function random_verification_code($length = 6)
+ {
+ $code = '';
+ for ($i = 0; $i < $length; $i++) $code .= mt_rand(0, 9);
+ return $code;
+ }
+}
+
+if ( !function_exists('get_dir_files') ) {
+
+ //获取某目录下所有子文件和子目录,可以过滤
+ function get_dir_files($path, $filter = [], $onlydir = false)
+ {
+ if ( !is_dir($path) ) {
+ return false;
+ }
+ //scandir方法
+ $arr = [];
+ $data = scandir($path);
+ foreach ($data as $value) {
+ if ( $value != '.' && $value != '..' && $value != ".DS_Store" && !in_array($value, $filter) ) {
+ if ( $onlydir ) {
+ if ( is_dir($path . "/" . $value) ) {
+ $arr[] = $value;
+ } else {
+ continue;
+ }
+ } else {
+ $arr[] = $value;
+ }
+ }
+ }
+ return $arr;
+ }
+
+ // 列出指定目录下所有目录和文件
+ function get_dir_files1($dir)
+ {
+ $arr = [];
+ if ( is_dir($dir) ) {//如果是目录,则进行下一步操作
+ $d = opendir($dir);//打开目录
+ if ( $d ) {//目录打开正常
+ while ( ($file = readdir($d)) !== false ) {//循环读出目录下的文件,直到读不到为止
+ if ( $file != '.' && $file != '..' ) {//排除一个点和两个点
+ if ( is_dir($file) ) {//如果当前是目录
+ $arr[$file] = get_dir_files($file);//进一步获取该目录里的文件
+ } else {
+ $arr[] = $file;//记录文件名
+ }
+ }
+ }
+ }
+ closedir($d);//关闭句柄
+ }
+ return $arr;
+ }
+}
+
+if ( !function_exists('write_lock_file') ) {
+ /**
+ * 写入锁文件
+ *
+ * @param $path
+ * @param string $content
+ */
+ function write_lock_file($path, $content = '')
+ {
+ $lock_file = fopen($path . '/lock', 'w+');//创建 锁文件
+ fwrite($lock_file, empty($content) ? date('Y-m-d H:i:s') : $content);//写入
+ }
+}
+
+if ( !function_exists('put_file_to_zip') ) {
+ /**
+ * 把指定文件目录下的所有文件,打包压缩至压缩包内
+ *
+ * @param string $path
+ * @param $zip
+ * @param string $old_filename
+ * @param array $limit_dir 限制压缩的文件目录
+ */
+ function put_file_to_zip(string $path, $zip, string $old_filename, $limit_dir = [])
+ {
+ header('content-type:text/html;charset=utf-8');
+ $handler = opendir($path); //打开当前文件夹由$path指定。
+ while ( ($filename = readdir($handler)) !== false ) {
+ if ( $filename != '.' && $filename != '..' ) {//文件夹文件名字为'.'和‘..’,不要对他们进行操作
+ if ( is_dir($path . '/' . $filename) ) {// 如果读取的某个对象是文件夹,则递归
+ if ( !empty($limit_dir) && !in_array($filename, $limit_dir) ) continue;
+ $old_filename = (empty($old_filename) ? '' : ($old_filename . '/'));
+ $zip->addEmptyDir($old_filename . $filename);
+ put_file_to_zip($path . '/' . $filename, $zip, $old_filename . $filename);
+ } else { //将文件加入zip对象
+ $zip->addFile($path . '/' . $filename, (empty($old_filename) ? '' : ($old_filename . '/')) . $filename);
+ }
+ }
+ }
+ @closedir($path);
+ }
+}
+
+if ( !function_exists('check_http_file_exists') ) {
+ //判断远程文件是否存在
+ function check_http_file_exists($url)
+ {
+ $curl = curl_init($url);
+ // 不取回数据
+ curl_setopt($curl, CURLOPT_NOBODY, true);
+ // 发送请求
+ $result = curl_exec($curl);
+ $found = false;
+ // 如果请求没有发送失败
+ if ( $result !== false ) {
+ $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ if ( $statusCode == 200 ) $found = true;
+ }
+ curl_close($curl);
+ return $found;
+ }
+}
+
+if ( !function_exists('check_dir_exits') ) {
+ /**
+ * 检测目录是否存在,不存在则创建目录
+ *
+ * @param string $dir_path
+ */
+ function check_dir_exits(string $dir_path) : void
+ {
+ if ( !is_dir($dir_path) ) @mkdir($dir_path, 0755, true);
+ }
+}
+
+function get_url() : string
+{
+ $scheme = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
+ $url = $scheme . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/' . $_SERVER['REQUEST_URI'];
+ return $url;
+}
+
+//把时间戳转换为几分钟或几小时前或几天前
+function formatting_timestamp($time) : string
+{
+ $time = (int)substr($time, 0, 10);
+ $int = time() - $time;
+ $str = '';
+ if ( $int <= 30 ) {
+ $str = sprintf('刚刚', $int);
+ } elseif ( $int < 60 ) {
+ $str = sprintf('%d秒前', $int);
+ } elseif ( $int < 3600 ) {
+ $str = sprintf('%d分钟前', floor($int / 60));
+ } elseif ( $int < 86400 ) {
+ $str = sprintf('%d小时前', floor($int / 3600));
+ } elseif ( $int < 2592000 ) {
+ $str = sprintf('%d天前', floor($int / 86400));
+ } elseif ( date('Y', $time) == date('Y') ) {
+ $str = date('m-d H:i:s', $time);
+ } else {
+ $str = date('Y-m-d H:i:s', $time);
+ }
+ return $str;
+}
+
+// 统计字数
+function comment_count_word($str)
+{
+ //$str =characet($str);
+ //判断是否存在替换字符
+ $is_tihuan_count = substr_count($str, "龘");
+ try {
+ //先将回车换行符做特殊处理
+ $str = preg_replace('/(\r\n+|\s+| +)/', "龘", $str);
+ //处理英文字符数字,连续字母、数字、英文符号视为一个单词
+ $str = preg_replace('/[a-z_A-Z0-9-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",\'<>~`\?:;|]/', "m", $str);
+ //合并字符m,连续字母、数字、英文符号视为一个单词
+ $str = preg_replace('/m+/', "*", $str);
+ //去掉回车换行符
+ $str = preg_replace('/龘+/', "", $str);
+ //返回字数
+ return mb_strlen($str) + $is_tihuan_count;
+ } catch (Exception $e) {
+ return 0;
+ }
+}
+
+// 二分查找法
+function Binary_Search()
+{
+ //function binary_search($nums, $num)
+ //{
+ // return binary_search_internal($nums, $num, 0, count($nums) - 1);
+ //}
+ //
+ //function binary_search_internal($nums, $num, $low, $high)
+ //{
+ // if ($low > $high) {
+ // return -1;
+ // }
+ //
+ // $mid = floor(($low + $high) / 2);
+ // if ($num > $nums[$mid]) {
+ // return binary_search_internal($nums, $num, $mid + 1, $high);
+ // } elseif ($num < $nums[$mid]) {
+ // return binary_search_internal($nums, $num, $low, $mid - 1);
+ // } else {
+ // return $mid;
+ // }
+ //}
+ //
+ //$nums = [1, 2, 3, 4, 5, 6];
+ //$index = binary_search($nums, 5);
+ //print $index;
+}
+
+function exist_http($str) : bool
+{
+ return preg_match('/(http:\/\/)|(https:\/\/)/i', $str);
+}
+
+/**
+ * 生成订单号
+ *
+ * @return string
+ */
+function order_no()
+{
+ return date('Ymd') . substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
+}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
new file mode 100644
index 0000000..a0a2a8a
--- /dev/null
+++ b/app/Http/Controllers/Controller.php
@@ -0,0 +1,13 @@
+ [
+ \App\Http\Middleware\EncryptCookies::class,
+ \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+ \Illuminate\Session\Middleware\StartSession::class,
+ // \Illuminate\Session\Middleware\AuthenticateSession::class,
+ \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+ \App\Http\Middleware\VerifyCsrfToken::class,
+ \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ ],
+
+ 'api' => [
+ 'throttle:api',
+ \Illuminate\Routing\Middleware\SubstituteBindings::class,
+ ],
+ ];
+
+ /**
+ * The application's route middleware.
+ *
+ * These middleware may be assigned to groups or used individually.
+ *
+ * @var array
+ */
+ protected $routeMiddleware = [
+ 'auth' => \App\Http\Middleware\Authenticate::class,
+ 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+ 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
+ 'can' => \Illuminate\Auth\Middleware\Authorize::class,
+ 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+ 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
+ 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
+ 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+ 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+ ];
+}
diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php
new file mode 100644
index 0000000..704089a
--- /dev/null
+++ b/app/Http/Middleware/Authenticate.php
@@ -0,0 +1,21 @@
+expectsJson()) {
+ return route('login');
+ }
+ }
+}
diff --git a/app/Http/Middleware/ConvertEmptyStringsToNull.php b/app/Http/Middleware/ConvertEmptyStringsToNull.php
new file mode 100644
index 0000000..386c627
--- /dev/null
+++ b/app/Http/Middleware/ConvertEmptyStringsToNull.php
@@ -0,0 +1,32 @@
+getMethod());
+ if (
+ $method == 'POST'
+ ||
+ $method == 'PUT'
+ ){
+ // 希望在POST、PUT时,新增与更新数据时,移除[空字符串自动转换为null的中间件]效果。
+ }else{
+ (new \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull)->handle($request, $next);
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php
new file mode 100644
index 0000000..033136a
--- /dev/null
+++ b/app/Http/Middleware/EncryptCookies.php
@@ -0,0 +1,17 @@
+check()) {
+ return redirect(RouteServiceProvider::HOME);
+ }
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php
new file mode 100644
index 0000000..5a50e7b
--- /dev/null
+++ b/app/Http/Middleware/TrimStrings.php
@@ -0,0 +1,18 @@
+allSubdomainsOfApplicationUrl(),
+ ];
+ }
+}
diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php
new file mode 100644
index 0000000..14befce
--- /dev/null
+++ b/app/Http/Middleware/TrustProxies.php
@@ -0,0 +1,23 @@
+setUserAgent('Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36 Edg/87.0.664.75');
+
+ $content = $http->fetch();
+ var_dump($content);
+ var_dump($http->getHttpCode());
+ var_dump($http->getLastError());
+ }
+
+ //Pre-defined content types
+ const CONTENT_TYPE_XML = 'application/xml; charset=UTF-8';
+ const CONTENT_TYPE_JSON = 'application/json; charset=UTF-8';
+ const CONTENT_TYPE_FORM_DATA = 'multipart/form-data';
+ const CONTENT_TYPE_URL_ENCODED = 'application/x-www-form-urlencoded';
+
+ //Job info
+ public $data = [];
+ public $file = [];
+ public $header = [];
+
+ public $url = '';
+ public $etag = '';
+ public $cookie = '';
+ public $referer = '';
+ public $modified = '';
+ public $curl_error = '';
+
+ public $proxy = '';
+ public $proxy_passwd = '';
+
+ public $max_follow = 0;
+ public $response_code = 0;
+
+ public $http_ver = 'HTTP/2'; //HTTP Version
+ public $method = 'GET'; //Request method
+ public $user_agent = 'Mozilla/5.0 (Compatible; NervSys/111)'; //User Agent string
+ public $connection = 'keep-alive'; //Connection type
+
+ public $content_type = self::CONTENT_TYPE_URL_ENCODED; //Content type
+ public $accept_charset = 'UTF-8,*;q=0'; //Accept charset
+ public $accept_encoding = 'gzip,deflate,identity,*;q=0'; //Accept encoding
+ public $accept_language = 'en-US,en,zh-CN,zh,*;q=0'; //Accept language
+
+ public $accept_type = 'application/json;q=0.9,application/xml;q=0.8,text/plain;q=0.7,text/html;q=0.6,*/*;q=0.5'; //Accept types
+
+ /**
+ * libHttp constructor.
+ *
+ * @param string $url
+ */
+ public function __construct(string $url = '')
+ {
+ $this->url = &$url;
+ unset($url);
+ }
+
+ /**
+ * Add request data
+ *
+ * @param array ...$data
+ *
+ * @return $this
+ */
+ public function addData(array ...$data): self
+ {
+ foreach ($data as $item) {
+ $this->data += $item;
+ }
+
+ unset($data, $item);
+ return $this;
+ }
+
+ /**
+ * Add upload file
+ *
+ * @param array ...$file
+ *
+ * @return $this
+ */
+ public function addFile(array ...$file): self
+ {
+ foreach ($file as $key => $val) {
+ if (file_exists($val)) {
+ $this->file[$key] = new \CURLFile($val);
+ }
+ }
+
+ unset($file, $key, $val);
+ return $this;
+ }
+
+ /**
+ * Add header data
+ *
+ * @param array $header
+ *
+ * @return $this
+ */
+ public function addHeader(array $header): self
+ {
+ $this->header += $header;
+
+ unset($header);
+ return $this;
+ }
+
+ /**
+ * Add cookie data
+ *
+ * @param array $cookie
+ *
+ * @return $this
+ */
+ public function addCookie(array $cookie): self
+ {
+ foreach ($cookie as $key => $val) {
+ if ('' !== $this->cookie) {
+ $this->cookie .= '; ';
+ }
+
+ $this->cookie .= $key . '=' . $val;
+ }
+
+ unset($cookie, $key, $val);
+ return $this;
+ }
+
+ /**
+ * Set method
+ *
+ * @param string $method
+ *
+ * @return $this
+ */
+ public function setMethod(string $method = 'POST'): self
+ {
+ $this->method = strtoupper($method);
+
+ unset($method);
+ return $this;
+ }
+
+ /**
+ * Set content type
+ *
+ * @param string $content_type
+ *
+ * @return $this
+ */
+ public function setContentType(string $content_type = self::CONTENT_TYPE_URL_ENCODED): self
+ {
+ $this->content_type = &$content_type;
+
+ unset($content_type);
+ return $this;
+ }
+
+ /**
+ * Set referer URL
+ *
+ * @param string $referer
+ *
+ * @return $this
+ */
+ public function setReferer(string $referer): self
+ {
+ $this->referer = &$referer;
+
+ unset($referer);
+ return $this;
+ }
+
+ /**
+ * Set User-Agent string
+ *
+ * @param string $user_agent
+ *
+ * @return $this
+ */
+ public function setUserAgent(string $user_agent): self
+ {
+ $this->user_agent = &$user_agent;
+
+ unset($user_agent);
+ return $this;
+ }
+
+ /**
+ * Set max follows
+ *
+ * @param int $max_follow
+ *
+ * @return $this
+ */
+ public function setMaxFollow(int $max_follow): self
+ {
+ $this->max_follow = &$max_follow;
+
+ unset($max_follow);
+ return $this;
+ }
+
+ /**
+ * Set HTTP accept types
+ *
+ * @param string $accept_type
+ *
+ * @return $this
+ */
+ public function setAcceptType(string $accept_type): self
+ {
+ $this->accept_type = &$accept_type;
+
+ unset($accept_type);
+ return $this;
+ }
+
+ /**
+ * Set ETag value
+ *
+ * @param string $etag
+ *
+ * @return $this
+ */
+ public function setETag(string $etag): self
+ {
+ $this->etag = &$etag;
+
+ unset($etag);
+ return $this;
+ }
+
+ /**
+ * Set modified since value
+ *
+ * @param string $last_modified
+ *
+ * @return $this
+ */
+ public function setLastModified(string $last_modified): self
+ {
+ $this->modified = &$last_modified;
+
+ unset($last_modified);
+ return $this;
+ }
+
+ /**
+ * Set proxy
+ *
+ * @param string $proxy
+ * @param string $proxy_passwd
+ *
+ * @return $this
+ */
+ public function setProxy(string $proxy, string $proxy_passwd): self
+ {
+ $this->proxy = &$proxy;
+ $this->proxy_passwd = &$proxy_passwd;
+
+ unset($proxy, $proxy_passwd);
+ return $this;
+ }
+
+ /**
+ * Fetch response data
+ *
+ * @param bool $with_body
+ * @param bool $with_header
+ *
+ * @return string
+ * @throws \Exception
+ */
+ public function fetch(bool $with_body = true, bool $with_header = false): string
+ {
+ if ('' === $this->url) {
+ throw new \Exception('URL not set!', E_USER_NOTICE);
+ }
+
+ //Prepare data
+ if (!empty($this->file)) {
+ $this->data += $this->file;
+ $this->content_type = self::CONTENT_TYPE_FORM_DATA;
+ }
+
+ //Set method
+ if (!empty($this->data)) {
+ $this->method = 'POST';
+ }
+
+ //Get URL units
+ $url_unit = $this->getUrlUnit($this->url);
+
+ //Get cURL headers
+ $header = $this->getHeader($url_unit);
+
+ //Initialize
+ $opt = [];
+ $curl = curl_init();
+
+ //Build options
+ $opt[CURLOPT_URL] = $this->url;
+ $opt[CURLOPT_PORT] = &$url_unit['port'];
+ $opt[CURLOPT_TIMEOUT] = 60;
+ $opt[CURLOPT_NOSIGNAL] = true;
+ $opt[CURLOPT_AUTOREFERER] = true;
+ $opt[CURLOPT_COOKIESESSION] = true;
+ $opt[CURLOPT_RETURNTRANSFER] = true;
+ $opt[CURLOPT_SSL_VERIFYHOST] = 2;
+ $opt[CURLOPT_SSL_VERIFYPEER] = false;
+ $opt[CURLOPT_HTTPHEADER] = &$header;
+ $opt[CURLOPT_ENCODING] = $this->accept_encoding;
+ $opt[CURLOPT_USERAGENT] = $this->user_agent;
+ $opt[CURLOPT_CUSTOMREQUEST] = strtoupper($this->method);
+ $opt[CURLOPT_POST] = ('POST' === $this->method);
+ $opt[CURLOPT_NOBODY] = !$with_body;
+ $opt[CURLOPT_HEADER] = &$with_header;
+
+ if ('' !== $this->cookie) {
+ $opt[CURLOPT_COOKIE] = $this->cookie;
+ }
+
+ if ('' !== $this->referer) {
+ $opt[CURLOPT_REFERER] = $this->referer;
+ }
+
+ if (0 < $this->max_follow) {
+ $opt[CURLOPT_FOLLOWLOCATION] = true;
+ $opt[CURLOPT_MAXREDIRS] = $this->max_follow;
+ }
+
+ if ('' !== $this->proxy) {
+ $opt[CURLOPT_PROXY] = $this->proxy;
+
+ if ('' !== $this->proxy_passwd) {
+ $opt[CURLOPT_PROXYUSERPWD] = $this->proxy_passwd;
+ }
+ }
+
+ if (!empty($this->data)) {
+ switch ($this->content_type) {
+ case self::CONTENT_TYPE_JSON:
+ $opt[CURLOPT_POSTFIELDS] = json_encode($this->data);
+ break;
+
+ case self::CONTENT_TYPE_XML:
+ $opt[CURLOPT_POSTFIELDS] = IOUnit::new()->toXml($this->data);
+ break;
+
+ case self::CONTENT_TYPE_URL_ENCODED:
+ $opt[CURLOPT_POSTFIELDS] = http_build_query($this->data);
+ break;
+
+ default:
+ $opt[CURLOPT_POSTFIELDS] = &$this->data;
+ break;
+ }
+ }
+
+ //Set cURL options
+ curl_setopt_array($curl, $opt);
+
+ //Get response
+ $response = curl_exec($curl);
+
+ //Collect HTTP CODE or ERROR
+ false !== $response
+ ? $this->response_code = curl_getinfo($curl, CURLINFO_RESPONSE_CODE)
+ : $this->curl_error = curl_error($curl);
+
+ //Close cURL handle
+ curl_close($curl);
+
+ unset($opt, $curl, $key, $val);
+ return (string)$response;
+ }
+
+ /**
+ * Get last HTTP response code
+ *
+ * @return string
+ */
+ public function getHttpCode()
+ {
+ return $this->response_code;
+ }
+
+ /**
+ * Get last HTTP curl error
+ *
+ * @return string
+ */
+ public function getLastError(): string
+ {
+ return $this->curl_error;
+ }
+
+ /**
+ * Extract URL units
+ *
+ * @param string $url
+ *
+ * @return array
+ */
+ private function getUrlUnit(string $url): array
+ {
+ //Parse URL
+ $unit = parse_url($url);
+
+ //Check main components
+ if (false === $unit || !isset($unit['scheme']) || !isset($unit['host'])) {
+ return [];
+ }
+
+ //Prepare URL unit
+ if (!isset($unit['path'])) {
+ $unit['path'] = '/';
+ }
+
+ $unit['query'] = isset($unit['query']) ? '?' . $unit['query'] : '';
+
+ if (!isset($unit['port'])) {
+ $unit['port'] = 'https' === $unit['scheme'] ? 443 : 80;
+ }
+
+ unset($url);
+ return $unit;
+ }
+
+ /**
+ * get request header
+ *
+ * @param array $url_unit
+ *
+ * @return array
+ */
+ private function getHeader(array $url_unit): array
+ {
+ $header_list = ['Host' => $url_unit['host'] . ':' . $url_unit['port']];
+
+ if (!empty($this->header)) {
+ $header_list += $this->header;
+ }
+
+ if ('' !== $this->cookie) {
+ $header_list['Cookie'] = $this->cookie;
+ }
+
+ if ('' !== $this->etag) {
+ $header_list['If-None-Match'] = $this->etag;
+ }
+
+ if ('' !== $this->modified) {
+ $header_list['If-Modified-Since'] = $this->modified;
+ }
+
+ $header_list += [
+ 'Accept' => $this->accept_type,
+ 'Accept-Charset' => $this->accept_charset,
+ 'Accept-Encoding' => $this->accept_encoding,
+ 'Accept-Language' => $this->accept_language,
+ 'Content-Type' => $this->content_type,
+ 'User-Agent' => $this->user_agent,
+ 'Connection' => $this->connection
+ ];
+
+ $headers = [$this->method . ' ' . $url_unit['path'] . $url_unit['query'] . ' ' . $this->http_ver];
+
+ foreach ($header_list as $key => $val) {
+ $headers[] = $key . ': ' . $val;
+ }
+
+ unset($url_unit, $header_list, $key, $val);
+ return $headers;
+ }
+}
diff --git a/app/Library/SystemInfo.php b/app/Library/SystemInfo.php
new file mode 100644
index 0000000..1bf1755
--- /dev/null
+++ b/app/Library/SystemInfo.php
@@ -0,0 +1,191 @@
+ 0,
+ // 空闲磁盘
+ 'free' => 0,
+ // 已使用的磁盘
+ 'used' => 0,
+ // 磁盘占用率
+ 'usage_ratio' => 0,
+
+ // 单位符
+ 'unit_symbol' => 'G',
+ ];
+
+ /**
+ * 获取磁盘相关数据
+ *
+ * @return array
+ */
+ public function getDisk()
+ {
+ if ( is_windows() ) { //windows服务器
+ // 总磁盘(kb -> k -> M -> G)
+ $this->disk['total'] = round(disk_total_space("C:") / 1024 / 1024 / 1024, 2);
+ $this->disk['free'] = round(disk_free_space("C:") / 1024 / 1024 / 1024, 2);
+ $this->disk['used'] = round($this->disk['total'] - $this->disk['free'], 2);
+ $this->disk['usage_ratio'] = round(round($this->disk['free'] / $this->disk['total'], 4) * 100, 2);
+ } else {
+ //获取磁盘占用率
+ $fp = popen('df -lh | grep -E "^(/)"', "r");
+ $rs = fread($fp, 1024);
+ pclose($fp);
+ $rs = preg_replace('/\s{2,}/', ' ', $rs); //把多个空格换成 “_”
+ $hd = explode(" ", $rs);
+ $this->disk['total'] = trim($hd[1], 'G');//可用空间G
+ $this->disk['used'] = trim($hd[2], 'G');//可用空间G
+ $this->disk['free'] = trim($hd[3], 'G');//可用空间G
+ $this->disk['usage_ratio'] = trim($hd[4], '%');//已使用百分比
+ }
+ return $this->disk;
+ }
+
+ // 内存
+ private $memory = [
+ // 总内存
+ 'total' => 0,
+ // 空闲内存
+ 'free' => 0,
+ // 已使用的内存
+ 'used' => 0,
+ // 内存占用率
+ 'usage_ratio' => 0,
+ // 单位符
+ 'unit_symbol' => 'M',
+ ];
+
+ /**
+ * 获取内存相关数据
+ *
+ * @return array
+ */
+ public function getMemory()
+ {
+ if ( is_windows() ) { // windows服务器
+ $path = $this->getMemoryUsageVbsPathByWindows();
+ exec("cscript -nologo $path", $usage);
+ $memory = my_json_decode($usage[0], true);
+
+ $this->memory['total'] = round($memory['TotalVisibleMemorySize'] / 1024, 2);
+ $this->memory['free'] = round($memory['FreePhysicalMemory'] / 1024, 2);
+ $this->memory['used'] = round($this->memory['total'] - $this->memory['free'], 2);
+ $this->memory['usage_ratio'] = round(round($this->memory['used'] / $this->memory['total'], 4) * 100, 2);
+ } else {
+ //内存使用率
+ $fp = popen('top -b -n 2 | grep -E "(Mem)"', "r");
+ $rs = fread($fp, 1024);
+ $sys_info = explode("\n", $rs);
+ $mem_info = explode(",", $sys_info[2]); //内存占有量 数组
+ $this->memory['total'] = trim(trim($mem_info[0], 'KiB Mem : '), ' total');
+ $this->memory['used'] = trim(trim($mem_info[2], 'used'));
+ $this->memory['buffer_cache'] = trim(trim($mem_info[3], 'buff/cache'));
+ $this->memory['free'] = trim(trim($mem_info[1], 'free'));
+ $this->memory['usage_ratio'] = round($this->memory['used'] / intval($this->memory['total']), 4) * 100; //百分比
+ }
+ return $this->memory;
+ }
+
+ // Cpu
+ private $cpu = [
+ // 总Cpu
+ 'total' => 0,
+ // 空闲Cpu
+ 'free' => 0,
+ // 已使用的Cpu
+ 'used' => 0,
+ // Cpu占用率
+ 'usage_ratio' => 0,
+ ];
+
+ /**
+ * 获取Cpu相关数据
+ *
+ * @return array
+ */
+ public function getCpu()
+ {
+ if ( is_windows() ) { // windows服务器
+ // WINDOWS的CPU是极为不准确的。
+ $this->cpu['usage_ratio'] = (float)$this->getCpuUsage();
+ } else {
+ //获取CPU使用率以及内存使用率
+ $fp = popen('top -b -n 2 | grep -E "(Cpu)"', "r");
+ $rs = fread($fp, 1024);
+ $sys_info = explode("\n", $rs);
+ $this->cpu['usage_ratio'] = (float)trim(current(explode(',', trim($sys_info[0], '%Cpu(s): '))), 'us'); //百分比;
+ }
+ return $this->cpu;
+ }
+
+ /**
+ * 获得总内存及可用物理内存JSON vbs文件生成函数
+ *
+ * @return string 返回vbs文件路径
+ */
+ private function getMemoryUsageVbsPathByWindows()
+ {
+ return $this->getFilePath('memory_usage.vbs', "On Error Resume Next
+ Set objWMI = GetObject(\"winmgmts:\\\\.\\root\cimv2\")
+ Set colOS = objWMI.InstancesOf(\"Win32_OperatingSystem\")
+ For Each objOS in colOS
+ Wscript.Echo(\"{\"\"TotalVisibleMemorySize\"\":\" & objOS.TotalVisibleMemorySize & \",\"\"FreePhysicalMemory\"\":\" & objOS.FreePhysicalMemory & \"}\")
+ Next");
+ }
+
+ /**
+ * 判断指定路径下指定文件是否存在,如不存在则创建
+ *
+ * @param string $fileName 文件名
+ * @param string $content 文件内容
+ *
+ * @return string 返回文件路径
+ */
+ private function getFilePath($fileName, $content)
+ {
+ $path = dirname(__FILE__) . "\\$fileName";
+ if ( !file_exists($path) ) {
+ file_put_contents($path, $content);
+ }
+ return $path;
+ }
+
+ /**
+ * 获得CPU使用率
+ *
+ * @return Number
+ */
+ private function getCpuUsage()
+ {
+ $path = $this->getCupUsageVbsPathByWindows();
+ exec("cscript -nologo $path", $usage);
+ return $usage[0];
+ }
+
+ /**
+ * 获得cpu使用率vbs文件生成函数
+ *
+ * @return string 返回vbs文件路径
+ */
+ private function getCupUsageVbsPathByWindows()
+ {
+ return $this->getFilePath('cpu_usage.vbs', "On Error Resume Next
+ Set objProc = GetObject(\"winmgmts:\\\\.\\root\cimv2:win32_processor='cpu0'\")
+ WScript.Echo(objProc.LoadPercentage)");
+ }
+}
diff --git a/app/Library/cpu_usage.vbs b/app/Library/cpu_usage.vbs
new file mode 100644
index 0000000..60a7bdd
--- /dev/null
+++ b/app/Library/cpu_usage.vbs
@@ -0,0 +1,3 @@
+On Error Resume Next
+ Set objProc = GetObject("winmgmts:\\.\root\cimv2:win32_processor='cpu0'")
+ WScript.Echo(objProc.LoadPercentage)
\ No newline at end of file
diff --git a/app/Library/memory_usage.vbs b/app/Library/memory_usage.vbs
new file mode 100644
index 0000000..4c4218f
--- /dev/null
+++ b/app/Library/memory_usage.vbs
@@ -0,0 +1,6 @@
+On Error Resume Next
+ Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
+ Set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
+ For Each objOS in colOS
+ Wscript.Echo("{""TotalVisibleMemorySize"":" & objOS.TotalVisibleMemorySize & ",""FreePhysicalMemory"":" & objOS.FreePhysicalMemory & "}")
+ Next
\ No newline at end of file
diff --git a/app/Listeners/QueryListener.php b/app/Listeners/QueryListener.php
new file mode 100644
index 0000000..63368e5
--- /dev/null
+++ b/app/Listeners/QueryListener.php
@@ -0,0 +1,52 @@
+log = new Logger('mysql');
+
+ // 创建mysql文件夹
+ $dir_path = dirname(dirname(__DIR__)) . '/storage/logs/' . $this->log->getName();
+ if (!is_dir($dir_path)) mkdir($dir_path, 0755);
+
+ $log_path = storage_path('logs/' . $this->log->getName() . '/' . date('Y-m-d') . '.log');
+ if (!file_exists($log_path)) {
+ fopen($log_path, "w");
+ }
+
+ $this->log->pushHandler(new StreamHandler($log_path, Logger::DEBUG));
+ }
+
+ /**
+ * Handle the event.
+ *
+ * @param object $event
+ *
+ * @return void
+ */
+ public function handle($event): void
+ {
+ if (env('APP_DEBUG') == true) {
+ /**
+ * sql语句的监听
+ */
+ $sql = str_replace("?", "'%s'", $event->sql);
+ $log = vsprintf($sql, $event->bindings);
+
+ $this->log->addRecord(Logger::DEBUG, '[' . $event->time . '] | ' . $log . ' |');
+ }
+ }
+}
diff --git a/app/Models/Model.php b/app/Models/Model.php
new file mode 100644
index 0000000..50b4779
--- /dev/null
+++ b/app/Models/Model.php
@@ -0,0 +1,92 @@
+attributes[self::CREATED_AT];
+ }
+
+ public function getUpdatedTimeAttribute()
+ {
+ return $this->attributes[self::UPDATED_AT];
+ }
+
+ /**
+ * 自定义的软删除
+ */
+ protected $is_delete = 1; //是否开启删除(1.开启删除,就是直接删除;0.假删除)
+ protected $delete_field = 'is_delete'; //删除字段
+
+ public function getIsDelete()
+ {
+ return $this->is_delete;
+ }
+
+ public function getDeleteField()
+ {
+ return $this->delete_field;
+ }
+
+ /**
+ * 不可批量赋值的属性
+ *
+ * @var array
+ */
+ protected $guarded = [];
+
+ /**
+ * 模型的 "booted" 方法
+ *
+ * 应用全局作用域
+ *
+ * @return void
+ */
+ protected static function booted()
+ {
+ // 假删除的作用域
+ static::addGlobalScope(new DeleteScope(new static));
+ }
+
+ public static function firstByWhere($where)
+ {
+ return self::where($where)->first();
+ }
+}
diff --git a/app/Models/MonthModel.php b/app/Models/MonthModel.php
new file mode 100644
index 0000000..e79223f
--- /dev/null
+++ b/app/Models/MonthModel.php
@@ -0,0 +1,100 @@
+setMonthTable();
+ }
+
+ public function getMonth(): string
+ {
+ return $this->month;
+ }
+
+
+ /**
+ * 设置按月分表
+ *
+ * @param string $month
+ *
+ * @return $this
+ */
+ public function setMonthTable(string $month = '')
+ {
+ $month = empty($month) ? date(self::MONTH_FORMAT) : date(self::MONTH_FORMAT, strtotime($month));
+ // 替换为日期格式,否则将无法转化为时间戳(有效的日期格式 - 拼接才可以)
+ $month = str_replace('_', '-', $month);
+
+ // 当表名大于最小表名时,设置表名。
+ if ( $month >= str_replace('_', '-', self::MIN_TABLE) ) {
+ $this->month = date(self::MONTH_FORMAT, strtotime($month));
+ $this->table = $this->getOldTableName() . '_' . $this->month;
+ }
+
+ return $this;
+ }
+
+ /**
+ * 获取从开始分表到今天为止,所有的月份
+ *
+ * @return array
+ */
+ public function getAllMonthes() : array
+ {
+ $monthes = get_month_range(str_replace('_', '-', self::MIN_TABLE), date('Y-m'));
+ krsort($monthes);
+ return $monthes;
+ }
+
+ /**
+ * 获取原始表名(移除月份表的后缀)【包含前缀】
+ *
+ * @return string
+ */
+ public function getOldTableName():string
+ {
+ $table_name = $this->getTable();
+ $suffix_len = strlen(self::MIN_TABLE);
+ $suffix = substr($table_name, -$suffix_len, $suffix_len);
+ // 检测当前表名是否为按月分表的表名
+ if (preg_match ("/^([0-9]{4})_([0-9]{2})$/", $suffix, $parts)){
+ // 减1是因为默认还有一个下划线
+ $table_name = substr($table_name, 0, -$suffix_len - 1);
+ }
+ return $table_name;
+ }
+
+ /**
+ * 生成新表
+ *
+ * @param string $new_table
+ * @param string $time
+ * @param string $format
+ * @param string $old_table
+ *
+ * @return bool
+ */
+ public function createMonthTable(string $new_table = '', $time = '', $format = self::MONTH_FORMAT, string $old_table = '')
+ {
+ $new_table = empty($new_table) ? $this->getOldTableName() . '_' . date($format, empty($time) ? time() : $time) : $new_table;
+
+ // 数据表前缀
+ $db_prefix = env('DB_PREFIX');
+
+ return $this->setCopyTable($db_prefix . $new_table, $db_prefix . $this->getOldTableName());
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
new file mode 100644
index 0000000..804799b
--- /dev/null
+++ b/app/Models/User.php
@@ -0,0 +1,43 @@
+ 'datetime',
+ ];
+}
diff --git a/app/Models/YearModel.php b/app/Models/YearModel.php
new file mode 100644
index 0000000..316ef1e
--- /dev/null
+++ b/app/Models/YearModel.php
@@ -0,0 +1,89 @@
+setMonthTable();
+ }
+
+ /**
+ * 获取从开始分表到明年为止,所有的年份
+ *
+ * @return array
+ */
+ public function getAllMonthes(): array
+ {
+ $years = get_year_range(self::MIN_TABLE . '-01', date('Y-01', strtotime('+1 year')));
+ krsort($years);
+ return $years;
+ }
+
+ /**
+ * 设置按年分表
+ *
+ * @param string $month
+ *
+ * @return $this
+ */
+ public function setMonthTable(string $month = '')
+ {
+ $month = empty($month) ? date(self::MONTH_FORMAT) : (strlen($month) == 4 ? $month : date(self::MONTH_FORMAT, strtotime($month)));
+ // 替换为日期格式,否则将无法转化为时间戳(有效的日期格式 - 拼接才可以)
+ $month = str_replace('_', '-', $month);
+
+ // 当表名大于最小表名时,设置表名。
+ if ( $month >= self::MIN_TABLE ) {
+ $this->month = $month;
+ $this->table = $this->getOldTableName() . '_' . $this->month;
+ }
+
+ return $this;
+ }
+
+ /**
+ * 生成新表
+ *
+ * @param string $new_table
+ * @param string $time
+ * @param string $format
+ * @param string $old_table
+ *
+ * @return bool
+ */
+ public function createMonthTable(string $new_table = '', $time = '', $format = self::MONTH_FORMAT, string $old_table = '')
+ {
+ $new_table = empty($new_table) ? $this->getOldTableName() . '_' . date($format, empty($time) ? time() : $time) : $new_table;
+
+ // 数据表前缀
+ $db_prefix = env('DB_PREFIX');
+
+ return $this->setCopyTable($db_prefix . $new_table, $db_prefix . $this->getOldTableName());
+ }
+
+ /**
+ * 获取原始表名(移除年份表的后缀)【包含前缀】
+ *
+ * @return string
+ */
+ public function getOldTableName():string
+ {
+ $table_name = $this->getTable();
+ $suffix_len = strlen(self::MIN_TABLE);
+ $suffix = substr($table_name, -$suffix_len, $suffix_len);
+ // 检测当前表名是否为按年分表的表名
+ if (preg_match ("/^([0-9]{4})$/", $suffix, $parts)){
+ // 减1是因为默认还有一个下划线
+ $table_name = substr($table_name, 0, -$suffix_len - 1);
+ }
+ return $table_name;
+ }
+}
diff --git a/app/Modules/Admin/Config/.gitkeep b/app/Modules/Admin/Config/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Config/config.php b/app/Modules/Admin/Config/config.php
new file mode 100644
index 0000000..0fa4b42
--- /dev/null
+++ b/app/Modules/Admin/Config/config.php
@@ -0,0 +1,5 @@
+ 'Admin'
+];
diff --git a/app/Modules/Admin/Console/.gitkeep b/app/Modules/Admin/Console/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Console/AutoTableBuild.php b/app/Modules/Admin/Console/AutoTableBuild.php
new file mode 100644
index 0000000..88c2774
--- /dev/null
+++ b/app/Modules/Admin/Console/AutoTableBuild.php
@@ -0,0 +1,77 @@
+model_lists as $model){
+ (new $model)->createMonthTable('', strtotime('+1 month'));
+ }
+ }
+
+ /**
+ * Get the console command arguments.
+ *
+ * @return array
+ */
+ protected function getArguments()
+ {
+ return [
+ ['example', InputArgument::REQUIRED, 'An example argument.'],
+ ];
+ }
+
+ /**
+ * Get the console command options.
+ *
+ * @return array
+ */
+ protected function getOptions()
+ {
+ return [
+ ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Database/Migrations/.gitkeep b/app/Modules/Admin/Database/Migrations/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Database/Seeders/.gitkeep b/app/Modules/Admin/Database/Seeders/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Database/Seeders/AdminDatabaseSeeder.php b/app/Modules/Admin/Database/Seeders/AdminDatabaseSeeder.php
new file mode 100644
index 0000000..9a8738e
--- /dev/null
+++ b/app/Modules/Admin/Database/Seeders/AdminDatabaseSeeder.php
@@ -0,0 +1,21 @@
+call("OthersTableSeeder");
+ }
+}
diff --git a/app/Modules/Admin/Database/factories/.gitkeep b/app/Modules/Admin/Database/factories/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Entities/.gitkeep b/app/Modules/Admin/Entities/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Entities/Article/Article.php b/app/Modules/Admin/Entities/Article/Article.php
new file mode 100644
index 0000000..cda0eee
--- /dev/null
+++ b/app/Modules/Admin/Entities/Article/Article.php
@@ -0,0 +1,35 @@
+attributes['article_cover'] = str_replace(Storage::url('/'), '', $key);
+ }
+ }
+
+ public function category()
+ {
+ return $this->hasOne(ArticleCategory::class, 'category_id', 'category_id');
+ }
+
+ public function labels()
+ {
+ return $this->belongsToMany(ArticleLabel::class, ArticleWithLabel::class, 'article_id' , 'label_id' , 'article_id', 'label_id')->withPivot(['article_id', 'label_id']);
+ }
+}
diff --git a/app/Modules/Admin/Entities/Article/ArticleCategory.php b/app/Modules/Admin/Entities/Article/ArticleCategory.php
new file mode 100644
index 0000000..82ef61f
--- /dev/null
+++ b/app/Modules/Admin/Entities/Article/ArticleCategory.php
@@ -0,0 +1,87 @@
+getCacheTree();
+ }
+
+ /**
+ * 删除缓存
+ *
+ * @return bool
+ */
+ public function delCache()
+ {
+ Cache::forget($this->cache_key);
+ return true;
+ }
+
+ /**
+ * 获取文章分类的数据缓存
+ *
+ * @param string $get_type
+ *
+ * @return mixed
+ */
+ private function getCache(string $get_type = '')
+ {
+ return Cache::remember($this->cache_key, 3600, function() use ($get_type) {
+ $all = $this->orderBy('category_sort', 'ASC')->get()->toArray();
+ $tree = list_to_tree($all, $this->getKeyName());
+ return compact('all', 'tree');
+ });
+ }
+
+ /**
+ * 获取Tree格式化数据
+ *
+ * @return array|mixed
+ */
+ public function getCacheTree()
+ {
+ return $this->getCache()['tree'] ?? [];
+ }
+
+ /**
+ * 获取所有文章数据
+ *
+ * @return array|mixed
+ */
+ public function getCacheAll()
+ {
+ return $this->getCache()['all'] ?? [];
+ }
+
+ /**
+ * 获取所有子集的Id集合(包含自己)
+ *
+ * @param int $parent_id
+ * @param array $all
+ *
+ * @return array|int[]
+ */
+ public function getChildIds(int $parent_id, array $all = [])
+ {
+ $ids = [$parent_id];
+ empty($all) && $all = $this->getCacheAll();
+ foreach ($all as $key => $value) {
+ if ( $value['parent_id'] == $parent_id ) {
+ unset($all[$key]);
+ $child_ids = $this->getChildIds((int)$value['category_id'], $all);
+ !empty($child_ids) && $ids = array_merge($ids, $child_ids);
+ }
+ }
+ return $ids;
+ }
+}
diff --git a/app/Modules/Admin/Entities/Article/ArticleLabel.php b/app/Modules/Admin/Entities/Article/ArticleLabel.php
new file mode 100644
index 0000000..8910298
--- /dev/null
+++ b/app/Modules/Admin/Entities/Article/ArticleLabel.php
@@ -0,0 +1,11 @@
+belongsTo(Admin::class, 'admin_id', 'admin_id');
+ }
+
+ public function getLogDurationAttribute($key)
+ {
+ return floatval($key);
+ }
+}
diff --git a/app/Modules/Admin/Entities/Log/AdminLoginLog.php b/app/Modules/Admin/Entities/Log/AdminLoginLog.php
new file mode 100644
index 0000000..063c8d0
--- /dev/null
+++ b/app/Modules/Admin/Entities/Log/AdminLoginLog.php
@@ -0,0 +1,38 @@
+belongsTo(Admin::class, 'admin_id', 'admin_id');
+ }
+
+ public function getLogDurationAttribute($key)
+ {
+ return floatval($key);
+ }
+
+ public function add(int $admin_id = 0, int $log_status = 1, $description = '登录成功')
+ {
+ $ip_agent = get_client_info();
+ return $this->setMonthTable()->create([
+ 'admin_id' => $admin_id,
+ 'created_ip' => $ip_agent['ip'] ?? get_ip(),
+ 'browser_type' => $ip_agent['agent'] ?? $_SERVER['HTTP_USER_AGENT'],
+ 'log_status' => $log_status,
+ 'description' => $description,
+ 'log_action' => request()->route()->getActionName(),
+ 'log_method' => request()->getMethod(),
+ 'log_duration' => microtime(true) - LARAVEL_START,
+ 'request_data' => json_encode(request()->all()),
+ ]);
+ }
+}
diff --git a/app/Modules/Admin/Entities/Rabc/Admin.php b/app/Modules/Admin/Entities/Rabc/Admin.php
new file mode 100644
index 0000000..891b305
--- /dev/null
+++ b/app/Modules/Admin/Entities/Rabc/Admin.php
@@ -0,0 +1,145 @@
+is_delete;
+ }
+
+ public function getDeleteField()
+ {
+ return $this->delete_field;
+ }
+
+ /**
+ * 是否主动维护时间戳
+ *
+ * @var bool
+ */
+ public $timestamps = false;
+
+ protected $hidden = ['password'];
+
+ /**
+ * 不可批量赋值的属性
+ *
+ * @var array
+ */
+ protected $guarded = [];
+
+ /**
+ * 模型的 "booted" 方法
+ *
+ * 应用全局作用域
+ *
+ * @return void
+ */
+ protected static function booted()
+ {
+ // 假删除的作用域
+ $static = new static;
+ static::addGlobalScope('delete', function(Builder $builder) use ($static){
+ if ($static->is_delete == 0) $builder->where($static->delete_field, $static->is_delete);
+ });
+ }
+
+ public function getAdminByName(string $admin_name)
+ {
+ return $this->where('admin_name', $admin_name)->first();
+ }
+
+ public function setPasswordAttribute($key)
+ {
+ if (empty($key)) unset($this->attributes['password']);
+ else $this->attributes['password'] = hash_encryption($key);
+ }
+
+ /**
+ * 获取会储存到 jwt 声明中的标识
+ * @return mixed
+ */
+ public function getJWTIdentifier()
+ {
+ return $this->getKey();
+ }
+
+ /**
+ * 返回包含要添加到 jwt 声明中的自定义键值对数组
+ * @return array
+ */
+ public function getJWTCustomClaims()
+ {
+ return ['role' => 'admin'];
+ }
+
+ public function adminInfo()
+ {
+ return $this->hasOne(AdminInfo::class, $this->primaryKey, $this->primaryKey);
+ }
+
+ public function roles()
+ {
+ return $this->belongsToMany(AdminRole::class, AdminWithRole::class, 'admin_id', 'role_id')->withPivot(['admin_id', 'role_id']);
+ }
+
+ /**
+ * @Function assignRole
+ *
+ * @param $roles
+ *
+ * @return bool
+ * @author : cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:给用户分配角色
+ * @englishAnnotation:
+ */
+ public function assignRole($roles)
+ {
+ return $this->roles()->save($roles);
+ }
+
+ /**
+ * @Function deleteRole
+ *
+ * @param $roles
+ *
+ * @return mixed
+ * @author : cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:取消用户分配的角色,取消而不是删除
+ * @englishAnnotation:
+ */
+ public function deleteRole($roles)
+ {
+ return $this->roles()->detach($roles);
+ }
+
+ public function getAdminHeadAttribute($key)
+ {
+ if (empty($key)) return $key;
+ return Storage::url($key);
+ }
+
+ public function setAdminHeadAttribute($key)
+ {
+ if (!empty($key)) {
+ $this->attributes['admin_head'] = str_replace(Storage::url('/'), '', $key);
+ }
+ }
+}
diff --git a/app/Modules/Admin/Entities/Rabc/AdminInfo.php b/app/Modules/Admin/Entities/Rabc/AdminInfo.php
new file mode 100644
index 0000000..9c0ed7d
--- /dev/null
+++ b/app/Modules/Admin/Entities/Rabc/AdminInfo.php
@@ -0,0 +1,10 @@
+orderBy('menu_sort', 'ASC')->get();
+ }
+
+ public function getSelectLists()
+ {
+ return list_to_tree($this->orderBy('menu_sort', 'ASC')->get()->toArray());
+ }
+
+ public function getMenusByIds(array $menu_ids)
+ {
+ return $this->whereIn('menu_id', $menu_ids)
+ ->orderBy('menu_sort', 'ASC')
+ ->get();
+ }
+
+ public function getMenusByIdsForRabc(array $menu_ids)
+ {
+ return $this->whereIn('menu_id', $menu_ids)->where('api_url', '<>', '')->pluck('api_method', 'api_url');
+ }
+}
diff --git a/app/Modules/Admin/Entities/Rabc/AdminRole.php b/app/Modules/Admin/Entities/Rabc/AdminRole.php
new file mode 100644
index 0000000..496acaa
--- /dev/null
+++ b/app/Modules/Admin/Entities/Rabc/AdminRole.php
@@ -0,0 +1,48 @@
+belongsToMany(AdminMenu::class, 'admin_role_with_menus', 'role_id', 'menu_id')
+ ->where(['is_delete' => 0, 'is_check' => 1])
+ ->orderBy('menu_sort', 'ASC');
+ }
+
+ /**
+ * @Function grantMenus
+ *
+ * @param $menus
+ *
+ * @return bool
+ * @author : cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:给角色赋予权限
+ * @englishAnnotation:
+ */
+ public function grantMenus($menus)
+ {
+ return AdminRoleWithMenu::create($menus);
+ }
+
+ /**
+ * @Function deleteMenus
+ *
+ * @param $menus
+ *
+ * @return mixed
+ * @author : cnpscy <[2278757482@qq.com]>
+ * @chineseAnnotation:取消角色赋予的权限
+ * @englishAnnotation:
+ */
+ public function deleteMenus($menus)
+ {
+ return AdminRoleWithMenu::where($menus)->delete();
+ }
+}
diff --git a/app/Modules/Admin/Entities/Rabc/AdminRoleWithMenu.php b/app/Modules/Admin/Entities/Rabc/AdminRoleWithMenu.php
new file mode 100644
index 0000000..28aeb42
--- /dev/null
+++ b/app/Modules/Admin/Entities/Rabc/AdminRoleWithMenu.php
@@ -0,0 +1,16 @@
+whereIn('role_id', $role_ids)->select('menu_id')->get()->toArray(), 'menu_id'));
+ }
+}
diff --git a/app/Modules/Admin/Entities/Rabc/AdminWithRole.php b/app/Modules/Admin/Entities/Rabc/AdminWithRole.php
new file mode 100644
index 0000000..89fed33
--- /dev/null
+++ b/app/Modules/Admin/Entities/Rabc/AdminWithRole.php
@@ -0,0 +1,10 @@
+attributes['banner_cover'] = str_replace(Storage::url('/'), '', $key);
+ }
+ }
+}
diff --git a/app/Modules/Admin/Entities/System/Config.php b/app/Modules/Admin/Entities/System/Config.php
new file mode 100644
index 0000000..91b85bf
--- /dev/null
+++ b/app/Modules/Admin/Entities/System/Config.php
@@ -0,0 +1,92 @@
+where('is_check', 1)
+ ->select('config_value', 'config_name', 'config_type')
+ ->get()
+ ->toArray();//字段进行过滤
+ $_data = $data_list = [];
+ array_walk($config_data, function ($value) use (&$data_list) {
+ /**
+ * 对于数组格式的处理
+ *
+ * in_array(strtoupper($value['config_name']), ['CONFIG_GROUP_LIST', 'CONFIG_TYPE_LIST', 'MENU_TYPE_LIST']) ||
+ */
+ if ($value['config_type'] == 3) {
+ $value_ary = array_filter(explode('|', str_replace(["\r", "\r\n", "\n"], '|', $value['config_value'])));
+ foreach ($value_ary as $k => $v) {
+ if (empty($value['config_name'])) continue;
+ $array = explode(':', str_replace(["'", '"', "\r", "\r\n", "\n"], '', $v));
+ $_data[$array[0]] = $array[1];
+ }
+ $data_list[$value['config_name']] = $_data;
+ }else{
+ /**
+ * 配置项的值,对于不同字符类型的格式进行处理
+ */
+ switch ($value['config_type']){
+ case 2: // 数字
+ $config_value = floatval($value['config_value']);
+ break;
+ default:
+ $config_value = $value['config_value'];
+ break;
+ }
+ /**
+ * 如果存在某一类的设置项:
+ * 如:user.login_days、user.pass 这一类,自动设置为数据格式数据,便于使用config的 . 找到数组下坐标
+ */
+ if (strstr($value['config_name'], '.')){
+ $ary = explode('.', $value['config_name']);
+ if (count($ary) > 2){
+ list($key, $val, $third) = $ary;
+ $data_list[$key][$val][$third] = $config_value;
+ }else{
+ list($key, $val) = $ary;
+ $data_list[$key][$val] = $config_value;
+ }
+ }else{
+ $data_list[$value['config_name']] = $config_value;
+ }
+ }
+ });
+ // 文件写入
+ $res = file_put_contents( config_path() . '/cnpscy.php', 'select()->toArray();
+ if (!empty($configs)) {
+ foreach ($configs as &$v) {
+ if (in_array($v['config_type'], [4])){
+ if (!empty($v['config_extra'])) $v['config_extra'] = config_array_analysis($v['config_extra']);
+ }
+ }
+ }
+ $configs = array_field_group($configs, 'config_group');//按照配置进行分组
+ if (empty($configs[0])) $configs[0] = [];
+ return $configs;
+ }
+}
diff --git a/app/Modules/Admin/Entities/System/Friendlink.php b/app/Modules/Admin/Entities/System/Friendlink.php
new file mode 100644
index 0000000..9e03f1a
--- /dev/null
+++ b/app/Modules/Admin/Entities/System/Friendlink.php
@@ -0,0 +1,25 @@
+attributes['link_cover'] = str_replace(Storage::url('/'), '', $key);
+ }
+ }
+}
diff --git a/app/Modules/Admin/Entities/System/Protocol.php b/app/Modules/Admin/Entities/System/Protocol.php
new file mode 100644
index 0000000..d4fd0e4
--- /dev/null
+++ b/app/Modules/Admin/Entities/System/Protocol.php
@@ -0,0 +1,11 @@
+service = $articleCategoryService;
+ }
+
+ public function create(ArticleCategoryRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(ArticleCategoryRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Article/ArticleController.php b/app/Modules/Admin/Http/Controllers/Article/ArticleController.php
new file mode 100644
index 0000000..cb9385c
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Article/ArticleController.php
@@ -0,0 +1,25 @@
+service = $articleService;
+ }
+
+ public function create(ArticleRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(ArticleRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Article/ArticleLabelController.php b/app/Modules/Admin/Http/Controllers/Article/ArticleLabelController.php
new file mode 100644
index 0000000..7aaf19d
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Article/ArticleLabelController.php
@@ -0,0 +1,25 @@
+service = $articleLabelService;
+ }
+
+ public function create(ArticleLabelRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(ArticleLabelRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/AuthController.php b/app/Modules/Admin/Http/Controllers/AuthController.php
new file mode 100644
index 0000000..e811b8c
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/AuthController.php
@@ -0,0 +1,64 @@
+service = $authService;
+ }
+
+ /**
+ * 登录流程
+ *
+ * @param LoginRequest $request
+ * @return \Illuminate\Http\JsonResponse
+ * @throws \App\Exceptions\Admin\AuthException
+ * @throws \App\Exceptions\InvalidRequestException
+ */
+ public function login(LoginRequest $request)
+ {
+ $data = $request->validated();
+
+ // 登录流程
+ $token = $this->service->login($data);
+
+ return $this->successJson($token);
+ }
+
+ /**
+ * 获取登录管理员信息
+ *
+ * @return \Illuminate\Http\JsonResponse
+ * @throws \App\Exceptions\Admin\AuthException
+ */
+ public function me()
+ {
+ if (\request()->getMethod() == 'OPTIONS'){
+ return $this->successJson();
+ }
+
+ return $this->successJson($this->service->me());
+ }
+
+ /**
+ * 退出登录
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function logout()
+ {
+ return $this->successJson($this->service->logout());
+ }
+
+ public function getRabcList()
+ {
+ // 临时测试数据
+ return $this->successJson(list_to_tree(AdminMenu::getInstance()->getAllMenus()->toArray()));
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/BaseController.php b/app/Modules/Admin/Http/Controllers/BaseController.php
new file mode 100644
index 0000000..6006d7c
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/BaseController.php
@@ -0,0 +1,147 @@
+service)){
+ return $this->successJson([], '请先设置Service或者重写方法!');
+ }
+ return $this->successJson($this->service->lists($request->all()));
+ }
+
+ /**
+ * 详情
+ *
+ * @param int $id
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function detail(Request $request)
+ {
+ if (!isset($this->service)){
+ return $this->successJson([], '请先设置Service或者重写方法!');
+ }
+ if ($detail = $this->service->detail($request)){
+ return $this->successJson($detail);
+ }else{
+ return $this->errorJson('数据不存在!');
+ }
+ }
+
+ /**
+ * 新增流程
+ *
+ * @param $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function createService($request)
+ {
+ if ($request instanceof FormRequest){
+ $request->validated();
+ }
+
+ if (!isset($this->service)){
+ return $this->successJson([], '请先设置Service或者重写方法!');
+ }
+ if ($this->service->create($request->all())){
+ return $this->successJson([], $this->service->getError());
+ }else{
+ return $this->errorJson($this->service->getError());
+ }
+ }
+
+ /**
+ * 更新流程
+ *
+ * @param $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function updateService($request)
+ {
+ if ($request instanceof FormRequest){
+ $request->validated();
+ }
+
+ if (!isset($this->service)){
+ return $this->successJson([], '请先设置Service或者重写方法!');
+ }
+ if ($this->service->update($request->all())){
+ return $this->successJson([], $this->service->getError());
+ }else{
+ return $this->errorJson($this->service->getError());
+ }
+ }
+
+ /**
+ * 删除
+ *
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function delete(Request $request)
+ {
+ if (!isset($this->service)){
+ return $this->successJson([], '请先设置Service或者重写方法!');
+ }
+ if ($this->service->delete($request->all())){
+ return $this->successJson([], $this->service->getError());
+ }else{
+ return $this->errorJson($this->service->getError());
+ }
+ }
+
+ /**
+ * 指定字段变更操作
+ *
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function changeFiledStatus(Request $request)
+ {
+ if (!isset($this->service)){
+ return $this->successJson([], '请先设置Service或者重写方法!');
+ }
+
+ if ($this->service->changeFiledStatus($request->all())){
+ return $this->successJson([], $this->service->getError());
+ }else{
+ return $this->errorJson($this->service->getError());
+ }
+ }
+
+ /**
+ * 下拉筛选列表(可搜索)
+ *
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getSelectLists(Request $request)
+ {
+ $lists = $this->service->getSelectLists($request);
+ return $this->successJson($lists);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/IndexController.php b/app/Modules/Admin/Http/Controllers/IndexController.php
new file mode 100644
index 0000000..a46eab3
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/IndexController.php
@@ -0,0 +1,78 @@
+service = $indexService;
+ }
+
+ public function index(Request $request)
+ {
+ return $this->successJson($this->service->index());
+ }
+
+ /**
+ * 按照日志类型的统计图数据
+ *
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function logsStatistics(Request $request)
+ {
+ return $this->successJson($this->service->logsStatistics());
+ }
+
+ /**
+ * 月份表列表
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getMonthList()
+ {
+ return $this->successJson(MonthModel::getInstance()->getAllMonthes());
+ }
+
+ /**
+ * 编辑登录管理员信息
+ *
+ * @param \Illuminate\Http\Request $request
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function update(Request $request)
+ {
+ if ( $this->service->updateAdmin($request) ) {
+ return $this->successJson([], $this->service->getError());
+ } else {
+ return $this->errorJson($this->service->getError());
+ }
+ }
+
+ /**
+ * 版本历史记录
+ *
+ * @return mixed
+ */
+ public function versionLogs()
+ {
+ return $this->successJson($this->service->versionLogs());
+ }
+
+ /**
+ * 获取服务器状态
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function getServerStatus()
+ {
+ return $this->successJson($this->service->getServerStatus());
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Log/AdminLogController.php b/app/Modules/Admin/Http/Controllers/Log/AdminLogController.php
new file mode 100644
index 0000000..7ca869b
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Log/AdminLogController.php
@@ -0,0 +1,14 @@
+service = $adminLogService;
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Log/AdminLoginLogController.php b/app/Modules/Admin/Http/Controllers/Log/AdminLoginLogController.php
new file mode 100644
index 0000000..47811b5
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Log/AdminLoginLogController.php
@@ -0,0 +1,14 @@
+service = $adminLoginLogService;
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Rabc/AdminController.php b/app/Modules/Admin/Http/Controllers/Rabc/AdminController.php
new file mode 100644
index 0000000..a4a03fd
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Rabc/AdminController.php
@@ -0,0 +1,25 @@
+service = $adminService;
+ }
+
+ public function create(AdminRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(AdminRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Rabc/AdminMenuController.php b/app/Modules/Admin/Http/Controllers/Rabc/AdminMenuController.php
new file mode 100644
index 0000000..40d95e9
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Rabc/AdminMenuController.php
@@ -0,0 +1,25 @@
+service = $adminMenuService;
+ }
+
+ public function create(AdminMenuRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(AdminMenuRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/Rabc/AdminRoleController.php b/app/Modules/Admin/Http/Controllers/Rabc/AdminRoleController.php
new file mode 100644
index 0000000..5ff9f1e
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/Rabc/AdminRoleController.php
@@ -0,0 +1,25 @@
+service = $adminRoleService;
+ }
+
+ public function create(AdminRoleRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(AdminRoleRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/System/BannerController.php b/app/Modules/Admin/Http/Controllers/System/BannerController.php
new file mode 100644
index 0000000..11598c2
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/System/BannerController.php
@@ -0,0 +1,25 @@
+service = $bannerService;
+ }
+
+ public function create(BannerRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(BannerRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/System/ConfigController.php b/app/Modules/Admin/Http/Controllers/System/ConfigController.php
new file mode 100644
index 0000000..3ab6f30
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/System/ConfigController.php
@@ -0,0 +1,59 @@
+service = $configService;
+ }
+
+ public function create(ConfigRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(ConfigRequest $request)
+ {
+ return $this->updateService($request);
+ }
+
+ public function getConfigGroupType()
+ {
+ $config_type_list = $config_group_list = [];
+ $config_group = cnpscy_config('config_group_list');
+ foreach ($config_group as $key => $value){
+ $config_group_list[] = [
+ 'value' => $key,
+ 'name' => $value,
+ ];
+ }
+ $config_type = cnpscy_config('config_type_list');
+ foreach ($config_type as $key => $value){
+ $config_type_list[] = [
+ 'value' => $key,
+ 'name' => $value,
+ ];
+ }
+ return $this->successJson([
+ 'config_group_list' => $config_group_list,
+ 'config_type_list' => $config_type_list,
+ ]);
+ }
+
+ /**
+ * 同步配置
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function pushRefreshConfig()
+ {
+ $this->service->pushRefreshConfig();
+ return $this->successJson([], '配置文件已同步成功!');
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/System/FriendlinkController.php b/app/Modules/Admin/Http/Controllers/System/FriendlinkController.php
new file mode 100644
index 0000000..3cecbac
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/System/FriendlinkController.php
@@ -0,0 +1,25 @@
+service = $friendlinkService;
+ }
+
+ public function create(FriendlinkRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(FriendlinkRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/System/ProtocolController.php b/app/Modules/Admin/Http/Controllers/System/ProtocolController.php
new file mode 100644
index 0000000..86ab726
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/System/ProtocolController.php
@@ -0,0 +1,25 @@
+service = $protocolService;
+ }
+
+ public function create(ProtocolRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(ProtocolRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/System/VersionController.php b/app/Modules/Admin/Http/Controllers/System/VersionController.php
new file mode 100644
index 0000000..768cfdb
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/System/VersionController.php
@@ -0,0 +1,25 @@
+service = $versionService;
+ }
+
+ public function create(VersionRequest $request)
+ {
+ return $this->createService($request);
+ }
+
+ public function update(VersionRequest $request)
+ {
+ return $this->updateService($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Controllers/UploadController.php b/app/Modules/Admin/Http/Controllers/UploadController.php
new file mode 100644
index 0000000..5277dc1
--- /dev/null
+++ b/app/Modules/Admin/Http/Controllers/UploadController.php
@@ -0,0 +1,31 @@
+file($file))){
+ return $this->errorJson('请上传文件!');
+ }
+
+ $path = $request->file($file)->storePublicly(
+ date('Ym'),
+ config('filesystems')
+ );
+
+ return $this->successJson($path, '上传成功', ['path_url' => Storage::url($path)]);
+ }
+}
diff --git a/app/Modules/Admin/Http/Middleware/.gitkeep b/app/Modules/Admin/Http/Middleware/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Http/Middleware/AdminLog.php b/app/Modules/Admin/Http/Middleware/AdminLog.php
new file mode 100644
index 0000000..c07e6a3
--- /dev/null
+++ b/app/Modules/Admin/Http/Middleware/AdminLog.php
@@ -0,0 +1,43 @@
+getMethod());
+
+ if ($method != 'GET'){
+ $ip_agent = get_client_info();
+ \App\Modules\Admin\Entities\Log\AdminLog::getInstance()->create([
+ 'request_data' => json_encode($request->all()),
+ 'admin_id' => !empty(auth($guard)->user()) ? auth($guard)->user()->admin_id : 0,
+ 'created_ip' => $ip_agent['ip'] ?? get_ip(),
+ 'browser_type' => $ip_agent['agent'] ?? $_SERVER['HTTP_USER_AGENT'],
+ 'created_time' => time(),
+ 'log_action' => request()->route()->getActionName(),
+ 'log_method' => $method,
+ 'log_duration' => microtime(true) - LARAVEL_START,
+ 'request_url' => URL::full() ?? get_this_url(),
+ ]);
+ }
+
+ return $resource;
+ }
+}
diff --git a/app/Modules/Admin/Http/Middleware/CheckAuth.php b/app/Modules/Admin/Http/Middleware/CheckAuth.php
new file mode 100644
index 0000000..5e297bd
--- /dev/null
+++ b/app/Modules/Admin/Http/Middleware/CheckAuth.php
@@ -0,0 +1,44 @@
+guard = 'admin';
+// // Auth认证
+// $auth = Auth()->guard($this->guard);
+// try {
+// if ( !$auth->check() ) { //未登录踢回,给予错误返回提示
+// return $this->errorJson('认证失败,请重新登录!');
+// }
+// } catch (TokenExpiredException $e) {
+// return $this->errorJson($e->getMessage());
+// } catch (TokenInvalidException $e) {
+// return $this->errorJson($e->getMessage());
+// } catch (JWTException $e) {
+// return $this->errorJson($e->getMessage());
+// }
+
+ return $next($request);
+ }
+}
diff --git a/app/Modules/Admin/Http/Middleware/CheckRabc.php b/app/Modules/Admin/Http/Middleware/CheckRabc.php
new file mode 100644
index 0000000..9940d4e
--- /dev/null
+++ b/app/Modules/Admin/Http/Middleware/CheckRabc.php
@@ -0,0 +1,64 @@
+guard = 'admin';
+// // 开始验证路由权限
+// if (!$this->checkRabc($request, Auth()->guard($this->guard)->user()->getAuthIdentifier(), $load_error)){
+// return $this->errorJson('无权限' . (empty($load_error) ? '!' : ',' . $load_error), -2);
+// }
+
+ return $next($request);
+ }
+
+ private function checkRabc($request, int $admin_id, &$load_error = ''):bool
+ {
+ // 超级管理员账户无需验证
+ if ($admin_id == 1) return true;
+
+ $roles = Admin::getInstance()->detail($admin_id)->roles->toArray();
+ if (empty($roles)) return false;
+ $role_ids = array_column($roles, 'role_id');
+ if (empty($role_ids)) return false;
+ $menu_ids = AdminRoleWithMenu::getInstance()->getMenuIdsByRoles($role_ids);
+ if (empty($menu_ids)) return false;
+ $menus = AdminMenu::getInstance()->getMenusByIdsForRabc($menu_ids)->toArray();
+ if (empty($menus)) return false;
+
+ // 获取当前路由
+ $route_path = $request->route()->uri();
+ // 检测是否存在当前API
+ if (isset($menus[$route_path])){
+ // 验证请求方式
+ if ($menus[$route_path] == $request->getMethod()){
+ return true;
+ }
+ $load_error = '请求方式有误!';
+ }
+ return false;
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/.gitkeep b/app/Modules/Admin/Http/Requests/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Http/Requests/Article/ArticleCategoryRequest.php b/app/Modules/Admin/Http/Requests/Article/ArticleCategoryRequest.php
new file mode 100644
index 0000000..c66d421
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/Article/ArticleCategoryRequest.php
@@ -0,0 +1,42 @@
+instance = ArticleCategory::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'category_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',category_name' . $this->validate_id
+ ],
+ 'category_sort' => [
+ 'required',
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'category_name.required' => '请输入分类名称!',
+ 'category_name.max' => '分类名称字数不可超过 256!',
+ 'category_name.unique' => '分类名称已存在,请更换!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/Article/ArticleLabelRequest.php b/app/Modules/Admin/Http/Requests/Article/ArticleLabelRequest.php
new file mode 100644
index 0000000..cc7dd14
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/Article/ArticleLabelRequest.php
@@ -0,0 +1,39 @@
+instance = ArticleLabel::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'label_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',label_name' . $this->validate_id
+ ],
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'label_name.required' => '请输入标签名称!',
+ 'label_name.max' => '标签名称字数不可超过 256!',
+ 'label_name.unique' => '标签名称已存在,请更换!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/Article/ArticleRequest.php b/app/Modules/Admin/Http/Requests/Article/ArticleRequest.php
new file mode 100644
index 0000000..8336bf2
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/Article/ArticleRequest.php
@@ -0,0 +1,47 @@
+instance = Article::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'article_title' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',article_title' . $this->validate_id,
+ ],
+ 'category_id' => [
+ 'required',
+ ],
+ 'article_cover' => [
+ 'required',
+ ],
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'article_title.required' => '请输入文章标题!',
+ 'article_title.max' => '标题字数不可超过 256!',
+ 'article_title.unique' => '文章标题已存在,请更换!',
+ 'category_id.required' => '请选择文章分类!',
+ 'article_cover.required' => '请上传封面!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/BaseRequest.php b/app/Modules/Admin/Http/Requests/BaseRequest.php
new file mode 100644
index 0000000..00711c4
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/BaseRequest.php
@@ -0,0 +1,52 @@
+setInstance();
+ }
+
+ $this->setValidateId();
+ }
+
+ protected function setValidateId()
+ {
+ if ($this->instance){
+ $primarykey = $this->instance->getKeyName();
+ $this->validate_id = ',' . request()->input($primarykey, 0) . ',' . $primarykey . ($this->instance->getIsDelete() == 0 ? ',is_delete,0' : '');
+ }
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ //
+ ];
+ }
+
+ /**
+ * Determine if the user is authorized to make this request.
+ *
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/LoginRequest.php b/app/Modules/Admin/Http/Requests/LoginRequest.php
new file mode 100644
index 0000000..b2b022d
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/LoginRequest.php
@@ -0,0 +1,27 @@
+ 'required',
+ 'password' => 'required',
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'admin_name.required' => '管理员账户为必填项!',
+ 'password.required' => '管理员密码为必填项!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/Rabc/AdminMenuRequest.php b/app/Modules/Admin/Http/Requests/Rabc/AdminMenuRequest.php
new file mode 100644
index 0000000..be8ffb6
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/Rabc/AdminMenuRequest.php
@@ -0,0 +1,37 @@
+instance = AdminMenu::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'menu_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',menu_name' . $this->validate_id
+ ],
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'menu_name.required' => '请输入菜单名称!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/Rabc/AdminRequest.php b/app/Modules/Admin/Http/Requests/Rabc/AdminRequest.php
new file mode 100644
index 0000000..5b59750
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/Rabc/AdminRequest.php
@@ -0,0 +1,52 @@
+instance = Admin::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'admin_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',admin_name' . $this->validate_id
+ ],
+ 'admin_email' => [
+ 'required',
+ 'max:256',
+ 'email',
+ ],
+ 'password' => [
+// 'confirmed',
+ ],
+ 'password_confirmation' => [
+
+ ],
+ 'is_check' => [
+ 'required',
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'admin_name.required' => '请输入管理员账户!',
+ 'password.confirmed' => '密码确认不匹配!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/Rabc/AdminRoleRequest.php b/app/Modules/Admin/Http/Requests/Rabc/AdminRoleRequest.php
new file mode 100644
index 0000000..c52ba76
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/Rabc/AdminRoleRequest.php
@@ -0,0 +1,40 @@
+instance = AdminRole::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'role_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',role_name' . $this->validate_id
+ ],
+ 'is_check' => [
+ 'required',
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'role_name.required' => '请输入角色名称!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/System/BannerRequest.php b/app/Modules/Admin/Http/Requests/System/BannerRequest.php
new file mode 100644
index 0000000..7671bab
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/System/BannerRequest.php
@@ -0,0 +1,41 @@
+instance = Banner::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'banner_title' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',banner_title' . $this->validate_id
+ ],
+ 'banner_cover' => [
+ 'required',
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'banner_title.required' => '请输入Banner标题!',
+ 'banner_cover.required' => '请上传Banner封面!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/System/ConfigRequest.php b/app/Modules/Admin/Http/Requests/System/ConfigRequest.php
new file mode 100644
index 0000000..ba37ccb
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/System/ConfigRequest.php
@@ -0,0 +1,42 @@
+instance = Config::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'config_title' => [
+ 'required',
+ 'max:256',
+ ],
+ 'config_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',config_name' . $this->validate_id
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'config_title.required' => '请输入配置标题!',
+ 'config_name.required' => '请输入配置标识!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/System/FriendlinkRequest.php b/app/Modules/Admin/Http/Requests/System/FriendlinkRequest.php
new file mode 100644
index 0000000..bf1248a
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/System/FriendlinkRequest.php
@@ -0,0 +1,50 @@
+instance = Friendlink::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'link_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',link_name' . $this->validate_id
+ ],
+ 'link_url' => [
+ 'url',
+ ],
+ 'link_cover' => [
+ 'required',
+ ],
+ 'link_sort' => [
+ 'required',
+ ],
+ 'is_check' => [
+ 'required',
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'link_name.required' => '请输入友链名称!',
+ 'link_cover.required' => '请上传友链图标!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/System/ProtocolRequest.php b/app/Modules/Admin/Http/Requests/System/ProtocolRequest.php
new file mode 100644
index 0000000..d339d53
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/System/ProtocolRequest.php
@@ -0,0 +1,47 @@
+instance = Protocol::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'protocol_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',protocol_name' . $this->validate_id
+ ],
+ 'protocol_type' => [
+ 'required',
+ ],
+ 'protocol_content' => [
+ 'required',
+ ],
+ 'is_check' => [
+ 'required',
+ ]
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'protocol_name.required' => '请输入版本名称!',
+ 'protocol_type.required' => '请输入版本号!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Http/Requests/System/VersionRequest.php b/app/Modules/Admin/Http/Requests/System/VersionRequest.php
new file mode 100644
index 0000000..33e8415
--- /dev/null
+++ b/app/Modules/Admin/Http/Requests/System/VersionRequest.php
@@ -0,0 +1,55 @@
+instance = Version::getInstance();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'version_name' => [
+ 'required',
+ 'max:256',
+ 'unique:' . $this->instance->getTable() . ',version_name' . $this->validate_id,
+ ],
+ 'version_number' => [
+ 'required',
+ 'unique:' . $this->instance->getTable() . ',version_number' . $this->validate_id,
+ ],
+ 'publish_date' => [
+ 'date_format:Y-m-d H:i:s'
+ ],
+ 'version_content' => [
+ 'required',
+ ],
+ 'version_sort' => [
+ 'min:0',
+ ],
+ ];
+ }
+
+ public function messages()
+ {
+ return [
+ 'version_name.required' => '请输入版本名称!',
+ 'version_name.unique' => '版本名称已存在,请更换!',
+ 'version_number.required' => '请输入版本号!',
+ 'version_number.unique' => '版本号已存在,请更换!',
+ 'publish_date.date_format' => '请选择有效的发布时间!',
+ 'version_content.required' => '请输入版本内容!',
+ ];
+ }
+}
diff --git a/app/Modules/Admin/Providers/.gitkeep b/app/Modules/Admin/Providers/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Providers/AdminServiceProvider.php b/app/Modules/Admin/Providers/AdminServiceProvider.php
new file mode 100644
index 0000000..9c6c103
--- /dev/null
+++ b/app/Modules/Admin/Providers/AdminServiceProvider.php
@@ -0,0 +1,112 @@
+registerTranslations();
+ $this->registerConfig();
+ $this->registerViews();
+ $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
+ }
+
+ /**
+ * Register the service provider.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ $this->app->register(RouteServiceProvider::class);
+ }
+
+ /**
+ * Register config.
+ *
+ * @return void
+ */
+ protected function registerConfig()
+ {
+ $this->publishes([
+ module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'),
+ ], 'config');
+ $this->mergeConfigFrom(
+ module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower
+ );
+ }
+
+ /**
+ * Register views.
+ *
+ * @return void
+ */
+ public function registerViews()
+ {
+ $viewPath = resource_path('views/modules/' . $this->moduleNameLower);
+
+ $sourcePath = module_path($this->moduleName, 'Resources/views');
+
+ $this->publishes([
+ $sourcePath => $viewPath
+ ], ['views', $this->moduleNameLower . '-module-views']);
+
+ $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
+ }
+
+ /**
+ * Register translations.
+ *
+ * @return void
+ */
+ public function registerTranslations()
+ {
+ $langPath = resource_path('lang/modules/' . $this->moduleNameLower);
+
+ if (is_dir($langPath)) {
+ $this->loadTranslationsFrom($langPath, $this->moduleNameLower);
+ } else {
+ $this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower);
+ }
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return [];
+ }
+
+ private function getPublishableViewPaths(): array
+ {
+ $paths = [];
+ foreach (\Config::get('view.paths') as $path) {
+ if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
+ $paths[] = $path . '/modules/' . $this->moduleNameLower;
+ }
+ }
+ return $paths;
+ }
+}
diff --git a/app/Modules/Admin/Providers/RouteServiceProvider.php b/app/Modules/Admin/Providers/RouteServiceProvider.php
new file mode 100644
index 0000000..4d65486
--- /dev/null
+++ b/app/Modules/Admin/Providers/RouteServiceProvider.php
@@ -0,0 +1,69 @@
+mapApiRoutes();
+
+ $this->mapWebRoutes();
+ }
+
+ /**
+ * Define the "web" routes for the application.
+ *
+ * These routes all receive session state, CSRF protection, etc.
+ *
+ * @return void
+ */
+ protected function mapWebRoutes()
+ {
+ Route::middleware('web')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('Admin', '/Routes/web.php'));
+ }
+
+ /**
+ * Define the "api" routes for the application.
+ *
+ * These routes are typically stateless.
+ *
+ * @return void
+ */
+ protected function mapApiRoutes()
+ {
+ Route::prefix('api')
+ ->middleware('api')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('Admin', '/Routes/api.php'));
+ }
+}
diff --git a/app/Modules/Admin/Resources/assets/.gitkeep b/app/Modules/Admin/Resources/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Resources/assets/js/app.js b/app/Modules/Admin/Resources/assets/js/app.js
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Resources/assets/sass/app.scss b/app/Modules/Admin/Resources/assets/sass/app.scss
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Resources/lang/.gitkeep b/app/Modules/Admin/Resources/lang/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Resources/views/.gitkeep b/app/Modules/Admin/Resources/views/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Modules/Admin/Resources/views/admin.blade.php b/app/Modules/Admin/Resources/views/admin.blade.php
new file mode 100644
index 0000000..1ff0628
--- /dev/null
+++ b/app/Modules/Admin/Resources/views/admin.blade.php
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ Title
+
+
+
+
+
+
+
+
+
diff --git a/app/Modules/Admin/Resources/views/index.blade.php b/app/Modules/Admin/Resources/views/index.blade.php
new file mode 100644
index 0000000..4ae9069
--- /dev/null
+++ b/app/Modules/Admin/Resources/views/index.blade.php
@@ -0,0 +1,9 @@
+@extends('admin::layouts.master')
+
+@section('content')
+ Hello World
+
+
+ This view is loaded from module: {!! config('admin.name') !!}
+
+@endsection
diff --git a/app/Modules/Admin/Resources/views/layouts/master.blade.php b/app/Modules/Admin/Resources/views/layouts/master.blade.php
new file mode 100644
index 0000000..b09a67f
--- /dev/null
+++ b/app/Modules/Admin/Resources/views/layouts/master.blade.php
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Module Admin
+
+ {{-- Laravel Mix - CSS File --}}
+ {{-- --}}
+
+
+
+ @yield('content')
+
+ {{-- Laravel Mix - JS File --}}
+ {{-- --}}
+
+
diff --git a/app/Modules/Admin/Resources/vue-element-admin/App.vue b/app/Modules/Admin/Resources/vue-element-admin/App.vue
new file mode 100644
index 0000000..ec9032c
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/App.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/.idea/api.iml b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/api.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/api.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/.idea/misc.xml b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/misc.xml
new file mode 100644
index 0000000..7e5bdf8
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/.idea/modules.xml b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/modules.xml
new file mode 100644
index 0000000..d50cf45
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/.idea/vcs.xml b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/vcs.xml
new file mode 100644
index 0000000..821e530
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/.idea/workspace.xml b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/workspace.xml
new file mode 100644
index 0000000..4b8a53e
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/.idea/workspace.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1610455420023
+
+
+ 1610455420023
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/admin_menus.js b/app/Modules/Admin/Resources/vue-element-admin/api/admin_menus.js
new file mode 100644
index 0000000..05c2fd5
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/admin_menus.js
@@ -0,0 +1,48 @@
+import request from '@/utils/request'
+
+export function getMenusSelect() {
+ return request({
+ url: '/admin_menus/getSelectLists',
+ method: 'get'
+ })
+}
+
+export function getList(params) {
+ return request({
+ url: 'admin_menus',
+ method: 'get',
+ params
+ })
+}
+
+export function create(data) {
+ return request({
+ url: '/admin_menus/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: `/admin_menus/update`,
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/admin_menus/delete`,
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: `/admin_menus/changeFiledStatus`,
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/admin_roles.js b/app/Modules/Admin/Resources/vue-element-admin/api/admin_roles.js
new file mode 100644
index 0000000..150c680
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/admin_roles.js
@@ -0,0 +1,48 @@
+import request from '@/utils/request'
+
+export function getRolesSelect() {
+ return request({
+ url: '/admin_roles/getSelectLists',
+ method: 'get'
+ })
+}
+
+export function getList(params) {
+ return request({
+ url: 'admin_roles',
+ method: 'get',
+ params
+ })
+}
+
+export function create(data) {
+ return request({
+ url: '/admin_roles/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: `/admin_roles/update`,
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/admin_roles/delete`,
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: `/admin_roles/changeFiledStatus`,
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/adminloginlogs.js b/app/Modules/Admin/Resources/vue-element-admin/api/adminloginlogs.js
new file mode 100644
index 0000000..2e3fea3
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/adminloginlogs.js
@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+ return request({
+ url: 'adminloginlogs',
+ method: 'get',
+ params
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/adminloginlogs/delete`,
+ method: 'delete',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/adminlogs.js b/app/Modules/Admin/Resources/vue-element-admin/api/adminlogs.js
new file mode 100644
index 0000000..c303f10
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/adminlogs.js
@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+ return request({
+ url: 'adminlogs',
+ method: 'get',
+ params
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/adminlogs/delete`,
+ method: 'delete',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/admins.js b/app/Modules/Admin/Resources/vue-element-admin/api/admins.js
new file mode 100644
index 0000000..201bbe3
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/admins.js
@@ -0,0 +1,57 @@
+import request from '@/utils/request'
+
+export function getAdminsSelect(params) {
+ return request({
+ url: '/admins/getSelectLists',
+ method: 'get',
+ params
+ });
+}
+
+export function getList(params) {
+ return request({
+ url: 'admins',
+ method: 'get',
+ params
+ })
+}
+
+// export function detail(data) {
+// return request({
+// url: '/admins/detail',
+// method: 'post',
+// data
+// })
+// }
+
+export function create(data) {
+ return request({
+ url: '/admins/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: `/admins/update`,
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/admins/delete`,
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: `/admins/changeFiledStatus`,
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/article_categories.js b/app/Modules/Admin/Resources/vue-element-admin/api/article_categories.js
new file mode 100644
index 0000000..99b3a5d
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/article_categories.js
@@ -0,0 +1,48 @@
+import request from '@/utils/request'
+
+export function getCategorySelect() {
+ return request({
+ url: '/article_categories/getSelectLists',
+ method: 'get'
+ })
+}
+
+export function getList(params) {
+ return request({
+ url: 'article_categories',
+ method: 'get',
+ params
+ })
+}
+
+export function create(data) {
+ return request({
+ url: '/article_categories/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: `/article_categories/update`,
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/article_categories/delete`,
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: `/article_categories/changeFiledStatus`,
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/article_labels.js b/app/Modules/Admin/Resources/vue-element-admin/api/article_labels.js
new file mode 100644
index 0000000..ebc0126
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/article_labels.js
@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function getArticleLabelSelect(params) {
+ return request({
+ url: '/article_labels/getSelectLists',
+ method: 'get',
+ params
+ });
+}
+
+export function getList(params) {
+ return request({
+ url: '/article_labels',
+ method: 'get',
+ params
+ });
+}
+
+export function create(data) {
+ return request({
+ url: '/article_labels/create',
+ method: 'post',
+ data
+ });
+}
+
+export function update(data) {
+ return request({
+ url: '/article_labels/update',
+ method: 'put',
+ data
+ });
+}
+
+export function setDel(data) {
+ return request({
+ url: '/article_labels/delete',
+ method: 'delete',
+ data
+ });
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/articles.js b/app/Modules/Admin/Resources/vue-element-admin/api/articles.js
new file mode 100644
index 0000000..74cc060
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/articles.js
@@ -0,0 +1,56 @@
+import request from '@/utils/request'
+
+export function getCategoriesSelect() {
+ return request({
+ url: '/article_categories/getSelectLists',
+ method: 'get'
+ })
+}
+
+export function getList(params) {
+ return request({
+ url: 'articles',
+ method: 'get',
+ params
+ })
+}
+
+export function detail(id) {
+ return request({
+ url: '/articles/detail',
+ method: 'get',
+ params: { article_id:id }
+ })
+}
+
+export function create(data) {
+ return request({
+ url: '/articles/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: `/articles/update`,
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: `/articles/delete`,
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: '/articles/changeFiledStatus',
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/banners.js b/app/Modules/Admin/Resources/vue-element-admin/api/banners.js
new file mode 100644
index 0000000..188f87d
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/banners.js
@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+ return request({
+ url: '/banners',
+ method: 'get',
+ params
+ });
+}
+
+export function create(data) {
+ return request({
+ url: '/banners/create',
+ method: 'post',
+ data
+ });
+}
+
+export function update(data) {
+ return request({
+ url: '/banners/update',
+ method: 'put',
+ data
+ });
+}
+
+export function setDel(data) {
+ return request({
+ url: '/banners/delete',
+ method: 'delete',
+ data
+ });
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: '/banners/changeFiledStatus',
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/common.js b/app/Modules/Admin/Resources/vue-element-admin/api/common.js
new file mode 100644
index 0000000..7f287ed
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/common.js
@@ -0,0 +1,16 @@
+import request from '@/utils/request'
+
+export function getUploadUrl() {
+ return process.env.VUE_APP_BASE_API + '/upload_file'
+}
+
+export function getBatchUploadUrl() {
+ return process.env.VUE_APP_BASE_API + '/upload_files'
+}
+
+export function getMonthLists() {
+ return request({
+ url: '/get_month_lists',
+ method: 'get'
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/configs.js b/app/Modules/Admin/Resources/vue-element-admin/api/configs.js
new file mode 100644
index 0000000..4a9b2fa
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/configs.js
@@ -0,0 +1,65 @@
+import request from '@/utils/request'
+
+export function getList(query) {
+ return request({
+ url: '/configs',
+ method: 'get',
+ params: query
+ })
+}
+
+// 获取配置分组与配置类型
+export function getConfigGroupType() {
+ return request({
+ url: '/configs/getConfigGroupType',
+ method: 'get',
+ })
+}
+
+export function detail(id) {
+ return request({
+ url: '/configs/detail',
+ method: 'get',
+ params: { config_id:id }
+ })
+}
+
+export function create(data) {
+ return request({
+ url: '/configs/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: '/configs/update',
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: '/configs/delete',
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: '/configs/changeFiledStatus',
+ method: 'put',
+ data
+ })
+}
+
+// 同步配置文件
+export function pushRefreshConfig() {
+ return request({
+ url: '/configs/pushRefreshConfig',
+ method: 'put',
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/friendlinks.js b/app/Modules/Admin/Resources/vue-element-admin/api/friendlinks.js
new file mode 100644
index 0000000..7635f09
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/friendlinks.js
@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+ return request({
+ url: '/friendlinks',
+ method: 'get',
+ params
+ })
+}
+
+export function create(data) {
+ return request({
+ url: '/friendlinks/create',
+ method: 'post',
+ data
+ })
+}
+
+export function update(data) {
+ return request({
+ url: '/friendlinks/update',
+ method: 'put',
+ data
+ })
+}
+
+export function setDel(data) {
+ return request({
+ url: '/friendlinks/delete',
+ method: 'delete',
+ data
+ })
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: '/friendlinks/changeFiledStatus',
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/indexs.js b/app/Modules/Admin/Resources/vue-element-admin/api/indexs.js
new file mode 100644
index 0000000..44a4d46
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/indexs.js
@@ -0,0 +1,42 @@
+import request from '@/utils/request'
+
+// 首页 - 基础统计数据
+export function statistics() {
+ return request({
+ url: '/indexs',
+ method: 'get'
+ })
+}
+
+// 首页 - 请求日志统计表数据
+export function logsStatistics() {
+ return request({
+ url: '/logsStatistics',
+ method: 'get'
+ })
+}
+
+// 编辑登录管理员信息
+export function updateAdmin(data) {
+ return request({
+ url: '/updateAdmin',
+ method: 'put',
+ data,
+ })
+}
+
+// 版本历史记录
+export function versionLogs() {
+ return request({
+ url: '/versionLogs',
+ method: 'get',
+ })
+}
+
+// 服务器状态
+export function getServerStatus() {
+ return request({
+ url: '/getServerStatus',
+ method: 'get',
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/login.js b/app/Modules/Admin/Resources/vue-element-admin/api/login.js
new file mode 100644
index 0000000..13124ef
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/login.js
@@ -0,0 +1,30 @@
+import request from '@/utils/request'
+
+export function login(data) {
+ return request({
+ url: '/auth/login',
+ method: 'post',
+ data
+ })
+}
+
+export function getInfo() {
+ return request({
+ url: '/auth/me',
+ method: 'post'
+ })
+}
+
+export function getMenus() {
+ return request({
+ url: '/auth/getRabcList',
+ method: 'post'
+ })
+}
+
+export function logout() {
+ return request({
+ url: '/auth/logout',
+ method: 'post'
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/api/versions.js b/app/Modules/Admin/Resources/vue-element-admin/api/versions.js
new file mode 100644
index 0000000..0abb550
--- /dev/null
+++ b/app/Modules/Admin/Resources/vue-element-admin/api/versions.js
@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+ return request({
+ url: '/versions',
+ method: 'get',
+ params
+ });
+}
+
+export function create(data) {
+ return request({
+ url: '/versions/create',
+ method: 'post',
+ data
+ });
+}
+
+export function update(data) {
+ return request({
+ url: '/versions/update',
+ method: 'put',
+ data
+ });
+}
+
+export function setDel(data) {
+ return request({
+ url: '/versions/delete',
+ method: 'delete',
+ data
+ });
+}
+
+export function changeFiledStatus(data) {
+ return request({
+ url: '/versions/changeFiledStatus',
+ method: 'put',
+ data
+ })
+}
diff --git a/app/Modules/Admin/Resources/vue-element-admin/assets/401_images/401.gif b/app/Modules/Admin/Resources/vue-element-admin/assets/401_images/401.gif
new file mode 100644
index 0000000000000000000000000000000000000000..cd6e0d9433421b3f29d0ec0c40f755e354728000
GIT binary patch
literal 164227
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