diff --git a/.bowerrc b/.bowerrc index d25737ea239c0342befb53543f8deda6e267ae46..e818e6b2025ab284ca875c240c056426a25d49e1 100644 --- a/.bowerrc +++ b/.bowerrc @@ -1,3 +1,10 @@ { - "directory" : "public/assets/libs" + "directory": "public/assets/libs", + "ignoredDependencies": [ + "es6-promise", + "file-saver", + "html2canvas", + "jspdf", + "jspdf-autotable" + ] } \ No newline at end of file diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000000000000000000000000000000000000..ccd0f2919a1d4bb8305046feb04e4433b805f146 --- /dev/null +++ b/.env.sample @@ -0,0 +1,11 @@ +[app] +debug = false +trace = false + +[database] +hostname = 127.0.0.1 +database = fastadmin +username = root +password = root +hostport = 3306 +prefix = fa_ diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index e3ef39600787454caea96a5d737e3f82ad0b63e9..3f2ebf8ce386ea0bf49af91dca00f2043fc394fb --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,17 @@ -/nbproject/ -.idea -composer.lock -*.log -*.css.map -thinkphp -vendor -runtime -public/assets/libs/ -/application/admin/command/Install/*.lock -/public/uploads +/nbproject/ +/thinkphp/ +/vendor/ +/runtime/* +/addons/* +/application/admin/command/Install/*.lock +/public/assets/libs/ +/public/assets/addons/* +/public/uploads/* +.idea +composer.lock +*.log +*.css.map +!.gitkeep +.env +.svn +.vscode diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index 80ef29106ee7ef0d122843a948586a7786f251cd..0000000000000000000000000000000000000000 --- a/.travis.yml +++ /dev/null @@ -1,42 +0,0 @@ -sudo: false - -language: php - -branches: - only: - - stable - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - composer self-update - -install: - - composer install --no-dev --no-interaction --ignore-platform-reqs - - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip . - - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0" - - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0" - - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0" - - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip . - -script: - - php think unit - -deploy: - provider: releases - api_key: - secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw= - file: - - ThinkPHP_Core.zip - - ThinkPHP_Full.zip - skip_cleanup: true - on: - tags: true diff --git a/LICENSE b/LICENSE index 2e38b71443f38fa9e6206013017e95bc11dc4098..088bc3f5c5d6aface18ad41df633550fe3af89ef 100755 --- a/LICENSE +++ b/LICENSE @@ -1,191 +1,191 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "{}" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright 2017 Karson - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2017 Karson + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index 10802d1c5487b61fab1573dcaa34d2929645a326..e20ce74600d2b0b4ec95bac6db5fd75c0a6841b2 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,90 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。 -=============== -## **主要特性** +## 主要特性 * 基于`Auth`验证的权限管理系统 * 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置 * 支持单管理员多角色 - * 支持目录和控制器结构一键生成权限节点 + * 支持管理子级数据或个人数据 +* 强大的一键生成功能 + * 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等 + * 一键压缩打包JS和CSS文件,一键CDN静态资源部署 + * 一键生成控制器菜单和规则 + * 一键生成API接口文档 * 完善的前端功能组件开发 * 基于`AdminLTE`二次开发 * 基于`Bootstrap`开发,自适应手机、平板、PC * 基于`RequireJS`进行JS模块管理,按需加载 * 基于`Less`进行样式开发 - * 基于`Bower`进行前端组件包管理 -* 数据库表一键生成`CRUD`,包括控制器、模型、视图、JS、语言包 -* 一键压缩打包JS和CSS文件 +* 强大的插件扩展功能,在线安装卸载升级插件 +* 通用的会员模块和API模块 +* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证 +* 二级域名部署支持,同时域名支持绑定到应用插件 * 多语言支持,服务端及客户端支持 -* 无缝整合又拍云上传功能 +* 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩 +* 支持表格固定列、固定表头、跨页选择、Excel导出、模板渲染等功能 +* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)、[博客](https://www.fastadmin.net/store/blog.html)、[知识付费问答](https://www.fastadmin.net/store/ask.html)、[在线投票系统](https://www.fastadmin.net/store/vote.html)、[B2C商城](https://www.fastadmin.net/store/shopro.html)、[B2B2C商城](https://www.fastadmin.net/store/wanlshop.html)) +* 支持CMS、博客、知识付费问答无缝整合[Xunsearch全文搜索](https://www.fastadmin.net/store/xunsearch.html) +* 第三方小程序支持([CMS小程序](https://www.fastadmin.net/store/cms.html)、[预订小程序](https://www.fastadmin.net/store/ball.html)、[问答小程序](https://www.fastadmin.net/store/ask.html)、[点餐小程序](https://www.fastadmin.net/store/unidrink.html)、[B2C小程序](https://www.fastadmin.net/store/shopro.html)、[B2B2C小程序](https://www.fastadmin.net/store/wanlshop.html)、[博客小程序](https://www.fastadmin.net/store/blog.html)) +* 整合第三方短信接口(阿里云、腾讯云短信) +* 无缝整合第三方云存储(七牛云、阿里云OSS、又拍云)功能,支持云储存分片上传 +* 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器) * 第三方登录(QQ、微信、微博)整合 -* Ucenter整合 +* 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付 +* 丰富的插件应用市场 -## **安装使用** +## 安装使用 -http://doc.fastadmin.net +https://doc.fastadmin.net -## **在线演示** +## 在线演示 -http://demo.fastadmin.net +https://demo.fastadmin.net 用户名:admin 密 码:123456 -提 示:演示站数据无法进行删除和修改,只能新增,完整体验请下载源码安装体验 +提 示:演示站数据无法进行修改,请下载源码安装体验全部功能 -## **界面截图** -![控制台](https://git.oschina.net/uploads/images/2017/0411/113717_e99ff3e7_10933.png "控制台") +## 界面截图 +![控制台](https://images.gitee.com/uploads/images/2020/0929/202947_8db2d281_10933.gif "控制台") -## **问题反馈** +## 问题反馈 在使用中有任何问题,请使用以下联系方式联系我们 -交流社区: http://forum.fastadmin.net +交流社区: https://ask.fastadmin.net -QQ群: [636393962](https://jq.qq.com/?_wv=1027&k=487PNBb) - -Email: (karsonzhang#163.com, 把#换成@) - -weibo: [@karsonzhang](https://weibo.com/karsonzhang) +QQ群: [636393962](https://jq.qq.com/?_wv=1027&k=487PNBb)(满) [708784003](https://jq.qq.com/?_wv=1027&k=5ObjtwM)(满) [964776039](https://jq.qq.com/?_wv=1027&k=59qjU2P)(3群) [749803490](https://jq.qq.com/?_wv=1027&k=5tczi88)(满) [767103006](https://jq.qq.com/?_wv=1027&k=5Z1U751)(满) [675115483](https://jq.qq.com/?_wv=1027&k=54I6mts)(6群) Github: https://github.com/karsonzhang/fastadmin -Git@OSC: https://git.oschina.net/karson/fastadmin +Gitee: https://gitee.com/karson/fastadmin -## **特别鸣谢** +## 特别鸣谢 感谢以下的项目,排名不分先后 ThinkPHP:http://www.thinkphp.cn -AdminLTE:https://almsaeedstudio.com +AdminLTE:https://adminlte.io Bootstrap:http://getbootstrap.com jQuery:http://jquery.com +Bootstrap-table:https://github.com/wenzhixin/bootstrap-table + +Nice-validator: https://validator.niceue.com + +SelectPage: https://github.com/TerryZ/SelectPage + +Layer: https://layer.layui.com + +DropzoneJS: https://www.dropzonejs.com + ## 版权信息 @@ -73,6 +92,6 @@ FastAdmin遵循Apache2开源协议发布,并提供免费使用。 本项目包含的第三方源码和二进制文件之版权信息另行标注。 -版权所有Copyright © 2017-2018 by FastAdmin (http://www.fastadmin.net) +版权所有Copyright © 2017-2020 by FastAdmin (https://www.fastadmin.net) -All rights reserved。 \ No newline at end of file +All rights reserved。 diff --git a/addons/.gitkeep b/addons/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/addons/.gitkeep @@ -0,0 +1 @@ + diff --git a/application/admin/behavior/AdminLog.php b/application/admin/behavior/AdminLog.php index 26d4e6dc15d67d88610c18b6d79902e076b4a201..f5bcd3c9f50600304c60de482364869a62cf5a0e 100644 --- a/application/admin/behavior/AdminLog.php +++ b/application/admin/behavior/AdminLog.php @@ -2,17 +2,13 @@ namespace app\admin\behavior; -use think\Config; - class AdminLog { - public function run(&$params) { - if (request()->isPost()) - { + //只记录POST请求的日志 + if (request()->isPost() && config('fastadmin.auto_record_log')) { \app\admin\model\AdminLog::record(); } } - } diff --git a/application/admin/command/Addon.php b/application/admin/command/Addon.php new file mode 100644 index 0000000000000000000000000000000000000000..aaba93a843378d0a2b04def04ba40bb44ef5d669 --- /dev/null +++ b/application/admin/command/Addon.php @@ -0,0 +1,383 @@ +setName('addon') + ->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null) + ->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/install/uninstall/refresh/upgrade/package/move)', 'create') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) + ->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null) + ->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null) + ->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null) + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null) + ->setDescription('Addon manager'); + } + + protected function execute(Input $input, Output $output) + { + $name = $input->getOption('name') ?: ''; + $action = $input->getOption('action') ?: ''; + if (stripos($name, 'addons' . DS) !== false) { + $name = explode(DS, $name)[1]; + } + //强制覆盖 + $force = $input->getOption('force'); + //版本 + $release = $input->getOption('release') ?: ''; + //uid + $uid = $input->getOption('uid') ?: ''; + //token + $token = $input->getOption('token') ?: ''; + + include dirname(__DIR__) . DS . 'common.php'; + + if (!$name) { + throw new Exception('Addon name could not be empty'); + } + if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package', 'move'])) { + throw new Exception('Please input correct action name'); + } + + // 查询一次SQL,判断连接是否正常 + Db::execute("SELECT 1"); + + $addonDir = ADDON_PATH . $name . DS; + switch ($action) { + case 'create': + //非覆盖模式时如果存在则报错 + if (is_dir($addonDir) && !$force) { + throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true "); + } + //如果存在先移除 + if (is_dir($addonDir)) { + rmdirs($addonDir); + } + mkdir($addonDir, 0755, true); + mkdir($addonDir . DS . 'controller', 0755, true); + $menuList = \app\common\library\Menu::export($name); + $createMenu = $this->getCreateMenu($menuList); + $prefix = Config::get('database.prefix'); + $createTableSql = ''; + try { + $result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;"); + if (isset($result[0]) && isset($result[0]['Create Table'])) { + $createTableSql = $result[0]['Create Table']; + } + } catch (PDOException $e) { + + } + + $data = [ + 'name' => $name, + 'addon' => $name, + 'addonClassName' => ucfirst($name), + 'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu) . ";\n\tMenu::create(\$menu);" : '', + 'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '', + 'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '', + 'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '', + ]; + $this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php'); + $this->writeToFile("config", $data, $addonDir . 'config.php'); + $this->writeToFile("info", $data, $addonDir . 'info.ini'); + $this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php'); + if ($createTableSql) { + $createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql); + file_put_contents($addonDir . 'install.sql', $createTableSql); + } + + $output->info("Create Successed!"); + break; + case 'disable': + case 'enable': + try { + //调用启用、禁用的方法 + Service::$action($name, 0); + } catch (AddonException $e) { + if ($e->getCode() != -3) { + throw new Exception($e->getMessage()); + } + if (!$force) { + //如果有冲突文件则提醒 + $data = $e->getData(); + foreach ($data['conflictlist'] as $k => $v) { + $output->warning($v); + } + $output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + //调用启用、禁用的方法 + Service::$action($name, 1); + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + $output->info(ucfirst($action) . " Successed!"); + break; + case 'install': + //非覆盖模式时如果存在则报错 + if (is_dir($addonDir) && !$force) { + throw new Exception("addon already exists!\nIf you need to install again, use the parameter --force=true "); + } + //如果存在先移除 + if (is_dir($addonDir)) { + rmdirs($addonDir); + } + // 获取本地路径 + $local = $input->getOption('local'); + try { + Service::install($name, 0, ['version' => $release], $local); + } catch (AddonException $e) { + if ($e->getCode() != -3) { + throw new Exception($e->getMessage()); + } + if (!$force) { + //如果有冲突文件则提醒 + $data = $e->getData(); + foreach ($data['conflictlist'] as $k => $v) { + $output->warning($v); + } + $output->info("Are you sure you want to override all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + Service::install($name, 1, ['version' => $release, 'uid' => $uid, 'token' => $token], $local); + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + + $output->info("Install Successed!"); + break; + case 'uninstall': + //非覆盖模式时如果存在则报错 + if (!$force) { + throw new Exception("If you need to uninstall addon, use the parameter --force=true "); + } + try { + Service::uninstall($name, 0); + } catch (AddonException $e) { + if ($e->getCode() != -3) { + throw new Exception($e->getMessage()); + } + if (!$force) { + //如果有冲突文件则提醒 + $data = $e->getData(); + foreach ($data['conflictlist'] as $k => $v) { + $output->warning($v); + } + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + Service::uninstall($name, 1); + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + + $output->info("Uninstall Successed!"); + break; + case 'refresh': + Service::refresh(); + $output->info("Refresh Successed!"); + break; + case 'upgrade': + Service::upgrade($name, ['version' => $release, 'uid' => $uid, 'token' => $token]); + $output->info("Upgrade Successed!"); + break; + case 'package': + $infoFile = $addonDir . 'info.ini'; + if (!is_file($infoFile)) { + throw new Exception(__('Addon info file was not found')); + } + + $info = get_addon_info($name); + if (!$info) { + throw new Exception(__('Addon info file data incorrect')); + } + $infoname = isset($info['name']) ? $info['name'] : ''; + if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) { + throw new Exception(__('Addon info name incorrect')); + } + + $infoversion = isset($info['version']) ? $info['version'] : ''; + if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) { + throw new Exception(__('Addon info version incorrect')); + } + + $addonTmpDir = RUNTIME_PATH . 'addons' . DS; + if (!is_dir($addonTmpDir)) { + @mkdir($addonTmpDir, 0755, true); + } + $addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip'; + if (!class_exists('ZipArchive')) { + throw new Exception(__('ZinArchive not install')); + } + $zip = new \ZipArchive; + $zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); + + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) { + if (!$file->isDir()) { + $filePath = $file->getRealPath(); + $relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir))); + if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) { + $zip->addFile($filePath, $relativePath); + } + } + } + $zip->close(); + $output->info("Package Successed!"); + break; + case 'move': + $movePath = [ + 'adminOnlySelfDir' => ['admin/behavior', 'admin/controller', 'admin/library', 'admin/model', 'admin/validate', 'admin/view'], + 'adminAllSubDir' => ['admin/lang'], + 'publicDir' => ['public/assets/addons', 'public/assets/js/backend'] + ]; + $paths = []; + $appPath = str_replace('/', DS, APP_PATH); + $rootPath = str_replace('/', DS, ROOT_PATH); + foreach ($movePath as $k => $items) { + switch ($k) { + case 'adminOnlySelfDir': + foreach ($items as $v) { + $v = str_replace('/', DS, $v); + $oldPath = $appPath . $v . DS . $name; + $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $name; + $paths[$oldPath] = $newPath; + } + break; + case 'adminAllSubDir': + foreach ($items as $v) { + $v = str_replace('/', DS, $v); + $vPath = $appPath . $v; + $list = scandir($vPath); + foreach ($list as $_v) { + if (!in_array($_v, ['.', '..']) && is_dir($vPath . DS . $_v)) { + $oldPath = $appPath . $v . DS . $_v . DS . $name; + $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $_v . DS . $name; + $paths[$oldPath] = $newPath; + } + } + } + break; + case 'publicDir': + foreach ($items as $v) { + $v = str_replace('/', DS, $v); + $oldPath = $rootPath . $v . DS . $name; + $newPath = $rootPath . 'addons' . DS . $name . DS . $v . DS . $name; + $paths[$oldPath] = $newPath; + } + break; + } + } + foreach ($paths as $oldPath => $newPath) { + if (is_dir($oldPath)) { + if ($force) { + if (is_dir($newPath)) { + $list = scandir($newPath); + foreach ($list as $_v) { + if (!in_array($_v, ['.', '..'])) { + $file = $newPath . DS . $_v; + @chmod($file, 0777); + @unlink($file); + } + } + @rmdir($newPath); + } + } + copydirs($oldPath, $newPath); + } + } + break; + default: + break; + } + } + + /** + * 获取创建菜单的数组 + * @param array $menu + * @return array + */ + protected function getCreateMenu($menu) + { + $result = []; + foreach ($menu as $k => & $v) { + $arr = [ + 'name' => $v['name'], + 'title' => $v['title'], + ]; + if ($v['icon'] != 'fa fa-circle-o') { + $arr['icon'] = $v['icon']; + } + if ($v['ismenu']) { + $arr['ismenu'] = $v['ismenu']; + } + if (isset($v['childlist']) && $v['childlist']) { + $arr['sublist'] = $this->getCreateMenu($v['childlist']); + } + $result[] = $arr; + } + return $result; + } + + /** + * 写入到文件 + * @param string $name + * @param array $data + * @param string $pathname + * @return mixed + */ + protected function writeToFile($name, $data, $pathname) + { + $search = $replace = []; + foreach ($data as $k => $v) { + $search[] = "{%{$k}%}"; + $replace[] = $v; + } + $stub = file_get_contents($this->getStub($name)); + $content = str_replace($search, $replace, $stub); + + if (!is_dir(dirname($pathname))) { + mkdir(strtolower(dirname($pathname)), 0755, true); + } + return file_put_contents($pathname, $content); + } + + /** + * 获取基础模板 + * @param string $name + * @return string + */ + protected function getStub($name) + { + return __DIR__ . '/Addon/stubs/' . $name . '.stub'; + } + +} diff --git a/application/admin/command/Addon/stubs/addon.stub b/application/admin/command/Addon/stubs/addon.stub new file mode 100644 index 0000000000000000000000000000000000000000..824e02c1c42f6f6de1d40dd16808676edb7fe2ae --- /dev/null +++ b/application/admin/command/Addon/stubs/addon.stub @@ -0,0 +1,68 @@ +getConfig()); + // 可以返回模板,模板文件默认读取的为插件目录中的文件。模板名不能为空! + //return $this->fetch('view/info'); + } + +} diff --git a/application/admin/command/Addon/stubs/config.stub b/application/admin/command/Addon/stubs/config.stub new file mode 100644 index 0000000000000000000000000000000000000000..034d73764f27613bd13430bf3882a80b22a0263e --- /dev/null +++ b/application/admin/command/Addon/stubs/config.stub @@ -0,0 +1,40 @@ + 'usernmae', + //显示的标题 + 'title' => '用户名', + //类型 + 'type' => 'string', + //数据字典 + 'content' => [ + ], + //值 + 'value' => '', + //验证规则 + 'rule' => 'required', + //错误消息 + 'msg' => '', + //提示消息 + 'tip' => '', + //成功消息 + 'ok' => '', + //扩展信息 + 'extend' => '' + ], + [ + 'name' => 'password', + 'title' => '密码', + 'type' => 'string', + 'content' => [ + ], + 'value' => '', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '' + ], +]; diff --git a/application/admin/command/Addon/stubs/controller.stub b/application/admin/command/Addon/stubs/controller.stub new file mode 100644 index 0000000000000000000000000000000000000000..d79476f2578778a1e22159c2c1da053458022165 --- /dev/null +++ b/application/admin/command/Addon/stubs/controller.stub @@ -0,0 +1,15 @@ +error("当前插件暂无前台页面"); + } + +} diff --git a/application/admin/command/Addon/stubs/info.stub b/application/admin/command/Addon/stubs/info.stub new file mode 100644 index 0000000000000000000000000000000000000000..a6a3496fd55283ad41b6b5fcd81b3fd7c2862342 --- /dev/null +++ b/application/admin/command/Addon/stubs/info.stub @@ -0,0 +1,7 @@ +name = {%name%} +title = 插件名称{%name%} +intro = FastAdmin插件 +author = yourname +website = https://www.fastadmin.net +version = 1.0.0 +state = 1 \ No newline at end of file diff --git a/application/admin/command/Api.php b/application/admin/command/Api.php new file mode 100644 index 0000000000000000000000000000000000000000..ab44912741a6d77d300cd8aa9059785fba2f6ee2 --- /dev/null +++ b/application/admin/command/Api.php @@ -0,0 +1,179 @@ +setName('api') + ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') + ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') + ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') + ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) + ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) + ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name']) + ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) + ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') + ->setDescription('Build Api document from controller'); + } + + protected function execute(Input $input, Output $output) + { + $apiDir = __DIR__ . DS . 'Api' . DS; + + $force = $input->getOption('force'); + $url = $input->getOption('url'); + $language = $input->getOption('language'); + $language = $language ? $language : 'zh-cn'; + $langFile = $apiDir . 'lang' . DS . $language . '.php'; + if (!is_file($langFile)) { + throw new Exception('language file not found'); + } + $lang = include_once $langFile; + // 目标目录 + $output_dir = ROOT_PATH . 'public' . DS; + $output_file = $output_dir . $input->getOption('output'); + if (is_file($output_file) && !$force) { + throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true "); + } + // 模板文件 + $template_dir = $apiDir . 'template' . DS; + $template_file = $template_dir . $input->getOption('template'); + if (!is_file($template_file)) { + throw new Exception('template file not found'); + } + // 额外的类 + $classes = $input->getOption('class'); + // 标题 + $title = $input->getOption('title'); + // 作者 + $author = $input->getOption('author'); + // 模块 + $module = $input->getOption('module'); + + $moduleDir = APP_PATH . $module . DS; + if (!is_dir($moduleDir)) { + throw new Exception('module not found'); + } + + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + if (extension_loaded('Zend OPcache')) { + $configuration = opcache_get_configuration(); + $directives = $configuration['directives']; + $configName = request()->isCli() ? 'opcache.enable_cli' : 'opcache.enable'; + if (!$directives[$configName]) { + throw new Exception("Please make sure {$configName} is turned on, Get help:https://forum.fastadmin.net/d/1321"); + } + } else { + throw new Exception("Please make sure opcache already enabled, Get help:https://forum.fastadmin.net/d/1321"); + } + } + + $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($controllerDir), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) { + if (!$file->isDir() && $file->getExtension() == 'php') { + $filePath = $file->getRealPath(); + $classes[] = $this->get_class_from_file($filePath); + } + } + $classes = array_unique(array_filter($classes)); + + $config = [ + 'sitename' => config('site.name'), + 'title' => $title, + 'author' => $author, + 'description' => '', + 'apiurl' => $url, + 'language' => $language, + ]; + $builder = new Builder($classes); + $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); + + if (!file_put_contents($output_file, $content)) { + throw new Exception('Cannot save the content to ' . $output_file); + } + $output->info("Build Successed!"); + } + + /** + * get full qualified class name + * + * @param string $path_to_file + * @return string + * @author JBYRNE http://jarretbyrne.com/2015/06/197/ + */ + protected function get_class_from_file($path_to_file) + { + //Grab the contents of the file + $contents = file_get_contents($path_to_file); + + //Start with a blank namespace and class + $namespace = $class = ""; + + //Set helper values to know that we have found the namespace/class token and need to collect the string values after them + $getting_namespace = $getting_class = false; + + //Go through each token and evaluate it as necessary + foreach (token_get_all($contents) as $token) { + + //If this token is the namespace declaring, then flag that the next tokens will be the namespace name + if (is_array($token) && $token[0] == T_NAMESPACE) { + $getting_namespace = true; + } + + //If this token is the class declaring, then flag that the next tokens will be the class name + if (is_array($token) && $token[0] == T_CLASS) { + $getting_class = true; + } + + //While we're grabbing the namespace name... + if ($getting_namespace === true) { + + //If the token is a string or the namespace separator... + if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) { + + //Append the token's value to the name of the namespace + $namespace .= $token[1]; + } elseif ($token === ';') { + + //If the token is the semicolon, then we're done with the namespace declaration + $getting_namespace = false; + } + } + + //While we're grabbing the class name... + if ($getting_class === true) { + + //If the token is a string, it's the name of the class + if (is_array($token) && $token[0] == T_STRING) { + + //Store the token's value as the class name + $class = $token[1]; + + //Got what we need, stope here + break; + } + } + } + + //Build the fully-qualified class name and return it + return $namespace ? $namespace . '\\' . $class : $class; + } +} diff --git a/application/admin/command/Api/lang/zh-cn.php b/application/admin/command/Api/lang/zh-cn.php new file mode 100644 index 0000000000000000000000000000000000000000..2154aa4dcd4d4222650d863d9c1fd11dca24ba9e --- /dev/null +++ b/application/admin/command/Api/lang/zh-cn.php @@ -0,0 +1,22 @@ + '基础信息', + 'Sandbox' => '在线测试', + 'Sampleoutput' => '返回示例', + 'Headers' => 'Headers', + 'Parameters' => '参数', + 'Body' => '正文', + 'Name' => '名称', + 'Type' => '类型', + 'Required' => '必选', + 'Description' => '描述', + 'Send' => '提交', + 'Reset' => '重置', + 'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中', + 'Apiurltips' => 'API接口URL', + 'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中', + 'ReturnHeaders' => '响应头', + 'ReturnParameters' => '返回参数', + 'Response' => '响应输出', +]; diff --git a/application/admin/command/Api/library/Builder.php b/application/admin/command/Api/library/Builder.php new file mode 100755 index 0000000000000000000000000000000000000000..ff9b08e54cee10cbe41f5029fe52eb4622b6f74a --- /dev/null +++ b/application/admin/command/Api/library/Builder.php @@ -0,0 +1,244 @@ + + * @author Karson + */ +class Builder +{ + + /** + * + * @var \think\View + */ + public $view = null; + + /** + * parse classes + * @var array + */ + protected $classes = []; + + /** + * + * @param array $classes + */ + public function __construct($classes = []) + { + $this->classes = array_merge($this->classes, $classes); + $this->view = new \think\View(Config::get('template'), Config::get('view_replace_str')); + } + + protected function extractAnnotations() + { + foreach ($this->classes as $class) { + $classAnnotation = Extractor::getClassAnnotations($class); + // 如果忽略 + if (isset($classAnnotation['ApiInternal'])) { + continue; + } + Extractor::getClassMethodAnnotations($class); + } + $allClassAnnotation = Extractor::getAllClassAnnotations(); + $allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations(); + +// foreach ($allClassMethodAnnotation as $className => &$methods) { +// foreach ($methods as &$method) { +// //权重判断 +// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) { +// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh']; +// } +// } +// } +// unset($methods); + return [$allClassAnnotation, $allClassMethodAnnotation]; + } + + protected function generateHeadersTemplate($docs) + { + if (!isset($docs['ApiHeaders'])) { + return []; + } + + $headerslist = array(); + foreach ($docs['ApiHeaders'] as $params) { + $tr = array( + 'name' => $params['name'], + 'type' => $params['type'], + 'sample' => isset($params['sample']) ? $params['sample'] : '', + 'required' => isset($params['required']) ? $params['required'] : false, + 'description' => isset($params['description']) ? $params['description'] : '', + ); + $headerslist[] = $tr; + } + + return $headerslist; + } + + protected function generateParamsTemplate($docs) + { + if (!isset($docs['ApiParams'])) { + return []; + } + + $paramslist = array(); + foreach ($docs['ApiParams'] as $params) { + $tr = array( + 'name' => $params['name'], + 'type' => isset($params['type']) ? $params['type'] : 'string', + 'sample' => isset($params['sample']) ? $params['sample'] : '', + 'required' => isset($params['required']) ? $params['required'] : true, + 'description' => isset($params['description']) ? $params['description'] : '', + ); + $paramslist[] = $tr; + } + + return $paramslist; + } + + protected function generateReturnHeadersTemplate($docs) + { + if (!isset($docs['ApiReturnHeaders'])) { + return []; + } + + $headerslist = array(); + foreach ($docs['ApiReturnHeaders'] as $params) { + $tr = array( + 'name' => $params['name'], + 'type' => 'string', + 'sample' => isset($params['sample']) ? $params['sample'] : '', + 'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No', + 'description' => isset($params['description']) ? $params['description'] : '', + ); + $headerslist[] = $tr; + } + + return $headerslist; + } + + protected function generateReturnParamsTemplate($st_params) + { + if (!isset($st_params['ApiReturnParams'])) { + return []; + } + + $paramslist = array(); + foreach ($st_params['ApiReturnParams'] as $params) { + $tr = array( + 'name' => $params['name'], + 'type' => isset($params['type']) ? $params['type'] : 'string', + 'sample' => isset($params['sample']) ? $params['sample'] : '', + 'description' => isset($params['description']) ? $params['description'] : '', + ); + $paramslist[] = $tr; + } + + return $paramslist; + } + + protected function generateBadgeForMethod($data) + { + $method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]); + $labes = array( + 'POST' => 'label-primary', + 'GET' => 'label-success', + 'PUT' => 'label-warning', + 'DELETE' => 'label-danger', + 'PATCH' => 'label-default', + 'OPTIONS' => 'label-info' + ); + + return isset($labes[$method]) ? $labes[$method] : $labes['GET']; + } + + public function parse() + { + list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations(); + + $sectorArr = []; + foreach ($allClassAnnotations as $index => $allClassAnnotation) { + $sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0]; + $sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0; + } + arsort($sectorArr); + $routes = include_once CONF_PATH . 'route.php'; + $subdomain = false; + if (config('url_domain_deploy') && isset($routes['__domain__']) && isset($routes['__domain__']['api']) && $routes['__domain__']['api']) { + $subdomain = true; + } + $counter = 0; + $section = null; + $weigh = 0; + $docslist = []; + foreach ($allClassMethodAnnotations as $class => $methods) { + foreach ($methods as $name => $docs) { + if (isset($docs['ApiSector'][0])) { + $section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0]; + } else { + $section = $class; + } + if (0 === count($docs)) { + continue; + } + $route = is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0]; + if ($subdomain) { + $route = substr($route, 4); + } + $docslist[$section][$class . $name] = [ + 'id' => $counter, + 'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0], + 'method_label' => $this->generateBadgeForMethod($docs), + 'section' => $section, + 'route' => $route, + 'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0], + 'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0], + 'body' => isset($docs['ApiBody'][0]) ? is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0] : '', + 'headerslist' => $this->generateHeadersTemplate($docs), + 'paramslist' => $this->generateParamsTemplate($docs), + 'returnheaderslist' => $this->generateReturnHeadersTemplate($docs), + 'returnparamslist' => $this->generateReturnParamsTemplate($docs), + 'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0], + 'return' => isset($docs['ApiReturn']) ? is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0] : '', + ]; + $counter++; + } + } + + //重建排序 + foreach ($docslist as $index => &$methods) { + $methodSectorArr = []; + foreach ($methods as $name => $method) { + $methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0; + } + arsort($methodSectorArr); + $methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods); + } + $docslist = array_merge(array_flip(array_keys($sectorArr)), $docslist); + $docslist = array_filter($docslist , function($v) {return is_array($v) ; }) ; + return $docslist; + } + + public function getView() + { + return $this->view; + } + + /** + * 渲染 + * @param string $template + * @param array $vars + * @return string + */ + public function render($template, $vars = []) + { + $docslist = $this->parse(); + + return $this->view->display(file_get_contents($template), array_merge($vars, ['docslist' => $docslist])); + } +} diff --git a/application/admin/command/Api/library/Extractor.php b/application/admin/command/Api/library/Extractor.php new file mode 100644 index 0000000000000000000000000000000000000000..5591921b11be485915d627f75e10e51cb165fe54 --- /dev/null +++ b/application/admin/command/Api/library/Extractor.php @@ -0,0 +1,511 @@ + + */ +class Extractor +{ + + /** + * Static array to store already parsed annotations + * @var array + */ + private static $annotationCache; + + private static $classAnnotationCache; + + private static $classMethodAnnotationCache; + + /** + * Indicates that annotations should has strict behavior, 'false' by default + * @var boolean + */ + private $strict = false; + + /** + * Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects() + * @var string + */ + public $defaultNamespace = ''; + + /** + * Sets strict variable to true/false + * @param bool $value boolean value to indicate that annotations to has strict behavior + */ + public function setStrict($value) + { + $this->strict = (bool)$value; + } + + /** + * Sets default namespace to use in object instantiation + * @param string $namespace default namespace + */ + public function setDefaultNamespace($namespace) + { + $this->defaultNamespace = $namespace; + } + + /** + * Gets default namespace used in object instantiation + * @return string $namespace default namespace + */ + public function getDefaultAnnotationNamespace() + { + return $this->defaultNamespace; + } + + /** + * Gets all anotations with pattern @SomeAnnotation() from a given class + * + * @param string $className class name to get annotations + * @return array self::$classAnnotationCache all annotated elements + */ + public static function getClassAnnotations($className) + { + if (!isset(self::$classAnnotationCache[$className])) { + $class = new \ReflectionClass($className); + self::$classAnnotationCache[$className] = self::parseAnnotations($class->getDocComment()); + } + + return self::$classAnnotationCache[$className]; + } + + /** + * 获取类所有方法的属性配置 + * @param $className + * @return mixed + * @throws \ReflectionException + */ + public static function getClassMethodAnnotations($className) + { + $class = new \ReflectionClass($className); + + foreach ($class->getMethods() as $object) { + self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name); + } + + return self::$classMethodAnnotationCache[$className]; + } + + public static function getAllClassAnnotations() + { + return self::$classAnnotationCache; + } + + public static function getAllClassMethodAnnotations() + { + return self::$classMethodAnnotationCache; + } + + /** + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class + * + * @param string $className class name + * @param string $methodName method name to get annotations + * @return array self::$annotationCache all annotated elements of a method given + */ + public static function getMethodAnnotations($className, $methodName) + { + if (!isset(self::$annotationCache[$className . '::' . $methodName])) { + try { + $method = new \ReflectionMethod($className, $methodName); + $class = new \ReflectionClass($className); + if (!$method->isPublic() || $method->isConstructor()) { + $annotations = array(); + } else { + $annotations = self::consolidateAnnotations($method, $class); + } + } catch (\ReflectionException $e) { + $annotations = array(); + } + + self::$annotationCache[$className . '::' . $methodName] = $annotations; + } + + return self::$annotationCache[$className . '::' . $methodName]; + } + + /** + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class + * and instance its abcAnnotation class + * + * @param string $className class name + * @param string $methodName method name to get annotations + * @return array self::$annotationCache all annotated objects of a method given + */ + public function getMethodAnnotationsObjects($className, $methodName) + { + $annotations = $this->getMethodAnnotations($className, $methodName); + $objects = array(); + + $i = 0; + + foreach ($annotations as $annotationClass => $listParams) { + $annotationClass = ucfirst($annotationClass); + $class = $this->defaultNamespace . $annotationClass . 'Annotation'; + + // verify is the annotation class exists, depending if Annotations::strict is true + // if not, just skip the annotation instance creation. + if (!class_exists($class)) { + if ($this->strict) { + throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class)); + } else { + // silent skip & continue + continue; + } + } + + if (empty($objects[$annotationClass])) { + $objects[$annotationClass] = new $class(); + } + + foreach ($listParams as $params) { + if (is_array($params)) { + foreach ($params as $key => $value) { + $objects[$annotationClass]->set($key, $value); + } + } else { + $objects[$annotationClass]->set($i++, $params); + } + } + } + + return $objects; + } + + private static function consolidateAnnotations($method, $class) + { + $dockblockClass = $class->getDocComment(); + $docblockMethod = $method->getDocComment(); + $methodName = $method->getName(); + + $methodAnnotations = self::parseAnnotations($docblockMethod); + $classAnnotations = self::parseAnnotations($dockblockClass); + if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') { + return []; + } + + $properties = $class->getDefaultProperties(); + $noNeedLogin = isset($properties['noNeedLogin']) ? is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']] : []; + $noNeedRight = isset($properties['noNeedRight']) ? is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']] : []; + + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr); + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr); + + if (!isset($methodAnnotations['ApiMethod'])) { + $methodAnnotations['ApiMethod'] = ['get']; + } + if (!isset($methodAnnotations['ApiWeigh'])) { + $methodAnnotations['ApiWeigh'] = [0]; + } + if (!isset($methodAnnotations['ApiSummary'])) { + $methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle']; + } + + if ($methodAnnotations) { + foreach ($classAnnotations as $name => $valueClass) { + if (count($valueClass) !== 1) { + continue; + } + + if ($name === 'ApiRoute') { + if (isset($methodAnnotations[$name])) { + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]]; + } else { + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()]; + } + } + + if ($name === 'ApiSector') { + $methodAnnotations[$name] = $valueClass; + } + } + } + if (!isset($methodAnnotations['ApiRoute'])) { + $urlArr = []; + $className = $class->getName(); + + list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className); + $prefixArr = explode('\\', $prefix); + $suffixArr = explode('\\', $suffix); + if ($prefixArr[0] == \think\Config::get('app_namespace')) { + $prefixArr[0] = ''; + } + $urlArr = array_merge($urlArr, $prefixArr); + $urlArr[] = implode('.', array_map(function ($item) { + return \think\Loader::parseName($item); + }, $suffixArr)); + $urlArr[] = $method->getName(); + + $methodAnnotations['ApiRoute'] = [implode('/', $urlArr)]; + } + if (!isset($methodAnnotations['ApiSector'])) { + $methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : $classAnnotations['ApiTitle']; + } + if (!isset($methodAnnotations['ApiParams'])) { + $params = self::parseCustomAnnotations($docblockMethod, 'param'); + foreach ($params as $k => $v) { + $arr = explode(' ', preg_replace("/[\s]+/", " ", $v)); + $methodAnnotations['ApiParams'][] = [ + 'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '', + 'nullable' => false, + 'type' => isset($arr[0]) ? $arr[0] : 'string', + 'description' => isset($arr[2]) ? $arr[2] : '' + ]; + } + } + $methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)]; + $methodAnnotations['ApiPermissionRight'] = [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)]; + return $methodAnnotations; + } + + /** + * Parse annotations + * + * @param string $docblock + * @param string $name + * @return array parsed annotations params + */ + private static function parseCustomAnnotations($docblock, $name = 'param') + { + $annotations = array(); + + $docblock = substr($docblock, 3, -2); + if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) { + foreach ($matches[1] as $k => $v) { + $annotations[] = $v; + } + } + return $annotations; + } + + /** + * Parse annotations + * + * @param string $docblock + * @return array parsed annotations params + */ + private static function parseAnnotations($docblock) + { + $annotations = array(); + + // Strip away the docblock header and footer to ease parsing of one line annotations + $docblock = substr($docblock, 3, -2); + if (preg_match_all('/@(?[A-Za-z_-]+)[\s\t]*\((?(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) { + $numMatches = count($matches[0]); + for ($i = 0; $i < $numMatches; ++$i) { + $name = $matches['name'][$i]; + $value = ''; + // annotations has arguments + if (isset($matches['args'][$i])) { + $argsParts = trim($matches['args'][$i]); + if ($name == 'ApiReturn') { + $value = $argsParts; + } elseif ($matches['args'][$i] != '') { + $argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts); + $value = self::parseArgs($argsParts); + if (is_string($value)) { + $value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts); + } + } + } + + $annotations[$name][] = $value; + } + } + if (stripos($docblock, '@ApiInternal') !== false) { + $annotations['ApiInternal'] = [true]; + } + if (!isset($annotations['ApiTitle'])) { + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr); + $title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : ''; + $annotations['ApiTitle'] = [$title]; + } + + return $annotations; + } + + /** + * Parse individual annotation arguments + * + * @param string $content arguments string + * @return array annotated arguments + */ + private static function parseArgs($content) + { + // Replace initial stars + $content = preg_replace('/^\s*\*/m', '', $content); + + $data = array(); + $len = strlen($content); + $i = 0; + $var = ''; + $val = ''; + $level = 1; + + $prevDelimiter = ''; + $nextDelimiter = ''; + $nextToken = ''; + $composing = false; + $type = 'plain'; + $delimiter = null; + $quoted = false; + $tokens = array('"', '"', '{', '}', ',', '='); + + while ($i <= $len) { + $prev_c = substr($content, $i - 1, 1); + $c = substr($content, $i++, 1); + + if ($c === '"' && $prev_c !== "\\") { + $delimiter = $c; + //open delimiter + if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) { + $prevDelimiter = $nextDelimiter = $delimiter; + $val = ''; + $composing = true; + $quoted = true; + } else { + // close delimiter + if ($c !== $nextDelimiter) { + throw new Exception(sprintf( + "Parse Error: enclosing error -> expected: [%s], given: [%s]", + $nextDelimiter, + $c + )); + } + + // validating syntax + if ($i < $len) { + if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) { + throw new Exception(sprintf( + "Parse Error: missing comma separator near: ...%s<--", + substr($content, ($i - 10), $i) + )); + } + } + + $prevDelimiter = $nextDelimiter = ''; + $composing = false; + $delimiter = null; + } + } elseif (!$composing && in_array($c, $tokens)) { + switch ($c) { + case '=': + $prevDelimiter = $nextDelimiter = ''; + $level = 2; + $composing = false; + $type = 'assoc'; + $quoted = false; + break; + case ',': + $level = 3; + + // If composing flag is true yet, + // it means that the string was not enclosed, so it is parsing error. + if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) { + throw new Exception(sprintf( + "Parse Error: enclosing error -> expected: [%s], given: [%s]", + $nextDelimiter, + $c + )); + } + + $prevDelimiter = $nextDelimiter = ''; + break; + case '{': + $subc = ''; + $subComposing = true; + + while ($i <= $len) { + $c = substr($content, $i++, 1); + + if (isset($delimiter) && $c === $delimiter) { + throw new Exception(sprintf( + "Parse Error: Composite variable is not enclosed correctly." + )); + } + + if ($c === '}') { + $subComposing = false; + break; + } + $subc .= $c; + } + + // if the string is composing yet means that the structure of var. never was enclosed with '}' + if ($subComposing) { + throw new Exception(sprintf( + "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", + $subc + )); + } + + $val = self::parseArgs($subc); + break; + } + } else { + if ($level == 1) { + $var .= $c; + } elseif ($level == 2) { + $val .= $c; + } + } + + if ($level === 3 || $i === $len) { + if ($type == 'plain' && $i === $len) { + $data = self::castValue($var); + } else { + $data[trim($var)] = self::castValue($val, !$quoted); + } + + $level = 1; + $var = $val = ''; + $composing = false; + $quoted = false; + } + } + + return $data; + } + + /** + * Try determinate the original type variable of a string + * + * @param string $val string containing possibles variables that can be cast to bool or int + * @param boolean $trim indicate if the value passed should be trimmed after to try cast + * @return mixed returns the value converted to original type if was possible + */ + private static function castValue($val, $trim = false) + { + if (is_array($val)) { + foreach ($val as $key => $value) { + $val[$key] = self::castValue($value); + } + } elseif (is_string($val)) { + if ($trim) { + $val = trim($val); + } + $val = stripslashes($val); + $tmp = strtolower($val); + + if ($tmp === 'false' || $tmp === 'true') { + $val = $tmp === 'true'; + } elseif (is_numeric($val)) { + return $val + 0; + } + + unset($tmp); + } + + return $val; + } +} diff --git a/application/admin/command/Api/template/index.html b/application/admin/command/Api/template/index.html new file mode 100755 index 0000000000000000000000000000000000000000..a0b5424f49a086c949304f0f153d562352a625ad --- /dev/null +++ b/application/admin/command/Api/template/index.html @@ -0,0 +1,581 @@ + + + + + + + + {$config.title} + + + + + + + + + + + + + + + + +
+ + +
+ {foreach name="docslist" id="docs"} +

{$key}

+
+ {foreach name="docs" id="api" } +
+
+

+ {$api.method|strtoupper} + {$api.title} {$api.route} +

+
+
+
+ + + + + +
+ +
+
+ {$api.summary} +
+
+
{$lang.Headers}
+
+ {if $api.headerslist} + + + + + + + + + + + {foreach name="api['headerslist']" id="header"} + + + + + + + {/foreach} + +
{$lang.Name}{$lang.Type}{$lang.Required}{$lang.Description}
{$header.name}{$header.type}{$header.required?'是':'否'}{$header.description}
+ {else /} + 无 + {/if} +
+
+
+
{$lang.Parameters}
+
+ {if $api.paramslist} + + + + + + + + + + + {foreach name="api['paramslist']" id="param"} + + + + + + + {/foreach} + +
{$lang.Name}{$lang.Type}{$lang.Required}{$lang.Description}
{$param.name}{$param.type}{:$param.required?'是':'否'}{$param.description}
+ {else /} + 无 + {/if} +
+
+
+
{$lang.Body}
+
+ {$api.body|default='无'} +
+
+
+ +
+
+
+ {if $api.headerslist} +
+
{$lang.Headers}
+
+
+ {foreach name="api['headerslist']" id="param"} +
+ + +
+ {/foreach} +
+
+
+ {/if} +
+
{$lang.Parameters}
+
+
+ {if $api.paramslist} + {foreach name="api['paramslist']" id="param"} +
+ + +
+ {/foreach} + {else /} +
+ 无 +
+ {/if} +
+ + +
+
+
+
+
+
{$lang.Response}
+
+
+
+

+                                                            

+                                                        
+
+
+
+
+
{$lang.ReturnParameters}
+
+ {if $api.returnparamslist} + + + + + + + + + + {foreach name="api['returnparamslist']" id="param"} + + + + + + {/foreach} + +
{$lang.Name}{$lang.Type}{$lang.Description}
{$param.name}{$param.type}{$param.description}
+ {else /} + 无 + {/if} +
+
+
+
+
+ +
+
+
+
{$api.return|default='无'}
+
+
+
+ +
+
+
+
+ {/foreach} + {/foreach} +
+ +
+ + + +
+ + + + + + + + + + diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php old mode 100644 new mode 100755 index 7f81d964b5b9694fd9d85913130b1dc1a18f816b..bdc2bd0e0a16af3f245c22b9a5fc054977016266 --- a/application/admin/command/Crud.php +++ b/application/admin/command/Crud.php @@ -10,13 +10,17 @@ use think\console\input\Option; use think\console\Output; use think\Db; use think\Exception; +use think\exception\ErrorException; use think\Lang; +use think\Loader; class Crud extends Command { - protected $stubList = []; + protected $internalKeywords = [ + 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor' + ]; /** * Selectpage搜索字段关联 */ @@ -44,6 +48,21 @@ class Crud extends Command */ protected $switchSuffix = ['switch']; + /** + * 富文本后缀 + */ + protected $editorSuffix = ['content']; + + /** + * 城市后缀 + */ + protected $citySuffix = ['city']; + + /** + * JSON后缀 + */ + protected $jsonSuffix = ['json']; + /** * Selectpage对应的后缀 */ @@ -58,12 +77,14 @@ class Crud extends Command * 以指定字符结尾的字段格式化函数 */ protected $fieldFormatterSuffix = [ - 'status' => 'status', + 'status' => ['type' => ['varchar', 'enum'], 'name' => 'status'], 'icon' => 'icon', 'flag' => 'flag', 'url' => 'url', 'image' => 'image', 'images' => 'images', + 'avatar' => 'image', + 'switch' => 'toggle', 'time' => ['type' => ['int', 'timestamp'], 'name' => 'datetime'] ]; @@ -80,62 +101,109 @@ class Crud extends Command /** * 保留字段 */ - protected $reservedField = ['createtime', 'updatetime']; + protected $reservedField = ['admin_id']; + + /** + * 排除字段 + */ + protected $ignoreFields = []; /** * 排序字段 */ protected $sortField = 'weigh'; + /** + * 筛选字段 + * @var string + */ + protected $headingFilterField = 'status'; + + /** + * 添加时间字段 + * @var string + */ + protected $createTimeField = 'createtime'; + + /** + * 更新时间字段 + * @var string + */ + protected $updateTimeField = 'updatetime'; + + /** + * 软删除时间字段 + * @var string + */ + protected $deleteTimeField = 'deletetime'; + /** * 编辑器的Class */ - protected $editorClass = 'summernote'; + protected $editorClass = 'editor'; + + /** + * langList的key最长字节数 + */ + protected $fieldMaxLen = 0; protected function configure() { $this - ->setName('crud') - ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) - ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) - ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) - ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) - ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) - ->addOption('relation', 'r', Option::VALUE_OPTIONAL, 'relation table name without prefix', null) - ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL, 'relation model name', null) - ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL, 'relation foreign key', null) - ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL, 'relation primary key', null) - ->addOption('mode', 'o', Option::VALUE_OPTIONAL, 'relation table mode,hasone or belongsto', 'belongsto') - ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) - ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) - ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) - ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) - ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) - ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) - ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) - ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) - ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) - ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) - ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) - ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) - ->setDescription('Build CRUD controller and model from table'); + ->setName('crud') + ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) + ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) + ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) + ->addOption('fields', 'i', Option::VALUE_OPTIONAL, 'model visible fields', null) + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override or force delete,without tips', null) + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) + ->addOption('relation', 'r', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table name without prefix', null) + ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation model name', null) + ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation foreign key', null) + ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation primary key', null) + ->addOption('relationfields', 's', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table fields', null) + ->addOption('relationmode', 'o', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table mode,hasone or belongsto', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) + ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) + ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) + ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) + ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) + ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) + ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) + ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) + ->addOption('citysuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate citypicker component with suffix', null) + ->addOption('jsonsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate fieldlist component with suffix', null) + ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) + ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) + ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) + ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) + ->addOption('headingfilterfield', null, Option::VALUE_OPTIONAL, 'heading filter field', null) + ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) + ->addOption('db', null, Option::VALUE_OPTIONAL, 'database config name', 'database') + ->setDescription('Build CRUD controller and model from table'); } protected function execute(Input $input, Output $output) { $adminPath = dirname(__DIR__) . DS; + //数据库 + $db = $input->getOption('db'); //表名 $table = $input->getOption('table') ?: ''; //自定义控制器 $controller = $input->getOption('controller'); //自定义模型 $model = $input->getOption('model'); + $model = $model ? $model : $controller; + //验证器类 + $validate = $model; + //自定义显示字段 + $fields = $input->getOption('fields'); //强制覆盖 $force = $input->getOption('force'); //是否为本地model,为0时表示为全局model将会把model放在app/common/model中 $local = $input->getOption('local'); - if (!$table) - { + if (!$table) { throw new Exception('table name can\'t empty'); } //是否生成菜单 @@ -143,13 +211,15 @@ class Crud extends Command //关联表 $relation = $input->getOption('relation'); //自定义关联表模型 - $relationModel = $input->getOption('relationmodel'); + $relationModels = $input->getOption('relationmodel'); //模式 - $mode = $input->getOption('mode'); + $relationMode = $mode = $input->getOption('relationmode'); //外键 $relationForeignKey = $input->getOption('relationforeignkey'); //主键 $relationPrimaryKey = $input->getOption('relationprimarykey'); + //关联表显示字段 + $relationFields = $input->getOption('relationfields'); //复选框后缀 $setcheckboxsuffix = $input->getOption('setcheckboxsuffix'); //单选框后缀 @@ -162,131 +232,217 @@ class Crud extends Command $intdatesuffix = $input->getOption('intdatesuffix'); //开关后缀 $switchsuffix = $input->getOption('switchsuffix'); + //城市后缀 + $citysuffix = $input->getOption('citysuffix'); + //JSON配置后缀 + $jsonsuffix = $input->getOption('jsonsuffix'); //selectpage后缀 $selectpagesuffix = $input->getOption('selectpagesuffix'); //selectpage多选后缀 $selectpagessuffix = $input->getOption('selectpagessuffix'); + //排除字段 + $ignoreFields = $input->getOption('ignorefields'); //排序字段 $sortfield = $input->getOption('sortfield'); + //顶部筛选过滤字段 + $headingfilterfield = $input->getOption('headingfilterfield'); //编辑器Class $editorclass = $input->getOption('editorclass'); - if ($setcheckboxsuffix) + if ($setcheckboxsuffix) { $this->setCheckboxSuffix = $setcheckboxsuffix; - if ($enumradiosuffix) + } + if ($enumradiosuffix) { $this->enumRadioSuffix = $enumradiosuffix; - if ($imagefield) + } + if ($imagefield) { $this->imageField = $imagefield; - if ($filefield) + } + if ($filefield) { $this->fileField = $filefield; - if ($intdatesuffix) + } + if ($intdatesuffix) { $this->intDateSuffix = $intdatesuffix; - if ($switchsuffix) + } + if ($switchsuffix) { $this->switchSuffix = $switchsuffix; - if ($selectpagesuffix) + } + if ($citysuffix) { + $this->citySuffix = $citysuffix; + } + if ($jsonsuffix) { + $this->jsonSuffix = $jsonsuffix; + } + if ($selectpagesuffix) { $this->selectpageSuffix = $selectpagesuffix; - if ($selectpagessuffix) + } + if ($selectpagessuffix) { $this->selectpagesSuffix = $selectpagessuffix; - if ($editorclass) + } + if ($ignoreFields) { + $this->ignoreFields = $ignoreFields; + } + if ($editorclass) { $this->editorClass = $editorclass; - if ($sortfield) + } + if ($sortfield) { $this->sortField = $sortfield; - - //如果有启用关联模式 - if ($relation && !in_array($mode, ['hasone', 'belongsto'])) - { - throw new Exception("relation table only work in hasone or belongsto mode"); + } + if ($headingfilterfield) { + $this->headingFilterField = $headingfilterfield; } - $dbname = Config::get('database.database'); - $prefix = Config::get('database.prefix'); + $this->reservedField = array_merge($this->reservedField, [$this->createTimeField, $this->updateTimeField, $this->deleteTimeField]); + + $dbconnect = Db::connect($db); + $dbname = Config::get($db . '.database'); + $prefix = Config::get($db . '.prefix'); + + //模块 + $moduleName = 'admin'; + $modelModuleName = $local ? $moduleName : 'common'; + $validateModuleName = $local ? $moduleName : 'common'; //检查主表 - $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; - $modelTableName = $tableName = $table; + $modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; $modelTableType = 'table'; - $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); - if (!$tableInfo) - { - $tableName = $prefix . $table; + $modelTableTypeName = $modelTableName = $modelName; + $modelTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true); + if (!$modelTableInfo) { $modelTableType = 'name'; - $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); - if (!$tableInfo) - { + $modelTableName = $prefix . $modelName; + $modelTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true); + if (!$modelTableInfo) { throw new Exception("table not found"); } } - $tableInfo = $tableInfo[0]; + $modelTableInfo = $modelTableInfo[0]; - $relationModelTableName = $relationTableName = $relation; - $relationModelTableType = 'table'; + $relations = []; //检查关联表 - if ($relation) - { - $relation = stripos($relation, $prefix) === 0 ? substr($relation, strlen($prefix)) : $relation; - $relationModelTableName = $relationTableName = $relation; - $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); - if (!$relationTableInfo) - { - $relationTableName = $prefix . $relation; - $relationModelTableType = 'name'; - $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); - if (!$relationTableInfo) - { - throw new Exception("relation table not found"); + if ($relation) { + $relationArr = $relation; + $relations = []; + + foreach ($relationArr as $index => $relationTable) { + $relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable; + $relationTableType = 'table'; + $relationTableTypeName = $relationTableName = $relationName; + $relationTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true); + if (!$relationTableInfo) { + $relationTableType = 'name'; + $relationTableName = $prefix . $relationName; + $relationTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true); + if (!$relationTableInfo) { + throw new Exception("relation table not found"); + } } + $relationTableInfo = $relationTableInfo[0]; + $relationModel = isset($relationModels[$index]) ? $relationModels[$index] : ''; + + list($relationNamespace, $relationName, $relationFile) = $this->getModelData($modelModuleName, $relationModel, $relationName); + + $relations[] = [ + //关联表基础名 + 'relationName' => $relationName, + //关联表类命名空间 + 'relationNamespace' => $relationNamespace, + //关联模型名 + 'relationModel' => $relationModel, + //关联文件 + 'relationFile' => $relationFile, + //关联表名称 + 'relationTableName' => $relationTableName, + //关联表信息 + 'relationTableInfo' => $relationTableInfo, + //关联模型表类型(name或table) + 'relationTableType' => $relationTableType, + //关联模型表类型名称 + 'relationTableTypeName' => $relationTableTypeName, + //关联模式 + 'relationFields' => isset($relationFields[$index]) ? explode(',', $relationFields[$index]) : [], + //关联模式 + 'relationMode' => isset($relationMode[$index]) ? $relationMode[$index] : 'belongsto', + //关联表外键 + 'relationForeignKey' => isset($relationForeignKey[$index]) ? $relationForeignKey[$index] : Loader::parseName($relationName) . '_id', + //关联表主键 + 'relationPrimaryKey' => isset($relationPrimaryKey[$index]) ? $relationPrimaryKey[$index] : '', + ]; } } //根据表名匹配对应的Fontawesome图标 $iconPath = ROOT_PATH . str_replace('/', DS, '/public/assets/libs/font-awesome/less/variables.less'); - $iconName = is_file($iconPath) && stripos(file_get_contents($iconPath), '@fa-var-' . $table . ':') ? $table : 'fa fa-circle-o'; - - //控制器默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入controller,格式为目录层级 - $controller = str_replace('_', '', $controller); - $controllerArr = !$controller ? explode('_', strtolower($table)) : explode('/', strtolower($controller)); - $controllerUrl = implode('/', $controllerArr); - $controllerName = ucfirst(array_pop($controllerArr)); - $controllerDir = implode(DS, $controllerArr); - $controllerFile = ($controllerDir ? $controllerDir . DS : '') . $controllerName . '.php'; - $viewDir = $adminPath . 'view' . DS . $controllerUrl . DS; + $iconName = is_file($iconPath) && stripos(file_get_contents($iconPath), '@fa-var-' . $table . ':') ? 'fa fa-' . $table : 'fa fa-circle-o'; + + //控制器 + list($controllerNamespace, $controllerName, $controllerFile, $controllerArr) = $this->getControllerData($moduleName, $controller, $table); + //模型 + list($modelNamespace, $modelName, $modelFile, $modelArr) = $this->getModelData($modelModuleName, $model, $table); + //验证器 + list($validateNamespace, $validateName, $validateFile, $validateArr) = $this->getValidateData($validateModuleName, $validate, $table); + + //处理基础文件名,取消所有下划线并转换为小写 + $baseNameArr = $controllerArr; + $baseFileName = Loader::parseName(array_pop($baseNameArr), 0); + array_push($baseNameArr, $baseFileName); + $controllerBaseName = strtolower(implode(DS, $baseNameArr)); + $controllerUrl = strtolower(implode('/', $baseNameArr)); + + //视图文件 + $viewArr = $controllerArr; + $lastValue = array_pop($viewArr); + $viewArr[] = Loader::parseName($lastValue, 0); + array_unshift($viewArr, 'view'); + $viewDir = $adminPath . strtolower(implode(DS, $viewArr)) . DS; //最终将生成的文件路径 - $controllerFile = $adminPath . 'controller' . DS . $controllerFile; - $javascriptFile = ROOT_PATH . 'public' . DS . 'assets' . DS . 'js' . DS . 'backend' . DS . $controllerUrl . '.js'; + $javascriptFile = ROOT_PATH . 'public' . DS . 'assets' . DS . 'js' . DS . 'backend' . DS . $controllerBaseName . '.js'; $addFile = $viewDir . 'add.html'; $editFile = $viewDir . 'edit.html'; $indexFile = $viewDir . 'index.html'; - $langFile = $adminPath . 'lang' . DS . Lang::detect() . DS . $controllerUrl . '.php'; - - //模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入model,不支持目录层级 - $modelName = $this->getModelName($model, $table); - $modelFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $modelName . '.php'; - - $validateFile = $adminPath . 'validate' . DS . $modelName . '.php'; - - //关联模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入relationmodel,不支持目录层级 - $relationModelName = $this->getModelName($relationModel, $relation); - $relationModelFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $relationModelName . '.php'; + $recyclebinFile = $viewDir . 'recyclebin.html'; + $langFile = $adminPath . 'lang' . DS . Lang::detect() . DS . $controllerBaseName . '.php'; //是否为删除模式 $delete = $input->getOption('delete'); - if ($delete) - { - $readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $langFile, $javascriptFile]; - foreach ($readyFiles as $k => $v) - { + if ($delete) { + $readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $recyclebinFile, $langFile, $javascriptFile]; + foreach ($readyFiles as $k => $v) { $output->warning($v); } - $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); - $line = fgets(STDIN); - if (trim($line) != 'yes') - { - throw new Exception("Operation is aborted!"); + if (!$force) { + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } } - foreach ($readyFiles as $k => $v) - { - if (file_exists($v)) + foreach ($readyFiles as $k => $v) { + if (file_exists($v)) { unlink($v); + } + //删除空文件夹 + switch ($v) { + case $modelFile: + $this->removeEmptyBaseDir($v, $modelArr); + break; + case $validateFile: + $this->removeEmptyBaseDir($v, $validateArr); + break; + case $addFile: + case $editFile: + case $indexFile: + case $recyclebinFile: + $this->removeEmptyBaseDir($v, $viewArr); + break; + default: + $this->removeEmptyBaseDir($v, $controllerArr); + } + } + + //继续删除菜单 + if ($menu) { + exec("php think menu -c {$controllerUrl} -d 1 -f 1"); } $output->info("Delete Successed"); @@ -294,20 +450,17 @@ class Crud extends Command } //非覆盖模式时如果存在控制器文件则报错 - if (is_file($controllerFile) && !$force) - { + if (is_file($controllerFile) && !$force) { throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true "); } //非覆盖模式时如果存在模型文件则报错 - if (is_file($modelFile) && !$force) - { + if (is_file($modelFile) && !$force) { throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true "); } //非覆盖模式时如果存在验证文件则报错 - if (is_file($validateFile) && !$force) - { + if (is_file($validateFile) && !$force) { throw new Exception("validate already exists!\nIf you need to rebuild again, use the parameter --force=true "); } @@ -315,26 +468,43 @@ class Crud extends Command //从数据库中获取表字段信息 $sql = "SELECT * FROM `information_schema`.`columns` " - . "WHERE TABLE_SCHEMA = ? AND table_name = ? " - . "ORDER BY ORDINAL_POSITION"; - $columnList = Db::query($sql, [$dbname, $tableName]); - $relationColumnList = []; - if ($relation) - { - $relationColumnList = Db::query($sql, [$dbname, $relationTableName]); - } - + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $columnList = $dbconnect->query($sql, [$dbname, $modelTableName]); $fieldArr = []; - foreach ($columnList as $k => $v) - { + foreach ($columnList as $k => $v) { $fieldArr[] = $v['COLUMN_NAME']; } - $relationFieldArr = []; - foreach ($relationColumnList as $k => $v) - { - $relationFieldArr[] = $v['COLUMN_NAME']; + // 加载关联表的列 + foreach ($relations as $index => &$relation) { + $relationColumnList = $dbconnect->query($sql, [$dbname, $relation['relationTableName']]); + + $relationFieldList = []; + foreach ($relationColumnList as $k => $v) { + $relationFieldList[] = $v['COLUMN_NAME']; + } + if (!$relation['relationPrimaryKey']) { + foreach ($relationColumnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { + $relation['relationPrimaryKey'] = $v['COLUMN_NAME']; + break; + } + } + } + // 如果主键为空 + if (!$relation['relationPrimaryKey']) { + throw new Exception('Relation Primary key not found!'); + } + // 如果主键不在表字段中 + if (!in_array($relation['relationPrimaryKey'], $relationFieldList)) { + throw new Exception('Relation Primary key not found in table!'); + } + $relation['relationColumnList'] = $relationColumnList; + $relation['relationFieldList'] = $relationFieldList; } + unset($relation); $addList = []; $editList = []; @@ -342,100 +512,82 @@ class Crud extends Command $langList = []; $field = 'id'; $order = 'id'; - $priDefined = FALSE; + $priDefined = false; $priKey = ''; - $relationPriKey = ''; - foreach ($columnList as $k => $v) - { - if ($v['COLUMN_KEY'] == 'PRI') - { + $relationPrimaryKey = ''; + foreach ($columnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { $priKey = $v['COLUMN_NAME']; break; } } - if (!$priKey) - { + if (!$priKey) { throw new Exception('Primary key not found!'); } - if ($relation) - { - foreach ($relationColumnList as $k => $v) - { - if ($v['COLUMN_KEY'] == 'PRI') - { - $relationPriKey = $v['COLUMN_NAME']; - break; - } - } - if (!$relationPriKey) - { - throw new Exception('Relation Primary key not found!'); - } - } - $order = $priKey; + $order = $priKey; //如果是关联模型 - if ($relation) - { - if ($mode == 'hasone') - { - $relationForeignKey = $relationForeignKey ? $relationForeignKey : $table . "_id"; - $relationPrimaryKey = $relationPrimaryKey ? $relationPrimaryKey : $priKey; - if (!in_array($relationForeignKey, $relationFieldArr)) - { - throw new Exception('relation table must be contain field:' . $relationForeignKey); + foreach ($relations as $index => &$relation) { + if ($relation['relationMode'] == 'hasone') { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; + + if (!in_array($relationForeignKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']'); } - if (!in_array($relationPrimaryKey, $fieldArr)) - { - throw new Exception('table must be contain field:' . $relationPrimaryKey); + if (!in_array($relationPrimaryKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']'); } - } - else - { - $relationForeignKey = $relationForeignKey ? $relationForeignKey : $relation . "_id"; - $relationPrimaryKey = $relationPrimaryKey ? $relationPrimaryKey : $relationPriKey; - if (!in_array($relationForeignKey, $fieldArr)) - { - throw new Exception('table must be contain field:' . $relationForeignKey); + } else { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : Loader::parseName($relation['relationName']) . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $relation['relationPriKey']; + if (!in_array($relationForeignKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationForeignKey . ']'); } - if (!in_array($relationPrimaryKey, $relationFieldArr)) - { - throw new Exception('relation table must be contain field:' . $relationPrimaryKey); + if (!in_array($relationPrimaryKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationPrimaryKey . ']'); } } + $relation['relationForeignKey'] = $relationForeignKey; + $relation['relationPrimaryKey'] = $relationPrimaryKey; + $relation['relationClassName'] = $modelNamespace != $relation['relationNamespace'] ? $relation['relationNamespace'] . '\\' . $relation['relationName'] : $relation['relationName']; } + unset($relation); - try - { + try { Form::setEscapeHtml(false); $setAttrArr = []; $getAttrArr = []; $getEnumArr = []; $appendAttrList = []; $controllerAssignList = []; + $headingHtml = '{:build_heading()}'; + $recyclebinHtml = ''; //循环所有字段,开始构造视图的HTML和JS信息 - foreach ($columnList as $k => $v) - { + foreach ($columnList as $k => $v) { $field = $v['COLUMN_NAME']; $itemArr = []; // 这里构建Enum和Set类型的列表数据 - if (in_array($v['DATA_TYPE'], ['enum', 'set'])) - { - $itemArr = substr($v['COLUMN_TYPE'], strlen($v['DATA_TYPE']) + 1, -1); - $itemArr = explode(',', str_replace("'", '', $itemArr)); + if (in_array($v['DATA_TYPE'], ['enum', 'set', 'tinyint'])) { + if ($v['DATA_TYPE'] !== 'tinyint') { + $itemArr = substr($v['COLUMN_TYPE'], strlen($v['DATA_TYPE']) + 1, -1); + $itemArr = explode(',', str_replace("'", '', $itemArr)); + } $itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']); + //如果类型为tinyint且有使用备注数据 + if ($itemArr && $v['DATA_TYPE'] == 'tinyint') { + $v['DATA_TYPE'] = 'enum'; + } } // 语言列表 - if ($v['COLUMN_COMMENT'] != '') - { + if ($v['COLUMN_COMMENT'] != '') { $langList[] = $this->getLangItem($field, $v['COLUMN_COMMENT']); } $inputType = ''; - //createtime和updatetime是保留字段不能修改和添加 - if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField)) - { + //保留字段不能修改和添加 + if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) { $inputType = $this->getFieldType($v); // 如果是number类型时增加一个步长 @@ -445,19 +597,16 @@ class Crud extends Command $cssClassArr = ['form-control']; $fieldName = "row[{$field}]"; $defaultValue = $v['COLUMN_DEFAULT']; - $editValue = "{\$row.{$field}}"; + $editValue = "{\$row.{$field}|htmlentities}"; // 如果默认值非null,则是一个必选项 - if (!is_null($v['COLUMN_DEFAULT'])) - { + if ($v['IS_NULLABLE'] == 'NO') { $attrArr['data-rule'] = 'required'; } - if ($inputType == 'select') - { + if ($inputType == 'select') { $cssClassArr[] = 'selectpicker'; $attrArr['class'] = implode(' ', $cssClassArr); - if ($v['DATA_TYPE'] == 'set') - { + if ($v['DATA_TYPE'] == 'set') { $attrArr['multiple'] = ''; $fieldName .= "[]"; } @@ -465,37 +614,38 @@ class Crud extends Command $this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); - $itemArr = $this->getLangArray($itemArr, FALSE); + $itemArr = $this->getLangArray($itemArr, false); //添加一个获取器 $this->getAttr($getAttrArr, $field, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); + if ($v['DATA_TYPE'] == 'set') { + $this->setAttr($setAttrArr, $field, $inputType); + } $this->appendAttr($appendAttrList, $field); $formAddElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); $formEditElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); - } - else if ($inputType == 'datetime') - { + } elseif ($inputType == 'datetime') { $cssClassArr[] = 'datetimepicker'; $attrArr['class'] = implode(' ', $cssClassArr); $format = "YYYY-MM-DD HH:mm:ss"; $phpFormat = "Y-m-d H:i:s"; $fieldFunc = ''; - switch ($v['DATA_TYPE']) - { - case 'year'; + switch ($v['DATA_TYPE']) { + case 'year': $format = "YYYY"; $phpFormat = 'Y'; break; - case 'date'; + case 'date': $format = "YYYY-MM-DD"; $phpFormat = 'Y-m-d'; break; - case 'time'; + case 'time': $format = "HH:mm:ss"; $phpFormat = 'H:i:s'; break; - case 'timestamp'; + case 'timestamp': $fieldFunc = 'datetime'; - case 'datetime'; + // no break + case 'datetime': $format = "YYYY-MM-DD HH:mm:ss"; $phpFormat = 'Y-m-d H:i:s'; break; @@ -509,59 +659,61 @@ class Crud extends Command $defaultDateTime = "{:date('{$phpFormat}')}"; $attrArr['data-date-format'] = $format; $attrArr['data-use-current'] = "true"; - $fieldFunc = $fieldFunc ? "|{$fieldFunc}" : ""; $formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr); - $formEditElement = Form::text($fieldName, "{\$row.{$field}{$fieldFunc}}", $attrArr); - } - else if ($inputType == 'checkbox' || $inputType == 'radio') - { + $formEditElement = Form::text($fieldName, ($fieldFunc ? "{:\$row.{$field}?{$fieldFunc}(\$row.{$field}):''}" : "{\$row.{$field}{$fieldFunc}}"), $attrArr); + } elseif ($inputType == 'checkbox' || $inputType == 'radio') { unset($attrArr['data-rule']); $fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName; $attrArr['name'] = "row[{$fieldName}]"; $this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $inputType); - $itemArr = $this->getLangArray($itemArr, FALSE); + $itemArr = $this->getLangArray($itemArr, false); //添加一个获取器 $this->getAttr($getAttrArr, $field, $inputType); + if ($inputType == 'checkbox') { + $this->setAttr($setAttrArr, $field, $inputType); + } $this->appendAttr($appendAttrList, $field); $defaultValue = $inputType == 'radio' && !$defaultValue ? key($itemArr) : $defaultValue; $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); - } - else if ($inputType == 'textarea') - { - $cssClassArr[] = substr($field, -7) == 'content' ? $this->editorClass : ''; + } elseif ($inputType == 'textarea' && !$this->isMatchSuffix($field, $this->selectpagesSuffix) && !$this->isMatchSuffix($field, $this->imageField)) { + $cssClassArr[] = $this->isMatchSuffix($field, $this->editorSuffix) ? $this->editorClass : ''; $attrArr['class'] = implode(' ', $cssClassArr); $attrArr['rows'] = 5; $formAddElement = Form::textarea($fieldName, $defaultValue, $attrArr); $formEditElement = Form::textarea($fieldName, $editValue, $attrArr); - } - else if ($inputType == 'switch') - { + } elseif ($inputType == 'switch') { unset($attrArr['data-rule']); - if ($defaultValue === '1' || $defaultValue === 'Y') - { + if ($defaultValue === '1' || $defaultValue === 'Y') { $yes = $defaultValue; $no = $defaultValue === '1' ? '0' : 'N'; - } - else - { + } else { $no = $defaultValue; $yes = $defaultValue === '0' ? '1' : 'Y'; } - $formAddElement = $formEditElement = Form::hidden($fieldName, $no, array_merge(['checked' => ''], $attrArr)); - $attrArr['id'] = $fieldName . "-switch"; - $formAddElement .= sprintf(Form::label("{$attrArr['id']}", "%s abcdefg"), Form::checkbox($fieldName, $yes, $defaultValue === $yes, $attrArr)); - $formEditElement .= sprintf(Form::label("{$attrArr['id']}", "%s abcdefg"), Form::checkbox($fieldName, $yes, 0, $attrArr)); - $formEditElement = str_replace('type="checkbox"', 'type="checkbox" {in name="' . "\$row.{$field}" . '" value="' . $yes . '"}checked{/in}', $formEditElement); - } - else - { + if (!$itemArr) { + $itemArr = [$yes => 'Yes', $no => 'No']; + } + $stateNoClass = 'fa-flip-horizontal text-gray'; + $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => $defaultValue, 'fieldSwitchClass' => $defaultValue == $no ? $stateNoClass : '']); + $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => "{\$row.{$field}}", 'fieldSwitchClass' => "{eq name=\"\$row.{$field}\" value=\"{$no}\"}fa-flip-horizontal text-gray{/eq}"]); + } elseif ($inputType == 'citypicker') { + $attrArr['class'] = implode(' ', $cssClassArr); + $attrArr['data-toggle'] = "city-picker"; + $formAddElement = sprintf("
%s
", Form::input('text', $fieldName, $defaultValue, $attrArr)); + $formEditElement = sprintf("
%s
", Form::input('text', $fieldName, $editValue, $attrArr)); + } elseif ($inputType == 'fieldlist') { + $itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']); + $itemKey = isset($itemArr['key']) ? ucfirst($itemArr['key']) : 'Key'; + $itemValue = isset($itemArr['value']) ? ucfirst($itemArr['value']) : 'Value'; + $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'itemKey' => $itemKey, 'itemValue' => $itemValue, 'fieldValue' => $defaultValue]); + $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'itemKey' => $itemKey, 'itemValue' => $itemValue, 'fieldValue' => $editValue]); + } else { $search = $replace = ''; //特殊字段为关联搜索 - if ($this->isMatchSuffix($field, $this->selectpageSuffix)) - { + if ($this->isMatchSuffix($field, $this->selectpageSuffix)) { $inputType = 'text'; $defaultValue = ''; $attrArr['data-rule'] = 'required'; @@ -569,21 +721,21 @@ class Crud extends Command $selectpageController = str_replace('_', '/', substr($field, 0, strripos($field, '_'))); $attrArr['data-source'] = $selectpageController . "/index"; //如果是类型表需要特殊处理下 - if ($selectpageController == 'category') - { + if ($selectpageController == 'category') { $attrArr['data-source'] = 'category/selectpage'; $attrArr['data-params'] = '##replacetext##'; $search = '"##replacetext##"'; $replace = '\'{"custom[type]":"' . $table . '"}\''; + } elseif ($selectpageController == 'admin') { + $attrArr['data-source'] = 'auth/admin/selectpage'; + } elseif ($selectpageController == 'user') { + $attrArr['data-source'] = 'user/user/index'; } - if ($this->isMatchSuffix($field, $this->selectpagesSuffix)) - { + if ($this->isMatchSuffix($field, $this->selectpagesSuffix)) { $attrArr['data-multiple'] = 'true'; } - foreach ($this->fieldSelectpageMap as $m => $n) - { - if (in_array($field, $n)) - { + foreach ($this->fieldSelectpageMap as $m => $n) { + if (in_array($field, $n)) { $attrArr['data-field'] = $m; break; } @@ -593,31 +745,26 @@ class Crud extends Command $step = array_intersect($cssClassArr, ['selectpage']) ? 0 : $step; $attrArr['class'] = implode(' ', $cssClassArr); $isUpload = false; - if ($this->isMatchSuffix($field, array_merge($this->imageField, $this->fileField))) - { + if ($this->isMatchSuffix($field, array_merge($this->imageField, $this->fileField))) { $isUpload = true; } //如果是步长则加上步长 - if ($step) - { + if ($step) { $attrArr['step'] = $step; } //如果是图片加上个size - if ($isUpload) - { + if ($isUpload) { $attrArr['size'] = 50; } $formAddElement = Form::input($inputType, $fieldName, $defaultValue, $attrArr); $formEditElement = Form::input($inputType, $fieldName, $editValue, $attrArr); - if ($search && $replace) - { + if ($search && $replace) { $formAddElement = str_replace($search, $replace, $formAddElement); $formEditElement = str_replace($search, $replace, $formEditElement); } //如果是图片或文件 - if ($isUpload) - { + if ($isUpload) { $formAddElement = $this->getImageUpload($field, $formAddElement); $formEditElement = $this->getImageUpload($field, $formEditElement); } @@ -628,160 +775,199 @@ class Crud extends Command } //过滤text类型字段 - if ($v['DATA_TYPE'] != 'text') - { + if ($v['DATA_TYPE'] != 'text' && $inputType != 'fieldlist') { //主键 - if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) - { - $priDefined = TRUE; + if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) { + $priDefined = true; $javascriptList[] = "{checkbox: true}"; } - //构造JS列信息 - $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : ''); - + if ($this->deleteTimeField == $field) { + $recyclebinHtml = $this->getReplacedStub('html/recyclebin-html', ['controllerUrl' => $controllerUrl]); + continue; + } + if (!$fields || in_array($field, explode(',', $fields))) { + //构造JS列信息 + $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr); + } + if ($this->headingFilterField && $this->headingFilterField == $field && $itemArr) { + $headingHtml = $this->getReplacedStub('html/heading-html', ['field' => $field, 'fieldName' => Loader::parseName($field, 1, false)]); + } //排序方式,如果有指定排序字段,否则按主键排序 $order = $field == $this->sortField ? $this->sortField : $order; } } - $relationPriKey = 'id'; - $relationFieldArr = []; - foreach ($relationColumnList as $k => $v) - { - $relationField = $v['COLUMN_NAME']; - $relationFieldArr[] = $field; + //循环关联表,追加语言包和JS列 + foreach ($relations as $index => $relation) { + foreach ($relation['relationColumnList'] as $k => $v) { + // 不显示的字段直接过滤掉 + if ($relation['relationFields'] && !in_array($v['COLUMN_NAME'], $relation['relationFields'])) { + continue; + } - $relationField = strtolower($relationModelName) . "." . $relationField; - // 语言列表 - if ($v['COLUMN_COMMENT'] != '') - { - $langList[] = $this->getLangItem($relationField, $v['COLUMN_COMMENT']); - } + $relationField = strtolower($relation['relationName']) . "." . $v['COLUMN_NAME']; + // 语言列表 + if ($v['COLUMN_COMMENT'] != '') { + $langList[] = $this->getLangItem($relationField, $v['COLUMN_COMMENT']); + } - //过滤text类型字段 - if ($v['DATA_TYPE'] != 'text') - { - //构造JS列信息 - $javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE']); + //过滤text类型字段 + if ($v['DATA_TYPE'] != 'text') { + //构造JS列信息 + $javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE']); + } } } //JS最后一列加上操作列 - $javascriptList[] = str_repeat(" ", 24) . "{field: 'operate', title: __('Operate'), events: Table.api.events.operate, formatter: Table.api.formatter.operate}"; + $javascriptList[] = str_repeat(" ", 24) . "{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}"; $addList = implode("\n", array_filter($addList)); $editList = implode("\n", array_filter($editList)); $javascriptList = implode(",\n", array_filter($javascriptList)); $langList = implode(",\n", array_filter($langList)); + //数组等号对齐 + $langList = array_filter(explode(",\n", $langList . ",\n")); + foreach ($langList as &$line) { + if (preg_match("/^\s+'([^']+)'\s*=>\s*'([^']+)'\s*/is", $line, $matches)) { + $line = " '{$matches[1]}'" . str_pad('=>', ($this->fieldMaxLen - strlen($matches[1]) + 3), ' ', STR_PAD_LEFT) . " '{$matches[2]}'"; + } + } + unset($line); + $langList = implode(",\n", array_filter($langList)); //表注释 - $tableComment = $tableInfo['Comment']; + $tableComment = $modelTableInfo['Comment']; $tableComment = mb_substr($tableComment, -1) == '表' ? mb_substr($tableComment, 0, -1) . '管理' : $tableComment; - $appNamespace = Config::get('app_namespace'); - $moduleName = 'admin'; - $controllerNamespace = "{$appNamespace}\\{$moduleName}\\controller" . ($controllerDir ? "\\" : "") . str_replace('/', "\\", $controllerDir); - $modelNamespace = "{$appNamespace}\\" . ($local ? $moduleName : "common") . "\\model"; - $validateNamespace = "{$appNamespace}\\" . $moduleName . "\\validate"; - $validateName = $modelName; - $modelInit = ''; - if ($priKey != $order) - { + if ($priKey != $order) { $modelInit = $this->getReplacedStub('mixins' . DS . 'modelinit', ['order' => $order]); } $data = [ + 'modelConnection' => $db == 'database' ? '' : "protected \$connection = '{$db}';", 'controllerNamespace' => $controllerNamespace, 'modelNamespace' => $modelNamespace, 'validateNamespace' => $validateNamespace, 'controllerUrl' => $controllerUrl, - 'controllerDir' => $controllerDir, 'controllerName' => $controllerName, 'controllerAssignList' => implode("\n", $controllerAssignList), 'modelName' => $modelName, + 'modelTableName' => $modelTableName, + 'modelTableType' => $modelTableType, + 'modelTableTypeName' => $modelTableTypeName, 'validateName' => $validateName, 'tableComment' => $tableComment, 'iconName' => $iconName, 'pk' => $priKey, 'order' => $order, 'table' => $table, - 'tableName' => $tableName, + 'tableName' => $modelTableName, 'addList' => $addList, 'editList' => $editList, 'javascriptList' => $javascriptList, 'langList' => $langList, - 'modelAutoWriteTimestamp' => in_array('createtime', $fieldArr) || in_array('updatetime', $fieldArr) ? "'int'" : 'false', - 'createTime' => in_array('createtime', $fieldArr) ? "'createtime'" : 'false', - 'updateTime' => in_array('updatetime', $fieldArr) ? "'updatetime'" : 'false', - 'modelTableName' => $modelTableName, - 'modelTableType' => $modelTableType, - 'relationModelTableName' => $relationModelTableName, - 'relationModelTableType' => $relationModelTableType, - 'relationModelName' => $relationModelName, - 'relationWith' => '', - 'relationMethod' => '', - 'relationModel' => '', - 'relationForeignKey' => '', - 'relationPrimaryKey' => '', - 'relationSearch' => $relation ? 'true' : 'false', + 'sofeDeleteClassPath' => in_array($this->deleteTimeField, $fieldArr) ? "use traits\model\SoftDelete;" : '', + 'softDelete' => in_array($this->deleteTimeField, $fieldArr) ? "use SoftDelete;" : '', + 'modelAutoWriteTimestamp' => in_array($this->createTimeField, $fieldArr) || in_array($this->updateTimeField, $fieldArr) ? "'int'" : 'false', + 'createTime' => in_array($this->createTimeField, $fieldArr) ? "'{$this->createTimeField}'" : 'false', + 'updateTime' => in_array($this->updateTimeField, $fieldArr) ? "'{$this->updateTimeField}'" : 'false', + 'deleteTime' => in_array($this->deleteTimeField, $fieldArr) ? "'{$this->deleteTimeField}'" : 'false', + 'relationSearch' => $relations ? 'true' : 'false', + 'relationWithList' => '', + 'relationMethodList' => '', 'controllerIndex' => '', + 'recyclebinJs' => '', + 'headingHtml' => $headingHtml, + 'recyclebinHtml' => $recyclebinHtml, + 'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '', 'appendAttrList' => implode(",\n", $appendAttrList), 'getEnumList' => implode("\n\n", $getEnumArr), 'getAttrList' => implode("\n\n", $getAttrArr), 'setAttrList' => implode("\n\n", $setAttrArr), 'modelInit' => $modelInit, - 'modelRelationMethod' => '', ]; //如果使用关联模型 - if ($relation) - { - //需要构造关联的方法 - $data['relationMethod'] = strtolower($relationModelName); - //预载入的方法 - $data['relationWith'] = "->with('{$data['relationMethod']}')"; + if ($relations) { + $relationWithList = $relationMethodList = $relationVisibleFieldList = []; + foreach ($relations as $index => $relation) { + //需要构造关联的方法 + $relation['relationMethod'] = strtolower($relation['relationName']); + + //关联的模式 + $relation['relationMode'] = $relation['relationMode'] == 'hasone' ? 'hasOne' : 'belongsTo'; + + //关联字段 + $relation['relationPrimaryKey'] = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; + + //预载入的方法 + $relationWithList[] = $relation['relationMethod']; + + unset($relation['relationColumnList'], $relation['relationFieldList'], $relation['relationTableInfo']); + + //构造关联模型的方法 + $relationMethodList[] = $this->getReplacedStub('mixins' . DS . 'modelrelationmethod', $relation); + + //如果设置了显示主表字段,则必须显式将关联表字段显示 + if ($fields) { + $relationVisibleFieldList[] = "\$row->visible(['{$relation['relationMethod']}']);"; + } + + //显示的字段 + if ($relation['relationFields']) { + $relationVisibleFieldList[] = "\$row->getRelation('" . $relation['relationMethod'] . "')->visible(['" . implode("','", $relation['relationFields']) . "']);"; + } + } + + $data['relationWithList'] = "->with(['" . implode("','", $relationWithList) . "'])"; + $data['relationMethodList'] = implode("\n\n", $relationMethodList); + $data['relationVisibleFieldList'] = implode("\n\t\t\t\t", $relationVisibleFieldList); + + //需要重写index方法 + $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); + } elseif ($fields) { + $data = array_merge($data, ['relationWithList' => '', 'relationMethodList' => '', 'relationVisibleFieldList' => '']); //需要重写index方法 $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); - //关联的模式 - $data['relationMode'] = $mode == 'hasone' ? 'hasOne' : 'belongsTo'; - //关联字段 - $data['relationForeignKey'] = $relationForeignKey; - $data['relationPrimaryKey'] = $relationPrimaryKey ? $relationPrimaryKey : $priKey; - //构造关联模型的方法 - $data['modelRelationMethod'] = $this->getReplacedStub('mixins' . DS . 'modelrelationmethod', $data); } // 生成控制器文件 - $result = $this->writeToFile('controller', $data, $controllerFile); + $this->writeToFile('controller', $data, $controllerFile); // 生成模型文件 - $result = $this->writeToFile('model', $data, $modelFile); - if ($relation && !is_file($relationModelFile)) - { - // 生成关联模型文件 - $result = $this->writeToFile('relationmodel', $data, $relationModelFile); + $this->writeToFile('model', $data, $modelFile); + + if ($relations) { + foreach ($relations as $i => $relation) { + $relation['modelNamespace'] = $data['modelNamespace']; + if (!is_file($relation['relationFile'])) { + // 生成关联模型文件 + $this->writeToFile('relationmodel', $relation, $relation['relationFile']); + } + } } // 生成验证文件 - $result = $this->writeToFile('validate', $data, $validateFile); + $this->writeToFile('validate', $data, $validateFile); // 生成视图文件 - $result = $this->writeToFile('add', $data, $addFile); - $result = $this->writeToFile('edit', $data, $editFile); - $result = $this->writeToFile('index', $data, $indexFile); + $this->writeToFile('add', $data, $addFile); + $this->writeToFile('edit', $data, $editFile); + $this->writeToFile('index', $data, $indexFile); + if ($recyclebinHtml) { + $this->writeToFile('recyclebin', $data, $recyclebinFile); + $recyclebinTitle = in_array('title', $fieldArr) ? 'title' : (in_array('name', $fieldArr) ? 'name' : ''); + $recyclebinTitleJs = $recyclebinTitle ? "\n {field: '{$recyclebinTitle}', title: __('" . (ucfirst($recyclebinTitle)) . "'), align: 'left'}," : ''; + $data['recyclebinJs'] = $this->getReplacedStub('mixins/recyclebinjs', ['recyclebinTitleJs' => $recyclebinTitleJs, 'controllerUrl' => $controllerUrl]); + } // 生成JS文件 - $result = $this->writeToFile('javascript', $data, $javascriptFile); + $this->writeToFile('javascript', $data, $javascriptFile); // 生成语言文件 - if ($langList) - { - $result = $this->writeToFile('lang', $data, $langFile); - } - } - catch (\think\exception\ErrorException $e) - { + $this->writeToFile('lang', $data, $langFile); + } catch (ErrorException $e) { throw new Exception("Code: " . $e->getCode() . "\nLine: " . $e->getLine() . "\nMessage: " . $e->getMessage() . "\nFile: " . $e->getFile()); } //继续生成菜单 - if ($menu) - { + if ($menu) { exec("php think menu -c {$controllerUrl}"); } @@ -790,13 +976,13 @@ class Crud extends Command protected function getEnum(&$getEnum, &$controllerAssignList, $field, $itemArr = '', $inputType = '') { - if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) + if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) { return; + } $fieldList = $this->getFieldListName($field); $methodName = 'get' . ucfirst($fieldList); - foreach ($itemArr as $k => &$v) - { - $v = "__('" . ucfirst($v) . "')"; + foreach ($itemArr as $k => &$v) { + $v = "__('" . mb_ucfirst($v) . "')"; } unset($v); $itemString = $this->getArrayString($itemArr); @@ -804,7 +990,7 @@ class Crud extends Command public function {$methodName}() { return [{$itemString}]; - } + } EOD; $controllerAssignList[] = <<view->assign("{$fieldList}", \$this->model->{$methodName}()); @@ -813,21 +999,26 @@ EOD; protected function getAttr(&$getAttr, $field, $inputType = '') { - if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) + if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) { return; + } $attrField = ucfirst($this->getCamelizeName($field)); $getAttr[] = $this->getReplacedStub("mixins" . DS . $inputType, ['field' => $field, 'methodName' => "get{$attrField}TextAttr", 'listMethodName' => "get{$attrField}List"]); } protected function setAttr(&$setAttr, $field, $inputType = '') { - if ($inputType != 'datetime') + if (!in_array($inputType, ['datetime', 'checkbox', 'select'])) { return; + } $attrField = ucfirst($this->getCamelizeName($field)); - if ($inputType == 'datetime') - { + if ($inputType == 'datetime') { $return = << 1) { + $parentDir = dirname($parseFile); + for ($i = 0; $i < count($parseArr); $i++) { + try { + $iterator = new \FilesystemIterator($parentDir); + $isDirEmpty = !$iterator->valid(); + if ($isDirEmpty) { + rmdir($parentDir); + $parentDir = dirname($parentDir); + } else { + return true; + } + } catch (\UnexpectedValueException $e) { + return false; + } + } + } + return true; + } + + /** + * 获取控制器相关信息 + * @param $module + * @param $controller + * @param $table + * @return array + */ + protected function getControllerData($module, $controller, $table) { - if (!$model) - { - $modelarr = explode('_', strtolower($table)); - foreach ($modelarr as $k => &$v) - $v = ucfirst($v); - unset($v); - $modelName = implode('', $modelarr); - } - else - { - $modelName = ucfirst($model); - } - return $modelName; + return $this->getParseNameData($module, $controller, $table, 'controller'); + } + + /** + * 获取模型相关信息 + * @param $module + * @param $model + * @param $table + * @return array + */ + protected function getModelData($module, $model, $table) + { + return $this->getParseNameData($module, $model, $table, 'model'); + } + + /** + * 获取验证器相关信息 + * @param $module + * @param $validate + * @param $table + * @return array + */ + protected function getValidateData($module, $validate, $table) + { + return $this->getParseNameData($module, $validate, $table, 'validate'); + } + + /** + * 获取已解析相关信息 + * @param string $module 模块名称 + * @param string $name 自定义名称 + * @param string $table 数据表名 + * @param string $type 解析类型,本例中为controller、model、validate + * @return array + */ + protected function getParseNameData($module, $name, $table, $type) + { + $arr = []; + if (!$name) { + $parseName = Loader::parseName($table, 1); + $parseArr = [$table]; + } else { + $name = str_replace(['.', '/', '\\'], '/', $name); + $arr = explode('/', $name); + $parseName = ucfirst(array_pop($arr)); + $parseArr = $arr; + array_push($parseArr, $parseName); + } + //类名不能为内部关键字 + if (in_array(strtolower($parseName), $this->internalKeywords)) { + throw new Exception('Unable to use internal variable:' . $parseName); + } + $appNamespace = Config::get('app_namespace'); + $parseNamespace = "{$appNamespace}\\{$module}\\{$type}" . ($arr ? "\\" . implode("\\", $arr) : ""); + $moduleDir = APP_PATH . $module . DS; + $parseFile = $moduleDir . $type . DS . ($arr ? implode(DS, $arr) . DS : '') . $parseName . '.php'; + return [$parseNamespace, $parseName, $parseFile, $parseArr]; } /** * 写入到文件 * @param string $name - * @param array $data + * @param array $data * @param string $pathname * @return mixed */ protected function writeToFile($name, $data, $pathname) { + foreach ($data as $index => &$datum) { + $datum = is_array($datum) ? '' : $datum; + } + unset($datum); $content = $this->getReplacedStub($name, $data); - if (!is_dir(dirname($pathname))) - { - mkdir(strtolower(dirname($pathname)), 0755, true); + if (!is_dir(dirname($pathname))) { + mkdir(dirname($pathname), 0755, true); } return file_put_contents($pathname, $content); } @@ -883,24 +1156,24 @@ EOD; /** * 获取替换后的数据 * @param string $name - * @param array $data + * @param array $data * @return string */ protected function getReplacedStub($name, $data) { + foreach ($data as $index => &$datum) { + $datum = is_array($datum) ? '' : $datum; + } + unset($datum); $search = $replace = []; - foreach ($data as $k => $v) - { + foreach ($data as $k => $v) { $search[] = "{%{$k}%}"; $replace[] = $v; } $stubname = $this->getStub($name); - if (isset($this->stubList[$stubname])) - { + if (isset($this->stubList[$stubname])) { $stub = $this->stubList[$stubname]; - } - else - { + } else { $this->stubList[$stubname] = $stub = file_get_contents($stubname); } $content = str_replace($search, $replace, $stub); @@ -919,51 +1192,44 @@ EOD; protected function getLangItem($field, $content) { - if ($content || !Lang::has($field)) - { - $itemArr = []; - if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false) - { + if ($content || !Lang::has($field)) { + $this->fieldMaxLen = strlen($field) > $this->fieldMaxLen ? strlen($field) : $this->fieldMaxLen; + $content = str_replace(',', ',', $content); + if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false) { list($fieldLang, $item) = explode(':', $content); $itemArr = [$field => $fieldLang]; - foreach (explode(',', $item) as $k => $v) - { + foreach (explode(',', $item) as $k => $v) { $valArr = explode('=', $v); - if (count($valArr) == 2) - { + if (count($valArr) == 2) { list($key, $value) = $valArr; $itemArr[$field . ' ' . $key] = $value; + $this->fieldMaxLen = strlen($field . ' ' . $key) > $this->fieldMaxLen ? strlen($field . ' ' . $key) : $this->fieldMaxLen; } } - } - else - { + } else { $itemArr = [$field => $content]; } $resultArr = []; - foreach ($itemArr as $k => $v) - { - $resultArr[] = " '" . ucfirst($k) . "' => '{$v}'"; + foreach ($itemArr as $k => $v) { + $resultArr[] = " '" . mb_ucfirst($k) . "' => '{$v}'"; } return implode(",\n", $resultArr); - } - else - { + } else { return ''; } } /** * 读取数据和语言数组列表 - * @param array $arr + * @param array $arr + * @param boolean $withTpl * @return array */ - protected function getLangArray($arr, $withTpl = TRUE) + protected function getLangArray($arr, $withTpl = true) { $langArr = []; - foreach ($arr as $k => $v) - { - $langArr[(is_numeric($k) ? $v : $k)] = is_numeric($k) ? ($withTpl ? "{:" : "") . "__('" . ucfirst($v) . "')" . ($withTpl ? "}" : "") : $v; + foreach ($arr as $k => $v) { + $langArr[$k] = is_numeric($k) ? ($withTpl ? "{:" : "") . "__('" . mb_ucfirst($v) . "')" . ($withTpl ? "}" : "") : $v; } return $langArr; } @@ -975,43 +1241,37 @@ EOD; */ protected function getArrayString($arr) { - if (!is_array($arr)) + if (!is_array($arr)) { return $arr; + } $stringArr = []; - foreach ($arr as $k => $v) - { + foreach ($arr as $k => $v) { $is_var = in_array(substr($v, 0, 1), ['$', '_']); - if (!$is_var) - { + if (!$is_var) { $v = str_replace("'", "\'", $v); $k = str_replace("'", "\'", $k); } $stringArr[] = "'" . $k . "' => " . ($is_var ? $v : "'{$v}'"); } - return implode(",", $stringArr); + return implode(", ", $stringArr); } protected function getItemArray($item, $field, $comment) { $itemArr = []; - if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) - { + $comment = str_replace(',', ',', $comment); + if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) { list($fieldLang, $item) = explode(':', $comment); $itemArr = []; - foreach (explode(',', $item) as $k => $v) - { + foreach (explode(',', $item) as $k => $v) { $valArr = explode('=', $v); - if (count($valArr) == 2) - { + if (count($valArr) == 2) { list($key, $value) = $valArr; $itemArr[$key] = $field . ' ' . $key; } } - } - else - { - foreach ($item as $k => $v) - { + } else { + foreach ($item as $k => $v) { $itemArr[$v] = is_numeric($v) ? $field . ' ' . $v : $v; } } @@ -1021,8 +1281,7 @@ EOD; protected function getFieldType(& $v) { $inputType = 'text'; - switch ($v['DATA_TYPE']) - { + switch ($v['DATA_TYPE']) { case 'bigint': case 'int': case 'mediumint': @@ -1046,11 +1305,11 @@ EOD; case 'tinytext': $inputType = 'textarea'; break; - case 'year'; - case 'date'; - case 'time'; - case 'datetime'; - case 'timestamp'; + case 'year': + case 'date': + case 'time': + case 'datetime': + case 'timestamp': $inputType = 'datetime'; break; default: @@ -1058,41 +1317,43 @@ EOD; } $fieldsName = $v['COLUMN_NAME']; // 指定后缀说明也是个时间字段 - if ($this->isMatchSuffix($fieldsName, $this->intDateSuffix)) - { + if ($this->isMatchSuffix($fieldsName, $this->intDateSuffix)) { $inputType = 'datetime'; } // 指定后缀结尾且类型为enum,说明是个单选框 - if ($this->isMatchSuffix($fieldsName, $this->enumRadioSuffix) && $v['DATA_TYPE'] == 'enum') - { + if ($this->isMatchSuffix($fieldsName, $this->enumRadioSuffix) && $v['DATA_TYPE'] == 'enum') { $inputType = "radio"; } // 指定后缀结尾且类型为set,说明是个复选框 - if ($this->isMatchSuffix($fieldsName, $this->setCheckboxSuffix) && $v['DATA_TYPE'] == 'set') - { + if ($this->isMatchSuffix($fieldsName, $this->setCheckboxSuffix) && $v['DATA_TYPE'] == 'set') { $inputType = "checkbox"; } // 指定后缀结尾且类型为char或tinyint且长度为1,说明是个Switch复选框 - if ($this->isMatchSuffix($fieldsName, $this->switchSuffix) && ($v['COLUMN_TYPE'] == 'tinyint(1)' || $v['COLUMN_TYPE'] == 'char(1)') && $v['COLUMN_DEFAULT'] !== '' && $v['COLUMN_DEFAULT'] !== null) - { + if ($this->isMatchSuffix($fieldsName, $this->switchSuffix) && ($v['COLUMN_TYPE'] == 'tinyint(1)' || $v['COLUMN_TYPE'] == 'char(1)') && $v['COLUMN_DEFAULT'] !== '' && $v['COLUMN_DEFAULT'] !== null) { $inputType = "switch"; } + // 指定后缀结尾城市选择框 + if ($this->isMatchSuffix($fieldsName, $this->citySuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'char')) { + $inputType = "citypicker"; + } + // 指定后缀结尾JSON配置 + if ($this->isMatchSuffix($fieldsName, $this->jsonSuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'text')) { + $inputType = "fieldlist"; + } return $inputType; } /** * 判断是否符合指定后缀 - * @param string $field 字段名称 - * @param mixed $suffixArr 后缀 + * @param string $field 字段名称 + * @param mixed $suffixArr 后缀 * @return boolean */ protected function isMatchSuffix($field, $suffixArr) { $suffixArr = is_array($suffixArr) ? $suffixArr : explode(',', $suffixArr); - foreach ($suffixArr as $k => $v) - { - if (preg_match("/{$v}$/i", $field)) - { + foreach ($suffixArr as $k => $v) { + if (preg_match("/{$v}$/i", $field)) { return true; } } @@ -1107,10 +1368,10 @@ EOD; */ protected function getFormGroup($field, $content) { - $langField = ucfirst($field); - return<< - +
{$content}
@@ -1122,74 +1383,103 @@ EOD; * 获取图片模板数据 * @param string $field * @param string $content - * @return array + * @return string */ protected function getImageUpload($field, $content) { - $filter = ''; - if ($this->isMatchSuffix($field, $this->imageField)) - { - $filter = ' data-mimetype="image/*"'; + $uploadfilter = $selectfilter = ''; + if ($this->isMatchSuffix($field, $this->imageField)) { + $uploadfilter = ' data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp"'; + $selectfilter = ' data-mimetype="image/*"'; } $multiple = substr($field, -1) == 's' ? ' data-multiple="true"' : ' data-multiple="false"'; - $preview = $filter ? ' data-preview-id="p-' . $field . '"' : ''; - $previewcontainer = $preview ? '
    ' : ''; + $preview = ' data-preview-id="p-' . $field . '"'; + $previewcontainer = $preview ? '
      ' : ''; return << +
      {$content} - - - {$previewcontainer} +
      + + +
      +
      + {$previewcontainer} EOD; } /** * 获取JS列数据 * @param string $field + * @param string $datatype + * @param string $extend + * @param array $itemArr * @return string */ - protected function getJsColumn($field, $datatype = '', $extend = '') + protected function getJsColumn($field, $datatype = '', $extend = '', $itemArr = []) { - $lang = ucfirst($field); + $lang = mb_ucfirst($field); $formatter = ''; - foreach ($this->fieldFormatterSuffix as $k => $v) - { - if (preg_match("/{$k}$/i", $field)) - { - if (is_array($v)) - { - if (in_array($datatype, $v['type'])) - { + foreach ($this->fieldFormatterSuffix as $k => $v) { + if (preg_match("/{$k}$/i", $field)) { + if (is_array($v)) { + if (in_array($datatype, $v['type'])) { $formatter = $v['name']; break; } - } - else - { + } else { $formatter = $v; break; } } } - if ($formatter) - { - $extend = ''; - } - $html = str_repeat(" ", 24) . "{field: '{$field}{$extend}', title: __('{$lang}')"; - //$formatter = $extend ? '' : $formatter; - if ($extend) - { - $html .= ", operate:false"; - if ($datatype == 'set') - { - $formatter = 'label'; + $html = str_repeat(" ", 24) . "{field: '{$field}', title: __('{$lang}')"; + + if ($datatype == 'set') { + $formatter = 'label'; + } + foreach ($itemArr as $k => &$v) { + if (substr($v, 0, 3) !== '__(') { + $v = "__('" . mb_ucfirst($v) . "')"; } } - if ($formatter) + unset($v); + $searchList = json_encode($itemArr, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE); + $searchList = str_replace(['":"', '"}', ')","'], ['":', '}', '),"'], $searchList); + if ($itemArr) { + $html .= ", searchList: " . $searchList; + } + + // 文件、图片、权重等字段默认不加入搜索栏,字符串类型默认LIKE + $noSearchFiles = ['file$', 'files$', 'image$', 'images$', '^weigh$']; + if(preg_match("/" . implode('|', $noSearchFiles) . "/i", $field)){ + $html .= ", operate: false"; + }else if(in_array($datatype, ['varchar'])) { + $html .= ", operate: 'LIKE'"; + } + + if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') { + $html .= ", operate:'RANGE', addclass:'datetimerange', autocomplete:false"; + } elseif (in_array($datatype, ['float', 'double', 'decimal'])) { + $html .= ", operate:'BETWEEN'"; + } + if (in_array($datatype, ['set'])) { + $html .= ", operate:'FIND_IN_SET'"; + } + if (in_array($formatter, ['image', 'images'])) { + $html .= ", events: Table.api.events.image"; + } + if (in_array($formatter, ['toggle'])) { + $html .= ", table: table"; + } + if ($itemArr && !$formatter) { + $formatter = 'normal'; + } + if ($formatter) { $html .= ", formatter: Table.api.formatter." . $formatter . "}"; - else + } else { $html .= "}"; + } return $html; } @@ -1203,5 +1493,4 @@ EOD; { return $this->getCamelizeName($field) . 'List'; } - } diff --git a/application/admin/command/Crud/stubs/controller.stub b/application/admin/command/Crud/stubs/controller.stub index 2c9ca9eccdc7ea780011e5cf51a44412d138b504..72a268e0ea09f886840525d9bfae466bb73ae56c 100644 --- a/application/admin/command/Crud/stubs/controller.stub +++ b/application/admin/command/Crud/stubs/controller.stub @@ -1,37 +1,40 @@ -model = model('{%modelName%}'); -{%controllerAssignList%} - } - - /** - * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个方法 - * 因此在当前控制器中可不用编写增删改查的代码,如果需要自己控制这部分逻辑 - * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 - */ - -{%controllerIndex%} -} +model = new \{%modelNamespace%}\{%modelName%}; +{%controllerAssignList%} + } + + public function import() + { + parent::import(); + } + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + +{%controllerIndex%} +} diff --git a/application/admin/command/Crud/stubs/controllerindex.stub b/application/admin/command/Crud/stubs/controllerindex.stub old mode 100644 new mode 100755 index 35c94e3e8fb7f99d8dd44a095faaf1b5191191b9..e04ca129b197568adc59e1c2aaf6181d35baff56 --- a/application/admin/command/Crud/stubs/controllerindex.stub +++ b/application/admin/command/Crud/stubs/controllerindex.stub @@ -7,25 +7,28 @@ //当前是否为关联查询 $this->relationSearch = {%relationSearch%}; //设置过滤方法 - $this->request->filter(['strip_tags', 'htmlspecialchars']); - if ($this->request->isAjax()) - { + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - {%relationWith%} - ->where($where) - ->order($sort, $order) - ->count(); $list = $this->model - {%relationWith%} + {%relationWithList%} ->where($where) ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $result = array("total" => $total, "rows" => $list); + ->paginate($limit); + + foreach ($list as $row) { + {%visibleFieldList%} + {%relationVisibleFieldList%} + } + + $result = array("total" => $list->total(), "rows" => $list->items()); return json($result); } return $this->view->fetch(); - } \ No newline at end of file + } diff --git a/application/admin/command/Crud/stubs/html/checkbox.stub b/application/admin/command/Crud/stubs/html/checkbox.stub index cdfeb8136388b075487af0f9b3d0a42165f826f8..9aa587e72ebe79e05a684e9683825c03563dfa17 100644 --- a/application/admin/command/Crud/stubs/html/checkbox.stub +++ b/application/admin/command/Crud/stubs/html/checkbox.stub @@ -1,4 +1,6 @@ - + +
      {foreach name="{%fieldList%}" item="vo"} {/foreach} +
      diff --git a/application/admin/command/Crud/stubs/html/fieldlist.stub b/application/admin/command/Crud/stubs/html/fieldlist.stub new file mode 100644 index 0000000000000000000000000000000000000000..42688bba40e5983f612778afa672c25558f37966 --- /dev/null +++ b/application/admin/command/Crud/stubs/html/fieldlist.stub @@ -0,0 +1,10 @@ + +
      +
      + {:__('{%itemKey%}')} + {:__('{%itemValue%}')} +
      +
      {:__('Append')}
      + +
      + diff --git a/application/admin/command/Crud/stubs/html/heading-html.stub b/application/admin/command/Crud/stubs/html/heading-html.stub new file mode 100644 index 0000000000000000000000000000000000000000..9392c8024004d9e5e3da9883628e7eb5c720fa76 --- /dev/null +++ b/application/admin/command/Crud/stubs/html/heading-html.stub @@ -0,0 +1,10 @@ + +
      + {:build_heading(null,FALSE)} + +
      diff --git a/application/admin/command/Crud/stubs/html/radio.stub b/application/admin/command/Crud/stubs/html/radio.stub index 292ca4c3267e67d7b2eaf8e507b792430b2b51df..a3c7b2ec00d6885934e6122ac55216967839fe3d 100644 --- a/application/admin/command/Crud/stubs/html/radio.stub +++ b/application/admin/command/Crud/stubs/html/radio.stub @@ -1,4 +1,6 @@ - + +
      {foreach name="{%fieldList%}" item="vo"} {/foreach} +
      diff --git a/application/admin/command/Crud/stubs/html/recyclebin-html.stub b/application/admin/command/Crud/stubs/html/recyclebin-html.stub new file mode 100644 index 0000000000000000000000000000000000000000..8aa39493775558942660774c4033d770aebfd898 --- /dev/null +++ b/application/admin/command/Crud/stubs/html/recyclebin-html.stub @@ -0,0 +1 @@ + {:__('Recycle bin')} \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/html/switch.stub b/application/admin/command/Crud/stubs/html/switch.stub new file mode 100644 index 0000000000000000000000000000000000000000..e2aeec014c48e3d7caac7344b86148a0d91a11bd --- /dev/null +++ b/application/admin/command/Crud/stubs/html/switch.stub @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/index.stub b/application/admin/command/Crud/stubs/index.stub index d3eefa4ffe993931e71fc62172907e92c38b5b9b..7a9e491bcfe4084d4661f89148946a533f6dc85a 100644 --- a/application/admin/command/Crud/stubs/index.stub +++ b/application/admin/command/Crud/stubs/index.stub @@ -1,21 +1,31 @@
      - {:build_heading()} + {%headingHtml%}
      diff --git a/application/admin/command/Crud/stubs/javascript.stub b/application/admin/command/Crud/stubs/javascript.stub index 6b938cf9fd0f1fb9f63bea2d6e0a4d268b34fb6c..4774dffab2b5d07b18f8e54a8582f76b99c907a0 100644 --- a/application/admin/command/Crud/stubs/javascript.stub +++ b/application/admin/command/Crud/stubs/javascript.stub @@ -5,11 +5,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin // 初始化表格参数配置 Table.api.init({ extend: { - index_url: '{%controllerUrl%}/index', + index_url: '{%controllerUrl%}/index' + location.search, add_url: '{%controllerUrl%}/add', edit_url: '{%controllerUrl%}/edit', del_url: '{%controllerUrl%}/del', multi_url: '{%controllerUrl%}/multi', + import_url: '{%controllerUrl%}/import', table: '{%table%}', } }); @@ -30,7 +31,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin // 为表格绑定事件 Table.api.bindevent(table); - }, + },{%recyclebinJs%} add: function () { Controller.api.bindevent(); }, diff --git a/application/admin/command/Crud/stubs/mixins/checkbox.stub b/application/admin/command/Crud/stubs/mixins/checkbox.stub index 909adfd45ab415afa6f7a94ffb2b6abf5b797a97..d5f7b664096815113afbe4c471f59a002a00d89d 100644 --- a/application/admin/command/Crud/stubs/mixins/checkbox.stub +++ b/application/admin/command/Crud/stubs/mixins/checkbox.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $valueArr = explode(',', $value); $list = $this->{%listMethodName%}(); return implode(',', array_intersect_key($list, array_flip($valueArr))); diff --git a/application/admin/command/Crud/stubs/mixins/datetime.stub b/application/admin/command/Crud/stubs/mixins/datetime.stub index 60da81f37c8e1b84384929a4cd1a41392c360d00..591dd4ddfe9acda6e0d49120a2b8b8c59c81addb 100644 --- a/application/admin/command/Crud/stubs/mixins/datetime.stub +++ b/application/admin/command/Crud/stubs/mixins/datetime.stub @@ -1,6 +1,6 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/modelinit.stub b/application/admin/command/Crud/stubs/mixins/modelinit.stub index 887f9de2a42f52d5a815393ee082a90bbe1ab785..329fb95c503667b9246779ccb5791d1ce6548b92 100644 --- a/application/admin/command/Crud/stubs/mixins/modelinit.stub +++ b/application/admin/command/Crud/stubs/mixins/modelinit.stub @@ -2,6 +2,7 @@ protected static function init() { self::afterInsert(function ($row) { - $row->save(['{%order%}' => $row['id']]); + $pk = $row->getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['{%order%}' => $row[$pk]]); }); } diff --git a/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub b/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub old mode 100644 new mode 100755 index fa6c42765def7a63016424981bb1f5cfbf974428..3da6462e7abcc374a542d64e06583105d2e41840 --- a/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub +++ b/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub @@ -1,5 +1,5 @@ public function {%relationMethod%}() { - return $this->{%relationMode%}('{%relationModelName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}')->setEagerlyType(0); + return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0); } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/multiple.stub b/application/admin/command/Crud/stubs/mixins/multiple.stub index 909adfd45ab415afa6f7a94ffb2b6abf5b797a97..d5f7b664096815113afbe4c471f59a002a00d89d 100644 --- a/application/admin/command/Crud/stubs/mixins/multiple.stub +++ b/application/admin/command/Crud/stubs/mixins/multiple.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $valueArr = explode(',', $value); $list = $this->{%listMethodName%}(); return implode(',', array_intersect_key($list, array_flip($valueArr))); diff --git a/application/admin/command/Crud/stubs/mixins/radio.stub b/application/admin/command/Crud/stubs/mixins/radio.stub index f5fa6e0dbc809fb6dc1d8c38bf3f6ef799c0daac..71234a63354d823a9ac536e861ab173e872121e8 100644 --- a/application/admin/command/Crud/stubs/mixins/radio.stub +++ b/application/admin/command/Crud/stubs/mixins/radio.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) - { - $value = $value ? $value : $data['{%field%}']; + { + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $list = $this->{%listMethodName%}(); return isset($list[$value]) ? $list[$value] : ''; } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/recyclebinjs.stub b/application/admin/command/Crud/stubs/mixins/recyclebinjs.stub new file mode 100644 index 0000000000000000000000000000000000000000..fa0ea5c7c3d3d8b2258eaea85ef638fbce55e451 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/recyclebinjs.stub @@ -0,0 +1,60 @@ + + recyclebin: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + 'dragsort_url': '' + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: '{%controllerUrl%}/recyclebin' + location.search, + pk: 'id', + sortName: 'id', + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')},{%recyclebinTitleJs%} + { + field: 'deletetime', + title: __('Deletetime'), + operate: 'RANGE', + addclass: 'datetimerange', + formatter: Table.api.formatter.datetime + }, + { + field: 'operate', + width: '130px', + title: __('Operate'), + table: table, + events: Table.api.events.operate, + buttons: [ + { + name: 'Restore', + text: __('Restore'), + classname: 'btn btn-xs btn-info btn-ajax btn-restoreit', + icon: 'fa fa-rotate-left', + url: '{%controllerUrl%}/restore', + refresh: true + }, + { + name: 'Destroy', + text: __('Destroy'), + classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit', + icon: 'fa fa-times', + url: '{%controllerUrl%}/destroy', + refresh: true + } + ], + formatter: Table.api.formatter.operate + } + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/select.stub b/application/admin/command/Crud/stubs/mixins/select.stub index f5fa6e0dbc809fb6dc1d8c38bf3f6ef799c0daac..71234a63354d823a9ac536e861ab173e872121e8 100644 --- a/application/admin/command/Crud/stubs/mixins/select.stub +++ b/application/admin/command/Crud/stubs/mixins/select.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) - { - $value = $value ? $value : $data['{%field%}']; + { + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $list = $this->{%listMethodName%}(); return isset($list[$value]) ? $list[$value] : ''; } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/model.stub b/application/admin/command/Crud/stubs/model.stub old mode 100644 new mode 100755 index 83615116a9dba517fed8275a48c4f4281b743b87..4e92e3a989d03f487a9ff9670170b8c75cafe4e6 --- a/application/admin/command/Crud/stubs/model.stub +++ b/application/admin/command/Crud/stubs/model.stub @@ -1,33 +1,40 @@ - + {:build_heading()} + +
      +
      +
      + +
      + +
      +
      +
      diff --git a/application/admin/command/Crud/stubs/relationmodel.stub b/application/admin/command/Crud/stubs/relationmodel.stub old mode 100644 new mode 100755 index 17f806934601111fe94c4fae078ee223f9c3890f..dd22a53351f22e43295e2ef02e737bf17a753520 --- a/application/admin/command/Crud/stubs/relationmodel.stub +++ b/application/admin/command/Crud/stubs/relationmodel.stub @@ -1,12 +1,12 @@ - [], - 'edit' => [], - ]; - -} + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/command/Install.php b/application/admin/command/Install.php index 811903962e4622806e52dfe9f7f6537adbd249ad..f7cd11af89c0c505c5dca533932358aff5734416 100644 --- a/application/admin/command/Install.php +++ b/application/admin/command/Install.php @@ -2,6 +2,7 @@ namespace app\admin\command; +use fast\Random; use PDO; use think\Config; use think\console\Command; @@ -10,48 +11,292 @@ use think\console\input\Option; use think\console\Output; use think\Db; use think\Exception; +use think\Lang; +use think\Request; +use think\View; class Install extends Command { - protected $model = null; + /** + * @var \think\View 视图类实例 + */ + protected $view; + + /** + * @var \think\Request Request 实例 + */ + protected $request; protected function configure() { + $config = Config::get('database'); $this - ->setName('install') - ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', FALSE) - ->setDescription('New installation of FastAdmin'); + ->setName('install') + ->addOption('hostname', 'a', Option::VALUE_OPTIONAL, 'mysql hostname', $config['hostname']) + ->addOption('hostport', 'o', Option::VALUE_OPTIONAL, 'mysql hostport', $config['hostport']) + ->addOption('database', 'd', Option::VALUE_OPTIONAL, 'mysql database', $config['database']) + ->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix']) + ->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username']) + ->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password']) + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', false) + ->setDescription('New installation of FastAdmin'); } + /** + * 命令行安装 + */ protected function execute(Input $input, Output $output) { - - //覆盖安装 + define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS); + // 覆盖安装 $force = $input->getOption('force'); + $hostname = $input->getOption('hostname'); + $hostport = $input->getOption('hostport'); + $database = $input->getOption('database'); + $prefix = $input->getOption('prefix'); + $username = $input->getOption('username'); + $password = $input->getOption('password'); - $installLockFile = __DIR__ . "/Install/install.lock"; - if (is_file($installLockFile) && !$force) - { + $installLockFile = INSTALL_PATH . "install.lock"; + if (is_file($installLockFile) && !$force) { throw new Exception("\nFastAdmin already installed!\nIf you need to reinstall again, use the parameter --force=true "); } - $sql = file_get_contents(__DIR__ . '/Install/fastadmin.sql'); + $adminUsername = 'admin'; + $adminPassword = Random::alnum(10); + $adminEmail = 'admin@admin.com'; + $siteName = __('My Website'); + + $adminName = $this->installation($hostname, $hostport, $database, $username, $password, $prefix, $adminUsername, $adminPassword, $adminEmail, $siteName); + if ($adminName) { + $output->highlight("Admin url:http://www.yoursite.com/{$adminName}"); + } + + $output->highlight("Admin username:{$adminUsername}"); + $output->highlight("Admin password:{$adminPassword}"); + + \think\Cache::rm('__menu__'); + + $output->info("Install Successed!"); + } + + /** + * PC端安装 + */ + public function index() + { + $this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); + $this->request = Request::instance(); + + define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS); + $langSet = strtolower($this->request->langset()); + if (!$langSet || in_array($langSet, ['zh-cn', 'zh-hans-cn'])) { + Lang::load(INSTALL_PATH . 'zh-cn.php'); + } + + $installLockFile = INSTALL_PATH . "install.lock"; + + if (is_file($installLockFile)) { + echo __('The system has been installed. If you need to reinstall, please remove %s first', 'install.lock'); + exit; + } + $output = function ($code, $msg, $url = null, $data = null) { + return json(['code' => $code, 'msg' => $msg, 'url' => $url, 'data' => $data]); + }; + + if ($this->request->isPost()) { + $mysqlHostname = $this->request->post('mysqlHostname', '127.0.0.1'); + $mysqlHostport = $this->request->post('mysqlHostport', '3306'); + $hostArr = explode(':', $mysqlHostname); + if (count($hostArr) > 1) { + $mysqlHostname = $hostArr[0]; + $mysqlHostport = $hostArr[1]; + } + $mysqlUsername = $this->request->post('mysqlUsername', 'root'); + $mysqlPassword = $this->request->post('mysqlPassword', ''); + $mysqlDatabase = $this->request->post('mysqlDatabase', ''); + $mysqlPrefix = $this->request->post('mysqlPrefix', 'fa_'); + $adminUsername = $this->request->post('adminUsername', 'admin'); + $adminPassword = $this->request->post('adminPassword', ''); + $adminPasswordConfirmation = $this->request->post('adminPasswordConfirmation', ''); + $adminEmail = $this->request->post('adminEmail', 'admin@admin.com'); + $siteName = $this->request->post('siteName', __('My Website')); + + if ($adminPassword !== $adminPasswordConfirmation) { + return $output(0, __('The two passwords you entered did not match')); + } + + $adminName = ''; + try { + $adminName = $this->installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail, $siteName); + } catch (\PDOException $e) { + throw new Exception($e->getMessage()); + } catch (\Exception $e) { + return $output(0, $e->getMessage()); + } + return $output(1, __('Install Successed'), null, ['adminName' => $adminName]); + } + $errInfo = ''; + try { + $this->checkenv(); + } catch (\Exception $e) { + $errInfo = $e->getMessage(); + } + return $this->view->fetch(INSTALL_PATH . "install.html", ['errInfo' => $errInfo]); + } + + /** + * 执行安装 + */ + protected function installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail = null, $siteName = null) + { + $this->checkenv(); + + if ($mysqlDatabase == '') { + throw new Exception(__('Please input correct database')); + } + if (!preg_match("/^\w{3,12}$/", $adminUsername)) { + throw new Exception(__('Please input correct username')); + } + if (!preg_match("/^[\S]{6,16}$/", $adminPassword)) { + throw new Exception(__('Please input correct password')); + } + if ($siteName == '' || preg_match("/fast" . "admin/i", $siteName)) { + throw new Exception(__('Please input correct website')); + } + + $sql = file_get_contents(INSTALL_PATH . 'fastadmin.sql'); + + $sql = str_replace("`fa_", "`{$mysqlPrefix}", $sql); // 先尝试能否自动创建数据库 $config = Config::get('database'); - $pdo = new PDO("{$config['type']}:host={$config['hostname']}" . ($config['hostport'] ? ";port={$config['hostport']}" : ''), $config['username'], $config['password']); - $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $pdo->query("CREATE DATABASE IF NOT EXISTS `{$config['database']}` CHARACTER SET utf8 COLLATE utf8_general_ci;"); + try { + $pdo = new PDO("{$config['type']}:host={$mysqlHostname}" . ($mysqlHostport ? ";port={$mysqlHostport}" : ''), $mysqlUsername, $mysqlPassword); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->query("CREATE DATABASE IF NOT EXISTS `{$mysqlDatabase}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"); - // 查询一次SQL,判断连接是否正常 - Db::execute("SELECT 1"); + // 连接install命令中指定的数据库 + $instance = Db::connect([ + 'type' => "{$config['type']}", + 'hostname' => "{$mysqlHostname}", + 'hostport' => "{$mysqlHostport}", + 'database' => "{$mysqlDatabase}", + 'username' => "{$mysqlUsername}", + 'password' => "{$mysqlPassword}", + 'prefix' => "{$mysqlPrefix}", + ]); - // 调用原生PDO对象进行批量查询 - Db::getPdo()->exec($sql); + // 查询一次SQL,判断连接是否正常 + $instance->execute("SELECT 1"); - file_put_contents($installLockFile, 1); - $output->info("Install Successed!"); + // 调用原生PDO对象进行批量查询 + $instance->getPdo()->exec($sql); + } catch (\PDOException $e) { + throw new Exception($e->getMessage()); + } + // 后台入口文件 + $adminFile = ROOT_PATH . 'public' . DS . 'admin.php'; + + // 数据库配置文件 + $dbConfigFile = APP_PATH . 'database.php'; + $config = @file_get_contents($dbConfigFile); + $callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) { + $field = "mysql" . ucfirst($matches[1]); + $replace = $$field; + if ($matches[1] == 'hostport' && $mysqlHostport == 3306) { + $replace = ''; + } + return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}Env::get('database.{$matches[1]}', '{$replace}'),"; + }; + $config = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $config); + + // 检测能否成功写入数据库配置 + $result = @file_put_contents($dbConfigFile, $config); + if (!$result) { + throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/database.php')); + } + + // 变更默认管理员密码 + $adminPassword = $adminPassword ? $adminPassword : Random::alnum(8); + $adminEmail = $adminEmail ? $adminEmail : "admin@admin.com"; + $newSalt = substr(md5(uniqid(true)), 0, 6); + $newPassword = md5(md5($adminPassword) . $newSalt); + $data = ['username' => $adminUsername, 'email' => $adminEmail, 'password' => $newPassword, 'salt' => $newSalt]; + $instance->name('admin')->where('username', 'admin')->update($data); + + // 变更前台默认用户的密码,随机生成 + $newSalt = substr(md5(uniqid(true)), 0, 6); + $newPassword = md5(md5(Random::alnum(8)) . $newSalt); + $instance->name('user')->where('username', 'admin')->update(['password' => $newPassword, 'salt' => $newSalt]); + + // 修改后台入口 + $adminName = ''; + if (is_file($adminFile)) { + $adminName = Random::alpha(10) . '.php'; + rename($adminFile, ROOT_PATH . 'public' . DS . $adminName); + } + + //修改站点名称 + if ($siteName != config('site.name')) { + $instance->name('config')->where('name', 'name')->update(['value' => $siteName]); + $configFile = CONF_PATH . 'extra' . DS . 'site.php'; + $config = include $configFile; + $configList = $instance->name("config")->select(); + foreach ($configList as $k => $value) { + if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) { + $value['value'] = explode(',', $value['value']); + } + if ($value['type'] == 'array') { + $value['value'] = (array)json_decode($value['value'], true); + } + $config[$value['name']] = $value['value']; + } + $config['name'] = $siteName; + file_put_contents($configFile, ' $v) { + if (!is_dir(ROOT_PATH . $v)) { + throw new Exception(__('Please go to the official website to download the full package or resource package and try to install')); + break; + } + } + return true; + } } diff --git a/application/admin/command/Install/fastadmin.sql b/application/admin/command/Install/fastadmin.sql index 5823a760f830b8b79e4cfa8b5a53d2b31e570e73..8cbffc6c6ab87dabbe793a88d2377c97bf62c111 100755 --- a/application/admin/command/Install/fastadmin.sql +++ b/application/admin/command/Install/fastadmin.sql @@ -1,144 +1,139 @@ /* FastAdmin Install SQL - - 官网: http://www.fastadmin.net - 演示: http://demo.fastadmin.net - - Date: 07/11/2017 23:52:44 PM + Date: 2020-06-11 22:11:09 */ SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- --- Table structure for `fa_admin` +-- Table structure for fa_admin -- ---------------------------- DROP TABLE IF EXISTS `fa_admin`; CREATE TABLE `fa_admin` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `username` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名', - `nickname` varchar(50) NOT NULL DEFAULT '' COMMENT '昵称', - `password` varchar(32) NOT NULL DEFAULT '' COMMENT '密码', - `salt` varchar(30) NOT NULL DEFAULT '' COMMENT '密码盐', - `avatar` varchar(100) NOT NULL DEFAULT '' COMMENT '头像', - `email` varchar(100) NOT NULL DEFAULT '' COMMENT '电子邮箱', + `username` varchar(20) DEFAULT '' COMMENT '用户名', + `nickname` varchar(50) DEFAULT '' COMMENT '昵称', + `password` varchar(32) DEFAULT '' COMMENT '密码', + `salt` varchar(30) DEFAULT '' COMMENT '密码盐', + `avatar` varchar(255) DEFAULT '' COMMENT '头像', + `email` varchar(100) DEFAULT '' COMMENT '电子邮箱', `loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数', - `logintime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '登录时间', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `token` varchar(59) NOT NULL DEFAULT '' COMMENT 'Session标识', + `logintime` int(10) DEFAULT NULL COMMENT '登录时间', + `loginip` varchar(50) DEFAULT NULL COMMENT '登录IP', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `token` varchar(59) DEFAULT '' COMMENT 'Session标识', `status` varchar(30) NOT NULL DEFAULT 'normal' COMMENT '状态', PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='管理员表'; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='管理员表'; -- ---------------------------- --- Records of `fa_admin` +-- Records of fa_admin -- ---------------------------- BEGIN; -INSERT INTO `fa_admin` VALUES ('1', 'admin', 'Admin', '075eaec83636846f51c152f29b98a2fd', 's4f3', '/assets/img/avatar.png', 'admin@fastadmin.net', '0', '1497070325', '1492186163', '1497070325', 'c586728f-0687-4e1a-84c0-c3b9f9003850', 'normal'), ('2', 'admin2', 'admin2', '9a28ce07ce875fbd14172a9ca5357d3c', '2dHDmj', '/assets/img/avatar.png', 'admin2@fastadmin.net', '0', '0', '1492186163', '1492186163', '', 'normal'), ('3', 'admin3', 'admin3', '1c11f945dfcd808a130a8c2a8753fe62', 'WOKJEn', '/assets/img/avatar.png', 'admin3@fastadmin.net', '0', '0', '1492186201', '1492186201', '', 'normal'), ('4', 'admin22', 'admin22', '1c1a0aa0c3c56a8c1a908aab94519648', 'Aybcn5', '/assets/img/avatar.png', 'admin22@fastadmin.net', '0', '0', '1492186240', '1492186240', '', 'normal'), ('5', 'admin32', 'admin32', 'ade94d5d7a7033afa7d84ac3066d0a02', 'FvYK0u', '/assets/img/avatar.png', 'admin32@fastadmin.net', '0', '0', '1492186263', '1492186263', '', 'normal'), ('6', 'test123', 'test', '2a9020e6ef15245399f00d5cda5fb1e6', 'unbBZH', '', 'test@163.com', '0', '1497062737', '1497062728', '1497070313', '', 'normal'); +INSERT INTO `fa_admin` VALUES (1, 'admin', 'Admin', '075eaec83636846f51c152f29b98a2fd', 's4f3', '/assets/img/avatar.png', 'admin@admin.com', 0, 1502029281, '127.0.0.1',1492186163, 1502029281, 'd3992c3b-5ecc-4ecb-9dc2-8997780fcadc', 'normal'); COMMIT; -- ---------------------------- --- Table structure for `fa_admin_log` +-- Table structure for fa_admin_log -- ---------------------------- DROP TABLE IF EXISTS `fa_admin_log`; CREATE TABLE `fa_admin_log` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID', - `username` varchar(30) NOT NULL DEFAULT '' COMMENT '管理员名字', - `url` varchar(100) NOT NULL DEFAULT '' COMMENT '操作页面', - `title` varchar(100) NOT NULL DEFAULT '' COMMENT '日志标题', + `username` varchar(30) DEFAULT '' COMMENT '管理员名字', + `url` varchar(1500) DEFAULT '' COMMENT '操作页面', + `title` varchar(100) DEFAULT '' COMMENT '日志标题', `content` text NOT NULL COMMENT '内容', - `ip` varchar(50) NOT NULL DEFAULT '' COMMENT 'IP', - `useragent` varchar(255) NOT NULL DEFAULT '' COMMENT 'User-Agent', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '操作时间', + `ip` varchar(50) DEFAULT '' COMMENT 'IP', + `useragent` varchar(255) DEFAULT '' COMMENT 'User-Agent', + `createtime` int(10) DEFAULT NULL COMMENT '操作时间', PRIMARY KEY (`id`), KEY `name` (`username`) -) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='管理员日志表'; - --- ---------------------------- --- Records of `fa_admin_log` --- ---------------------------- -BEGIN; -INSERT INTO `fa_admin_log` VALUES ('1', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"phone-bg.png\",\"signature\":\"e6afb5fb65947ba639810670d67535f2\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499768230\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499681848'), ('2', '1', 'admin', '/public/admin.php/category/edit/ids/5?dialog=1', '分类管理 编辑', '{\"dialog\":\"1\",\"row\":{\"type\":\"page\",\"pid\":\"0\",\"name\":\"\\u8f6f\\u4ef6\\u4ea7\\u54c1\",\"nickname\":\"software\",\"flag\":[\"recommend\"],\"image\":\"\\/uploads\\/20170710\\/52ecad420fc9bb113e588de3b3593b90.png\",\"keywords\":\"\",\"description\":\"\",\"weigh\":\"0\",\"status\":\"normal\"},\"ids\":\"5\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499681850'), ('3', '1', 'admin', '/public/admin.php/test/add/ids?dialog=1', '测试管理 添加', '{\"dialog\":\"1\",\"row_category_id_text\":\"\\u6d4b\\u8bd51\",\"row\":{\"category_id\":\"12\",\"category_ids\":\"12,13\",\"user_id\":\"3\",\"user_ids\":\"3\",\"week\":\"monday\",\"flag\":[\"hot\",\"index\"],\"genderdata\":\"male\",\"hobbydata\":[\"music\",\"reading\"],\"title\":\"\\u6211\\u662f\\u4e00\\u7bc7\\u6d4b\\u8bd5\\u6587\\u7ae0\",\"content\":\"

      \\u6211\\u662f\\u6d4b\\u8bd5\\u5185\\u5bb9<\\/p>\",\"image\":\"\\/assets\\/img\\/avatar.png\",\"images\":\"\\/assets\\/img\\/avatar.png,\\/assets\\/img\\/qrcode.png\",\"attachfile\":\"\\/assets\\/img\\/avatar.png\",\"keywords\":\"\\u5173\\u952e\\u5b57\",\"description\":\"\\u63cf\\u8ff0\",\"price\":\"0.00\",\"views\":\"0\",\"startdate\":\"2017-07-10\",\"activitytime\":\"2017-07-10 18:24:45\",\"year\":\"2017\",\"times\":\"18:24:45\",\"refreshtime\":\"2017-07-10 18:24:45\",\"weigh\":\"0\",\"status\":\"normal\",\"state\":\"1\"},\"row_category_ids_text\":\"\",\"row_user_id_text\":\"admin\",\"row_user_ids_text\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499682526'), ('4', '1', 'admin', '/public/admin/test/add/ids?dialog=1', '测试管理 添加', '{\"dialog\":\"1\",\"row_category_id_text\":\"\\u6d4b\\u8bd52\",\"row\":{\"category_id\":\"13\",\"category_ids\":\"13\",\"user_id\":\"3\",\"user_ids\":\"3\",\"week\":\"monday\",\"flag\":[\"hot\",\"index\"],\"genderdata\":\"male\",\"hobbydata\":[\"music\",\"reading\"],\"title\":\"tes\",\"content\":\"

      test<\\/p>\",\"image\":\"a\",\"images\":\"b\",\"attachfile\":\"c\",\"keywords\":\"a\",\"description\":\"a\",\"price\":\"0.00\",\"views\":\"0\",\"startdate\":\"2017-07-11\",\"activitytime\":\"2017-07-11 16:12:03\",\"year\":\"2017\",\"times\":\"16:12:03\",\"refreshtime\":\"2017-07-11 16:12:03\",\"weigh\":\"0\",\"status\":\"normal\",\"state\":\"1\"},\"row_category_ids_text\":\"\",\"row_user_id_text\":\"admin\",\"row_user_ids_text\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499760748'), ('5', '1', 'admin', '/public/admin/ajax/upload', '', '{\"name\":\"qrcode.png\",\"signature\":\"45fd8c9bd10632cabd23ba0ce7070533\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499847826\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499761445'), ('6', '1', 'admin', '/public/admin/auth/rule/edit/ids/11291?dialog=1', '权限管理 规则管理 编辑', '{\"dialog\":\"1\",\"row\":{\"ismenu\":\"1\",\"pid\":\"11290\",\"name\":\"\\/admin\\/wechat\\/autoreply\",\"title\":\"\\u5fae\\u4fe1\\u81ea\\u52a8\\u56de\\u590d\\u7ba1\\u7406\",\"icon\":\"fa fa-reply-all\",\"weigh\":\"26\",\"condition\":\"\\u7528\\u6237\\u5728\\u5fae\\u4fe1\\u516c\\u4f17\\u53f7\\u4e2d\\u8f93\\u5165\\u7279\\u5b9a\\u7684\\u6587\\u5b57\\u540e\\uff0c\\u670d\\u52a1\\u5668\\u54cd\\u5e94\\u4e0d\\u540c\\u7684\\u4e8b\\u4ef6\",\"remark\":\"\",\"status\":\"normal\"},\"ids\":\"11291\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499761628'), ('7', '1', 'admin', '/public/admin/auth/group/del/ids/1', '权限管理 角色组 删除', '{\"action\":\"del\",\"ids\":\"1\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499762290'), ('8', '1', 'admin', '/public/admin/auth/group/del/ids/1', '权限管理 角色组 删除', '{\"action\":\"del\",\"ids\":\"1\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499762464'), ('9', '1', 'admin', '/public/admin/auth/group/del/ids/1', '权限管理 角色组 删除', '{\"action\":\"del\",\"ids\":\"1\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499762761'), ('10', '1', 'admin', '/public/admin/auth/group/del/ids/1', '权限管理 角色组 删除', '{\"action\":\"del\",\"ids\":\"1\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499762770'), ('11', '1', 'admin', '/public/admin/auth/group/del/ids/1', '权限管理 角色组 删除', '{\"action\":\"del\",\"ids\":\"1\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499762943'), ('12', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795288.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764194'), ('13', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795290.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764194'), ('14', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795282.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764194'), ('15', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795283.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764194'), ('16', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795292.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764194'), ('17', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795286.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764195'), ('18', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795293.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764245'), ('19', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795277.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764259'), ('20', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795284.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764270'), ('21', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795287.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764281'), ('22', '1', 'admin', '/public/admin.php/ajax/upload', '', '{\"name\":\"14558668_1350878795289.jpg\",\"signature\":\"99d25cbae44dab9ed570f7db53ef28fb\",\"bucket\":\"yourbucketname\",\"save-key\":\"\\/uploads\\/{year}{mon}{day}\\/{filemd5}{.suffix}\",\"expiration\":\"1499850578\",\"notify-url\":\"http:\\/\\/www.yoursite.com\\/upyun\\/notify\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499764291'), ('23', '1', 'admin', '/public/admin.php/general/crontab/get_schedule_future', '', '{\"schedule\":\"* * * * *\",\"days\":\"7\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765449'), ('24', '1', 'admin', '/public/admin.php/general/crontab/check_schedule', '', '{\"row\":{\"schedule\":\"* * * * *\"}}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765451'), ('25', '1', 'admin', '/public/admin.php/general/crontab/get_schedule_future', '', '{\"schedule\":\"* * * * *\",\"days\":\"7\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765451'), ('26', '1', 'admin', '/public/admin.php/general/crontab/edit/ids/1?dialog=1', '常规管理 定时任务 编辑', '{\"dialog\":\"1\",\"row\":{\"title\":\"\\u8bf7\\u6c42FastAdmin\",\"type\":\"url\",\"content\":\"http:\\/\\/www.fastadmin.net\",\"schedule\":\"* * * * *\",\"maximums\":\"0\",\"begintime\":\"2017-01-01 00:00:00\",\"endtime\":\"2019-01-01 00:00:00\",\"weigh\":\"1\",\"status\":\"normal\"},\"ids\":\"1\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765451'), ('27', '1', 'admin', '/public/admin.php/general/crontab/get_schedule_future', '', '{\"schedule\":\"* * * * *\",\"days\":\"7\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765453'), ('28', '1', 'admin', '/public/admin.php/general/crontab/check_schedule', '', '{\"row\":{\"schedule\":\"* * * * *\"}}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765456'), ('29', '1', 'admin', '/public/admin.php/general/crontab/get_schedule_future', '', '{\"schedule\":\"* * * * *\",\"days\":\"7\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765456'), ('30', '1', 'admin', '/public/admin.php/general/crontab/edit/ids/2?dialog=1', '常规管理 定时任务 编辑', '{\"dialog\":\"1\",\"row\":{\"title\":\"\\u67e5\\u8be2\\u4e00\\u6761SQL\",\"type\":\"sql\",\"content\":\"SELECT 1;\",\"schedule\":\"* * * * *\",\"maximums\":\"0\",\"begintime\":\"2017-01-01 00:00:00\",\"endtime\":\"2019-01-01 00:00:00\",\"weigh\":\"2\",\"status\":\"normal\"},\"ids\":\"2\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765456'), ('31', '1', 'admin', '/public/admin.php/general/crontab/get_schedule_future', '', '{\"schedule\":\"* * * * *\",\"days\":\"7\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499765460'), ('32', '1', 'admin', '/public/admin.php/page/add/ids?dialog=1', '单页管理 添加', '{\"dialog\":\"1\",\"row_category_id_text\":\"Android\\u5f00\\u53d1\",\"row\":{\"category_id\":\"4\",\"title\":\"test\",\"keywords\":\"test\",\"flag\":\"recommend\",\"image\":\"\\/assets\\/img\\/qrcode.png\",\"content\":\"

      test<\\/p>\",\"icon\":\"\",\"views\":\"0\",\"comments\":\"0\",\"weigh\":\"0\",\"status\":\"normal\"}}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499769482'), ('33', '1', 'admin', '/public/admin.php/example/bootstraptable/change/ids/31', '', '{\"action\":\"\",\"ids\":\"31\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499769813'), ('34', '1', 'admin', '/public/admin.php/example/bootstraptable/change/ids/31', '', '{\"action\":\"\",\"ids\":\"31\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499769814'), ('35', '1', 'admin', '/public/admin.php/example/bootstraptable/change/ids/33', '', '{\"action\":\"\",\"ids\":\"33\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499769815'), ('36', '1', 'admin', '/public/admin.php/example/bootstraptable/change/ids/35', '', '{\"action\":\"\",\"ids\":\"35\",\"params\":\"\"}', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36', '1499769816'); -COMMIT; - --- ---------------------------- --- Table structure for `fa_article` --- ---------------------------- -DROP TABLE IF EXISTS `fa_article`; -CREATE TABLE `fa_article` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类ID', - `flag` set('h','i','r') NOT NULL DEFAULT '' COMMENT '标志', - `title` varchar(50) NOT NULL DEFAULT '' COMMENT '标题', - `content` text NOT NULL COMMENT '内容', - `image` varchar(100) NOT NULL DEFAULT '' COMMENT '图片', - `keywords` varchar(255) NOT NULL DEFAULT '' COMMENT '关键字', - `description` varchar(255) NOT NULL DEFAULT '' COMMENT '描述', - `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '点击', - `comments` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '评论数', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', - `status` enum('normal','hidden') NOT NULL DEFAULT 'normal' COMMENT '状态', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章表'; +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='管理员日志表'; + +-- ---------------------------- +-- Table structure for fa_area +-- ---------------------------- +DROP TABLE IF EXISTS `fa_area`; +CREATE TABLE `fa_area` ( + `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `pid` int(10) DEFAULT NULL COMMENT '父id', + `shortname` varchar(100) DEFAULT NULL COMMENT '简称', + `name` varchar(100) DEFAULT NULL COMMENT '名称', + `mergename` varchar(255) DEFAULT NULL COMMENT '全称', + `level` tinyint(4) DEFAULT NULL COMMENT '层级 0 1 2 省市区县', + `pinyin` varchar(100) DEFAULT NULL COMMENT '拼音', + `code` varchar(100) DEFAULT NULL COMMENT '长途区号', + `zip` varchar(100) DEFAULT NULL COMMENT '邮编', + `first` varchar(50) DEFAULT NULL COMMENT '首字母', + `lng` varchar(100) DEFAULT NULL COMMENT '经度', + `lat` varchar(100) DEFAULT NULL COMMENT '纬度', + PRIMARY KEY (`id`), + KEY `pid` (`pid`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='地区表'; -- ---------------------------- --- Table structure for `fa_attachment` +-- Table structure for fa_attachment -- ---------------------------- DROP TABLE IF EXISTS `fa_attachment`; CREATE TABLE `fa_attachment` ( `id` int(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `url` varchar(255) NOT NULL DEFAULT '' COMMENT '物理路径', - `imagewidth` varchar(30) NOT NULL DEFAULT '' COMMENT '宽度', - `imageheight` varchar(30) NOT NULL DEFAULT '' COMMENT '宽度', - `imagetype` varchar(30) NOT NULL DEFAULT '' COMMENT '图片类型', + `admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID', + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `url` varchar(255) DEFAULT '' COMMENT '物理路径', + `imagewidth` varchar(30) DEFAULT '' COMMENT '宽度', + `imageheight` varchar(30) DEFAULT '' COMMENT '高度', + `imagetype` varchar(30) DEFAULT '' COMMENT '图片类型', `imageframes` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '图片帧数', + `filename` varchar(100) DEFAULT '' COMMENT '文件名称', `filesize` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '文件大小', - `mimetype` varchar(30) NOT NULL DEFAULT '' COMMENT 'mime类型', - `extparam` varchar(255) NOT NULL DEFAULT '' COMMENT '透传数据', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建日期', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `uploadtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上传时间', - `storage` enum('local','upyun') NOT NULL DEFAULT 'local' COMMENT '存储位置', - `sha1` varchar(40) NOT NULL DEFAULT '' COMMENT '文件 sha1编码', - PRIMARY KEY (`id`), - UNIQUE KEY `sha1` (`sha1`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='附件表'; + `mimetype` varchar(100) DEFAULT '' COMMENT 'mime类型', + `extparam` varchar(255) DEFAULT '' COMMENT '透传数据', + `createtime` int(10) DEFAULT NULL COMMENT '创建日期', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `uploadtime` int(10) DEFAULT NULL COMMENT '上传时间', + `storage` varchar(100) NOT NULL DEFAULT 'local' COMMENT '存储位置', + `sha1` varchar(40) DEFAULT '' COMMENT '文件 sha1编码', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='附件表'; -- ---------------------------- --- Records of `fa_attachment` +-- Records of fa_attachment -- ---------------------------- BEGIN; -INSERT INTO `fa_attachment` VALUES ('1', '/assets/img/qrcode.png', '150', '150', 'png', '0', '21859', 'image/png', '', '1499681848', '1499681848', '1499681848', 'local', '17163603d0263e4838b9387ff2cd4877e8b018f6'); +INSERT INTO `fa_attachment` VALUES (1, 1, 0, '/assets/img/qrcode.png', '150', '150', 'png', 0, 'qrcode.png', 21859, 'image/png', '', 1499681848, 1499681848, 1499681848, 'local', '17163603d0263e4838b9387ff2cd4877e8b018f6'); COMMIT; -- ---------------------------- --- Table structure for `fa_auth_group` +-- Table structure for fa_auth_group -- ---------------------------- DROP TABLE IF EXISTS `fa_auth_group`; CREATE TABLE `fa_auth_group` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父组别', - `name` varchar(100) NOT NULL DEFAULT '' COMMENT '组名', + `name` varchar(100) DEFAULT '' COMMENT '组名', `rules` text NOT NULL COMMENT '规则ID', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `status` varchar(30) DEFAULT '' COMMENT '状态', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='分组表'; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='分组表'; -- ---------------------------- --- Records of `fa_auth_group` +-- Records of fa_auth_group -- ---------------------------- BEGIN; -INSERT INTO `fa_auth_group` VALUES ('1', '0', '超级管理员', '*', '1490883540', '1490883540', 'normal'), ('2', '1', '二级管理员', '11180,11181,11182,11183,11184,11185,11198,11199,11200,11201,11202,11203,11204,11205,11206,11207,11208,11209,11210,11211,11212,11213,11214,11215,11216,11217,11218,11219,11220,11221,11222,11223,11224,11225,11226,11227,11228,11229,11230,11231,11232,11233,11234,11235,11236,11237,11238,11239,11240,11241,11242,11243,11244,11245,11246,11247,11248,11249,11250,11251,11252,11253,11254,11255,11256,11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279,11280,11281,11282,11283,11284,11285,11286,11287,11288,11289', '1490883540', '1497431170', 'normal'), ('3', '2', '三级管理员', '11180,11181,11182,11183,11184,11185,11198,11199,11200,11201,11202,11203,11204,11205,11206,11207,11208,11209,11210,11211,11212,11213,11214,11215,11216,11217', '1490883540', '1497431183', 'normal'), ('4', '1', '二级管理员2', '11174,11175,11176,11177,11178,11179,11180,11181,11182,11183,11184,11185,11290,11291,11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,11304,11305,11306,11307,11308,11309,11310,11311,11312,11313,11314,11315,11316', '1490883540', '1497431177', 'normal'), ('5', '2', '三级管理员2', '11180,11181,11182,11183,11184,11185,11218,11219,11220,11221,11222,11223,11224,11225,11226,11227,11228,11229,11230,11231,11232,11233,11234,11235,11236,11237,11238,11239,11240,11241,11242,11243,11244,11245,11246,11247,11248,11249,11250,11251,11252,11253,11254,11255,11256,11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,11268,11269,11270,11271,11272,11273,11274,11275,11276,11277,11278,11279,11280,11281,11282,11283,11284,11285,11286,11287,11288,11289', '1490883540', '1497431190', 'normal'); +INSERT INTO `fa_auth_group` VALUES (1, 0, 'Admin group', '*', 1490883540, 149088354, 'normal'); +INSERT INTO `fa_auth_group` VALUES (2, 1, 'Second group', '13,14,16,15,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63,64,65,1,9,10,11,7,6,8,2,4,5', 1490883540, 1505465692, 'normal'); +INSERT INTO `fa_auth_group` VALUES (3, 2, 'Third group', '1,4,9,10,11,13,14,15,16,17,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63,64,65,5', 1490883540, 1502205322, 'normal'); +INSERT INTO `fa_auth_group` VALUES (4, 1, 'Second group 2', '1,4,13,14,15,16,17,55,56,57,58,59,60,61,62,63,64,65', 1490883540, 1502205350, 'normal'); +INSERT INTO `fa_auth_group` VALUES (5, 2, 'Third group 2', '1,2,6,7,8,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34', 1490883540, 1502205344, 'normal'); COMMIT; -- ---------------------------- --- Table structure for `fa_auth_group_access` +-- Table structure for fa_auth_group_access -- ---------------------------- DROP TABLE IF EXISTS `fa_auth_group_access`; CREATE TABLE `fa_auth_group_access` ( @@ -147,389 +142,451 @@ CREATE TABLE `fa_auth_group_access` ( UNIQUE KEY `uid_group_id` (`uid`,`group_id`), KEY `uid` (`uid`), KEY `group_id` (`group_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限分组表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='权限分组表'; -- ---------------------------- --- Records of `fa_auth_group_access` +-- Records of fa_auth_group_access -- ---------------------------- BEGIN; -INSERT INTO `fa_auth_group_access` VALUES ('1', '1'), ('2', '2'), ('3', '3'), ('4', '5'), ('5', '5'), ('6', '2'); +INSERT INTO `fa_auth_group_access` VALUES (1, 1); COMMIT; -- ---------------------------- --- Table structure for `fa_auth_rule` +-- Table structure for fa_auth_rule -- ---------------------------- DROP TABLE IF EXISTS `fa_auth_rule`; CREATE TABLE `fa_auth_rule` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `type` enum('menu','file') NOT NULL DEFAULT 'file' COMMENT 'menu为菜单,file为权限节点', `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID', - `name` varchar(100) NOT NULL DEFAULT '' COMMENT '规则名称', - `title` varchar(50) NOT NULL DEFAULT '' COMMENT '规则名称', - `icon` varchar(50) NOT NULL DEFAULT '' COMMENT '图标', - `condition` varchar(255) NOT NULL DEFAULT '' COMMENT '条件', - `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', + `name` varchar(100) DEFAULT '' COMMENT '规则名称', + `title` varchar(50) DEFAULT '' COMMENT '规则名称', + `icon` varchar(50) DEFAULT '' COMMENT '图标', + `condition` varchar(255) DEFAULT '' COMMENT '条件', + `remark` varchar(255) DEFAULT '' COMMENT '备注', `ismenu` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否为菜单', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', + `status` varchar(30) DEFAULT '' COMMENT '状态', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) USING BTREE, KEY `pid` (`pid`), KEY `weigh` (`weigh`) -) ENGINE=InnoDB AUTO_INCREMENT=11324 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='节点表'; +) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='节点表'; -- ---------------------------- --- Records of `fa_auth_rule` +-- Records of fa_auth_rule -- ---------------------------- BEGIN; -INSERT INTO `fa_auth_rule` VALUES ('11174', 'file', '0', '/admin/category', '分类管理', 'fa fa-list\r', '', '用于统一管理网站的所有分类,分类可进行无限级分类\r', '1', '1497429920', '1497429920', '119', 'normal'), ('11175', 'file', '11174', '/admin/category/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '142', 'normal'), ('11176', 'file', '11174', '/admin/category/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '141', 'normal'), ('11177', 'file', '11174', '/admin/category/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '140', 'normal'), ('11178', 'file', '11174', '/admin/category/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '139', 'normal'), ('11179', 'file', '11174', '/admin/category/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '138', 'normal'), ('11180', 'file', '0', '/admin/dashboard', '控制台', 'fa fa-dashboard\r', '', '用于展示当前系统中的统计数据、统计报表及重要实时数据\r', '1', '1497429920', '1497429920', '143', 'normal'), ('11181', 'file', '11180', '/admin/dashboard/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '136', 'normal'), ('11182', 'file', '11180', '/admin/dashboard/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '135', 'normal'), ('11183', 'file', '11180', '/admin/dashboard/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '134', 'normal'), ('11184', 'file', '11180', '/admin/dashboard/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '133', 'normal'), ('11185', 'file', '11180', '/admin/dashboard/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '132', 'normal'), ('11186', 'file', '0', '/admin/page', '单页管理', 'fa fa-tags', '', '用于管理普通的单页面,通常用于关于我们、联系我们、商务合作等单一页面\r\n', '1', '1497429920', '1497430149', '125', 'normal'), ('11187', 'file', '11186', '/admin/page/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '130', 'normal'), ('11188', 'file', '11186', '/admin/page/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '129', 'normal'), ('11189', 'file', '11186', '/admin/page/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '128', 'normal'), ('11190', 'file', '11186', '/admin/page/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '127', 'normal'), ('11191', 'file', '11186', '/admin/page/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '126', 'normal'), ('11192', 'file', '0', '/admin/version', '版本管理', 'fa fa-file-text-o', '', '', '1', '1497429920', '1497430600', '27', 'normal'), ('11193', 'file', '11192', '/admin/version/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '124', 'normal'), ('11194', 'file', '11192', '/admin/version/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '123', 'normal'), ('11195', 'file', '11192', '/admin/version/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '122', 'normal'), ('11196', 'file', '11192', '/admin/version/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '121', 'normal'), ('11197', 'file', '11192', '/admin/version/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '120', 'normal'), ('11198', 'file', '0', '/admin/auth', '权限管理', 'fa fa-group', '', '', '1', '1497429920', '1497430092', '99', 'normal'), ('11199', 'file', '11198', '/admin/auth/admin', '管理员管理', 'fa fa-user', '', '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成', '1', '1497429920', '1497430320', '118', 'normal'), ('11200', 'file', '11199', '/admin/auth/admin/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '117', 'normal'), ('11201', 'file', '11199', '/admin/auth/admin/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '116', 'normal'), ('11202', 'file', '11199', '/admin/auth/admin/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '115', 'normal'), ('11203', 'file', '11199', '/admin/auth/admin/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '114', 'normal'), ('11204', 'file', '11198', '/admin/auth/adminlog', '管理员日志', 'fa fa-list-alt', '', '管理员可以查看自己所拥有的权限的管理员日志', '1', '1497429920', '1497430307', '113', 'normal'), ('11205', 'file', '11204', '/admin/auth/adminlog/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '112', 'normal'), ('11206', 'file', '11204', '/admin/auth/adminlog/detail', '详情', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '111', 'normal'), ('11207', 'file', '11204', '/admin/auth/adminlog/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '110', 'normal'), ('11208', 'file', '11198', '/admin/auth/group', '角色组', 'fa fa-group', '', '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别下级的角色组或管理员', '1', '1497429920', '1497429920', '109', 'normal'), ('11209', 'file', '11208', '/admin/auth/group/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '108', 'normal'), ('11210', 'file', '11208', '/admin/auth/group/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '107', 'normal'), ('11211', 'file', '11208', '/admin/auth/group/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '106', 'normal'), ('11212', 'file', '11208', '/admin/auth/group/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '105', 'normal'), ('11213', 'file', '11198', '/admin/auth/rule', '规则管理', 'fa fa-bars', '', '规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过控制台进行生成规则节点', '1', '1497429920', '1497430581', '104', 'normal'), ('11214', 'file', '11213', '/admin/auth/rule/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '103', 'normal'), ('11215', 'file', '11213', '/admin/auth/rule/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '102', 'normal'), ('11216', 'file', '11213', '/admin/auth/rule/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '101', 'normal'), ('11217', 'file', '11213', '/admin/auth/rule/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '100', 'normal'), ('11218', 'file', '0', '/admin/example', '示例管理', 'fa fa-magic', '', '', '1', '1497429920', '1497430123', '131', 'normal'), ('11219', 'file', '11218', '/admin/example/bootstraptable', '表格完整示例', 'fa fa-table', '', '在使用Bootstrap-table中的常用方式,更多使用方式可查看:http://bootstrap-table.wenzhixin.net.cn/zh-cn/', '1', '1497429920', '1497429920', '98', 'normal'), ('11220', 'file', '11219', '/admin/example/bootstraptable/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '97', 'normal'), ('11221', 'file', '11219', '/admin/example/bootstraptable/detail', '详情', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '96', 'normal'), ('11222', 'file', '11219', '/admin/example/bootstraptable/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '95', 'normal'), ('11223', 'file', '11219', '/admin/example/bootstraptable/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '94', 'normal'), ('11224', 'file', '11219', '/admin/example/bootstraptable/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '93', 'normal'), ('11225', 'file', '11219', '/admin/example/bootstraptable/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '92', 'normal'), ('11226', 'file', '11218', '/admin/example/colorbadge', '彩色角标', 'fa fa-table', '', '在JS端控制角标的显示与隐藏,请注意左侧菜单栏角标的数值变化', '1', '1497429920', '1497429920', '91', 'normal'), ('11227', 'file', '11226', '/admin/example/colorbadge/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '90', 'normal'), ('11228', 'file', '11226', '/admin/example/colorbadge/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '89', 'normal'), ('11229', 'file', '11226', '/admin/example/colorbadge/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '88', 'normal'), ('11230', 'file', '11226', '/admin/example/colorbadge/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '87', 'normal'), ('11231', 'file', '11226', '/admin/example/colorbadge/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '86', 'normal'), ('11232', 'file', '11218', '/admin/example/controllerjump', '控制器间跳转', 'fa fa-table', '', 'FastAdmin支持在控制器间跳转,点击后将切换到另外一个TAB中,无需刷新当前页面', '1', '1497429920', '1497429920', '85', 'normal'), ('11233', 'file', '11232', '/admin/example/controllerjump/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '84', 'normal'), ('11234', 'file', '11232', '/admin/example/controllerjump/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '83', 'normal'), ('11235', 'file', '11232', '/admin/example/controllerjump/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '82', 'normal'), ('11236', 'file', '11232', '/admin/example/controllerjump/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '81', 'normal'), ('11237', 'file', '11232', '/admin/example/controllerjump/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '80', 'normal'), ('11238', 'file', '11218', '/admin/example/cxselect', '多级联动', 'fa fa-table', '', 'FastAdmin使用了jQuery-cxselect实现多级联动,更多文档请参考https://github.com/karsonzhang/cxSelect', '1', '1497429920', '1497429920', '79', 'normal'), ('11239', 'file', '11238', '/admin/example/cxselect/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '78', 'normal'), ('11240', 'file', '11238', '/admin/example/cxselect/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '77', 'normal'), ('11241', 'file', '11238', '/admin/example/cxselect/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '76', 'normal'), ('11242', 'file', '11238', '/admin/example/cxselect/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '75', 'normal'), ('11243', 'file', '11238', '/admin/example/cxselect/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '74', 'normal'), ('11244', 'file', '11218', '/admin/example/multitable', '多表格示例', 'fa fa-table', '', '当一个页面上存在多个Bootstrap-table时该如何控制按钮和表格', '1', '1497429920', '1497429920', '73', 'normal'), ('11245', 'file', '11244', '/admin/example/multitable/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '72', 'normal'), ('11246', 'file', '11244', '/admin/example/multitable/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '71', 'normal'), ('11247', 'file', '11244', '/admin/example/multitable/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '70', 'normal'), ('11248', 'file', '11244', '/admin/example/multitable/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '69', 'normal'), ('11249', 'file', '11244', '/admin/example/multitable/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '68', 'normal'), ('11250', 'file', '11218', '/admin/example/relationmodel', '多模型关联', 'fa fa-table', '', '当使用到关联模型时需要重载index方法', '1', '1497429920', '1497429920', '67', 'normal'), ('11251', 'file', '11250', '/admin/example/relationmodel/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '66', 'normal'), ('11252', 'file', '11250', '/admin/example/relationmodel/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '65', 'normal'), ('11253', 'file', '11250', '/admin/example/relationmodel/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '64', 'normal'), ('11254', 'file', '11250', '/admin/example/relationmodel/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '63', 'normal'), ('11255', 'file', '11250', '/admin/example/relationmodel/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '62', 'normal'), ('11256', 'file', '0', '/admin/general', '常规管理', 'fa fa-cogs', '', '', '1', '1497429920', '1497430169', '137', 'normal'), ('11257', 'file', '11256', '/admin/general/attachment', '附件管理', 'fa fa-file-image-o', '', '主要用于管理上传到又拍云的数据或上传至本服务的上传数据\r\n', '1', '1497429920', '1497430699', '53', 'normal'), ('11258', 'file', '11257', '/admin/general/attachment/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '59', 'normal'), ('11259', 'file', '11257', '/admin/general/attachment/select', '选择附件', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '58', 'normal'), ('11260', 'file', '11257', '/admin/general/attachment/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '57', 'normal'), ('11261', 'file', '11257', '/admin/general/attachment/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '56', 'normal'), ('11262', 'file', '11257', '/admin/general/attachment/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '55', 'normal'), ('11263', 'file', '11257', '/admin/general/attachment/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '54', 'normal'), ('11264', 'file', '11256', '/admin/general/config', '系统配置', 'fa fa-cog', '', '', '1', '1497429920', '1497430683', '60', 'normal'), ('11265', 'file', '11264', '/admin/general/config/index', 'index', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '52', 'normal'), ('11266', 'file', '11264', '/admin/general/config/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '51', 'normal'), ('11267', 'file', '11264', '/admin/general/config/edit', 'edit', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '50', 'normal'), ('11268', 'file', '11264', '/admin/general/config/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '49', 'normal'), ('11269', 'file', '11264', '/admin/general/config/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '48', 'normal'), ('11270', 'file', '11256', '/admin/general/crontab', '定时任务', 'fa fa-tasks', '', '类似于Linux的Crontab定时任务,可以按照设定的时间进行任务的执行,目前仅支持三种任务:请求URL、执行SQL、执行Shell', '1', '1497429920', '1497429920', '47', 'normal'), ('11271', 'file', '11270', '/admin/general/crontab/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '46', 'normal'), ('11272', 'file', '11270', '/admin/general/crontab/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '45', 'normal'), ('11273', 'file', '11270', '/admin/general/crontab/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '44', 'normal'), ('11274', 'file', '11270', '/admin/general/crontab/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '43', 'normal'), ('11275', 'file', '11270', '/admin/general/crontab/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '42', 'normal'), ('11276', 'file', '11256', '/admin/general/database', '数据库管理', 'fa fa-database', '', '可在线进行一些简单的数据库表优化或修复,查看表结构和数据。也可以进行SQL语句的操作', '1', '1497429920', '1497429920', '41', 'normal'), ('11277', 'file', '11276', '/admin/general/database/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '40', 'normal'), ('11278', 'file', '11276', '/admin/general/database/query', 'SQL查询', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '39', 'normal'), ('11279', 'file', '11276', '/admin/general/database/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '38', 'normal'), ('11280', 'file', '11276', '/admin/general/database/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '37', 'normal'), ('11281', 'file', '11276', '/admin/general/database/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '36', 'normal'), ('11282', 'file', '11276', '/admin/general/database/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '35', 'normal'), ('11283', 'file', '11256', '/admin/general/profile', '个人配置', 'fa fa-user\r', '', '', '1', '1497429920', '1497429920', '34', 'normal'), ('11284', 'file', '11283', '/admin/general/profile/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '33', 'normal'), ('11285', 'file', '11283', '/admin/general/profile/update', '更新个人信息', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '32', 'normal'), ('11286', 'file', '11283', '/admin/general/profile/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '31', 'normal'), ('11287', 'file', '11283', '/admin/general/profile/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '30', 'normal'), ('11288', 'file', '11283', '/admin/general/profile/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '29', 'normal'), ('11289', 'file', '11283', '/admin/general/profile/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '28', 'normal'), ('11290', 'file', '0', '/admin/wechat', '微信管理', 'fa fa-wechat', '', '', '1', '1497429920', '1497430064', '61', 'normal'), ('11291', 'file', '11290', '/admin/wechat/autoreply', '微信自动回复管理', 'fa fa-reply-all', '用户在微信公众号中输入特定的文字后,服务器响应不同的事件', '', '1', '1497429920', '1499761628', '26', 'normal'), ('11292', 'file', '11291', '/admin/wechat/autoreply/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '25', 'normal'), ('11293', 'file', '11291', '/admin/wechat/autoreply/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '24', 'normal'), ('11294', 'file', '11291', '/admin/wechat/autoreply/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '23', 'normal'), ('11295', 'file', '11291', '/admin/wechat/autoreply/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '22', 'normal'), ('11296', 'file', '11291', '/admin/wechat/autoreply/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '21', 'normal'), ('11297', 'file', '11290', '/admin/wechat/config', '微信配置管理', 'fa fa-cog', '', '', '1', '1497429920', '1497430632', '20', 'normal'), ('11298', 'file', '11297', '/admin/wechat/config/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '19', 'normal'), ('11299', 'file', '11297', '/admin/wechat/config/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '18', 'normal'), ('11300', 'file', '11297', '/admin/wechat/config/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '17', 'normal'), ('11301', 'file', '11297', '/admin/wechat/config/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '16', 'normal'), ('11302', 'file', '11297', '/admin/wechat/config/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '15', 'normal'), ('11303', 'file', '11290', '/admin/wechat/menu', '菜单管理', 'fa fa-bars', '', '', '1', '1497429920', '1497430652', '14', 'normal'), ('11304', 'file', '11303', '/admin/wechat/menu/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '13', 'normal'), ('11305', 'file', '11303', '/admin/wechat/menu/edit', '修改', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '12', 'normal'), ('11306', 'file', '11303', '/admin/wechat/menu/sync', '同步', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '11', 'normal'), ('11307', 'file', '11303', '/admin/wechat/menu/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '10', 'normal'), ('11308', 'file', '11303', '/admin/wechat/menu/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '9', 'normal'), ('11309', 'file', '11303', '/admin/wechat/menu/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '8', 'normal'), ('11310', 'file', '11290', '/admin/wechat/response', '资源管理', 'fa fa-list-alt', '', '', '1', '1497429920', '1497429920', '7', 'normal'), ('11311', 'file', '11310', '/admin/wechat/response/select', '选择素材', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '6', 'normal'), ('11312', 'file', '11310', '/admin/wechat/response/add', '添加', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '5', 'normal'), ('11313', 'file', '11310', '/admin/wechat/response/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '4', 'normal'), ('11314', 'file', '11310', '/admin/wechat/response/index', '查看', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '3', 'normal'), ('11315', 'file', '11310', '/admin/wechat/response/del', '删除', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '2', 'normal'), ('11316', 'file', '11310', '/admin/wechat/response/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497429920', '1497429920', '1', 'normal'), ('11317', 'file', '11218', '/admin/example/tabletemplate', '表格模板示例', 'fa fa-table', '', '可以通过使用表格模板将表格中的行渲染成一样的展现方式,基于此功能可以任意定制自己想要的展示列表', '1', '1497968508', '1497968508', '0', 'normal'), ('11318', 'file', '11317', '/admin/example/tabletemplate/index', '查看', 'fa fa-circle-o', '', '', '0', '1497968508', '1497968508', '0', 'normal'), ('11319', 'file', '11317', '/admin/example/tabletemplate/detail', '详情', 'fa fa-circle-o', '', '', '0', '1497968508', '1497968508', '0', 'normal'), ('11320', 'file', '11317', '/admin/example/tabletemplate/add', '添加', 'fa fa-circle-o', '', '', '0', '1497968508', '1497968508', '0', 'normal'), ('11321', 'file', '11317', '/admin/example/tabletemplate/edit', '编辑', 'fa fa-circle-o', '', '', '0', '1497968508', '1497968508', '0', 'normal'), ('11322', 'file', '11317', '/admin/example/tabletemplate/del', '删除', 'fa fa-circle-o', '', '', '0', '1497968508', '1497968508', '0', 'normal'), ('11323', 'file', '11317', '/admin/example/tabletemplate/multi', '批量更新', 'fa fa-circle-o', '', '', '0', '1497968508', '1497968508', '0', 'normal'); +INSERT INTO `fa_auth_rule` VALUES (1, 'file', 0, 'dashboard', 'Dashboard', 'fa fa-dashboard', '', 'Dashboard tips', 1, 1497429920, 1497429920, 143, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (2, 'file', 0, 'general', 'General', 'fa fa-cogs', '', '', 1, 1497429920, 1497430169, 137, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (3, 'file', 0, 'category', 'Category', 'fa fa-leaf', '', 'Category tips', 1, 1497429920, 1497429920, 119, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (4, 'file', 0, 'addon', 'Addon', 'fa fa-rocket', '', 'Addon tips', 1, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (5, 'file', 0, 'auth', 'Auth', 'fa fa-group', '', '', 1, 1497429920, 1497430092, 99, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (6, 'file', 2, 'general/config', 'Config', 'fa fa-cog', '', 'Config tips', 1, 1497429920, 1497430683, 60, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (7, 'file', 2, 'general/attachment', 'Attachment', 'fa fa-file-image-o', '', 'Attachment tips', 1, 1497429920, 1497430699, 53, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (8, 'file', 2, 'general/profile', 'Profile', 'fa fa-user', '', '', 1, 1497429920, 1497429920, 34, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (9, 'file', 5, 'auth/admin', 'Admin', 'fa fa-user', '', 'Admin tips', 1, 1497429920, 1497430320, 118, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (10, 'file', 5, 'auth/adminlog', 'Admin log', 'fa fa-list-alt', '', 'Admin log tips', 1, 1497429920, 1497430307, 113, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (11, 'file', 5, 'auth/group', 'Group', 'fa fa-group', '', 'Group tips', 1, 1497429920, 1497429920, 109, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (12, 'file', 5, 'auth/rule', 'Rule', 'fa fa-bars', '', 'Rule tips', 1, 1497429920, 1497430581, 104, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (13, 'file', 1, 'dashboard/index', 'View', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 136, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (14, 'file', 1, 'dashboard/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 135, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (15, 'file', 1, 'dashboard/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 133, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (16, 'file', 1, 'dashboard/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 134, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (17, 'file', 1, 'dashboard/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 132, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (18, 'file', 6, 'general/config/index', 'View', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 52, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (19, 'file', 6, 'general/config/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 51, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (20, 'file', 6, 'general/config/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 50, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (21, 'file', 6, 'general/config/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 49, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (22, 'file', 6, 'general/config/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 48, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (23, 'file', 7, 'general/attachment/index', 'View', 'fa fa-circle-o', '', 'Attachment tips', 0, 1497429920, 1497429920, 59, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (24, 'file', 7, 'general/attachment/select', 'Select attachment', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 58, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (25, 'file', 7, 'general/attachment/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 57, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (26, 'file', 7, 'general/attachment/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 56, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (27, 'file', 7, 'general/attachment/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 55, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (28, 'file', 7, 'general/attachment/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 54, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (29, 'file', 8, 'general/profile/index', 'View', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 33, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (30, 'file', 8, 'general/profile/update', 'Update profile', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 32, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (31, 'file', 8, 'general/profile/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 31, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (32, 'file', 8, 'general/profile/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 30, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (33, 'file', 8, 'general/profile/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 29, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (34, 'file', 8, 'general/profile/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 28, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (35, 'file', 3, 'category/index', 'View', 'fa fa-circle-o', '', 'Category tips', 0, 1497429920, 1497429920, 142, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (36, 'file', 3, 'category/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 141, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (37, 'file', 3, 'category/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 140, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (38, 'file', 3, 'category/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 139, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (39, 'file', 3, 'category/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 138, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (40, 'file', 9, 'auth/admin/index', 'View', 'fa fa-circle-o', '', 'Admin tips', 0, 1497429920, 1497429920, 117, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (41, 'file', 9, 'auth/admin/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 116, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (42, 'file', 9, 'auth/admin/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 115, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (43, 'file', 9, 'auth/admin/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 114, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (44, 'file', 10, 'auth/adminlog/index', 'View', 'fa fa-circle-o', '', 'Admin log tips', 0, 1497429920, 1497429920, 112, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (45, 'file', 10, 'auth/adminlog/detail', 'Detail', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 111, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (46, 'file', 10, 'auth/adminlog/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 110, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (47, 'file', 11, 'auth/group/index', 'View', 'fa fa-circle-o', '', 'Group tips', 0, 1497429920, 1497429920, 108, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (48, 'file', 11, 'auth/group/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 107, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (49, 'file', 11, 'auth/group/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 106, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (50, 'file', 11, 'auth/group/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 105, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (51, 'file', 12, 'auth/rule/index', 'View', 'fa fa-circle-o', '', 'Rule tips', 0, 1497429920, 1497429920, 103, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (52, 'file', 12, 'auth/rule/add', 'Add', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 102, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (53, 'file', 12, 'auth/rule/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 101, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (54, 'file', 12, 'auth/rule/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1497429920, 1497429920, 100, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (55, 'file', 4, 'addon/index', 'View', 'fa fa-circle-o', '', 'Addon tips', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (56, 'file', 4, 'addon/add', 'Add', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (57, 'file', 4, 'addon/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (58, 'file', 4, 'addon/del', 'Delete', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (59, 'file', 4, 'addon/downloaded', 'Local addon', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (60, 'file', 4, 'addon/state', 'Update state', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (63, 'file', 4, 'addon/config', 'Setting', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (64, 'file', 4, 'addon/refresh', 'Refresh', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (65, 'file', 4, 'addon/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1502035509, 1502035509, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (66, 'file', 0, 'user', 'User', 'fa fa-list', '', '', 1, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (67, 'file', 66, 'user/user', 'User', 'fa fa-user', '', '', 1, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (68, 'file', 67, 'user/user/index', 'View', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (69, 'file', 67, 'user/user/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (70, 'file', 67, 'user/user/add', 'Add', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (71, 'file', 67, 'user/user/del', 'Del', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (72, 'file', 67, 'user/user/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (73, 'file', 66, 'user/group', 'User group', 'fa fa-users', '', '', 1, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (74, 'file', 73, 'user/group/add', 'Add', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (75, 'file', 73, 'user/group/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (76, 'file', 73, 'user/group/index', 'View', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (77, 'file', 73, 'user/group/del', 'Del', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (78, 'file', 73, 'user/group/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (79, 'file', 66, 'user/rule', 'User rule', 'fa fa-circle-o', '', '', 1, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (80, 'file', 79, 'user/rule/index', 'View', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (81, 'file', 79, 'user/rule/del', 'Del', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (82, 'file', 79, 'user/rule/add', 'Add', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (83, 'file', 79, 'user/rule/edit', 'Edit', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (84, 'file', 79, 'user/rule/multi', 'Multi', 'fa fa-circle-o', '', '', 0, 1516374729, 1516374729, 0, 'normal'); COMMIT; -- ---------------------------- --- Table structure for `fa_category` +-- Table structure for fa_category -- ---------------------------- DROP TABLE IF EXISTS `fa_category`; CREATE TABLE `fa_category` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID', - `type` varchar(30) NOT NULL DEFAULT '' COMMENT '栏目类型', - `name` varchar(30) NOT NULL DEFAULT '', - `nickname` varchar(50) NOT NULL DEFAULT '', - `flag` set('hot','index','recommend') NOT NULL DEFAULT '', - `image` varchar(100) NOT NULL DEFAULT '' COMMENT '图片', - `keywords` varchar(255) NOT NULL DEFAULT '' COMMENT '关键字', - `description` varchar(255) NOT NULL DEFAULT '' COMMENT '描述', - `diyname` varchar(30) NOT NULL DEFAULT '' COMMENT '自定义名称', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + `type` varchar(30) DEFAULT '' COMMENT '栏目类型', + `name` varchar(30) DEFAULT '', + `nickname` varchar(50) DEFAULT '', + `flag` set('hot','index','recommend') DEFAULT '', + `image` varchar(100) DEFAULT '' COMMENT '图片', + `keywords` varchar(255) DEFAULT '' COMMENT '关键字', + `description` varchar(255) DEFAULT '' COMMENT '描述', + `diyname` varchar(30) DEFAULT '' COMMENT '自定义名称', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', + `status` varchar(30) DEFAULT '' COMMENT '状态', PRIMARY KEY (`id`), KEY `weigh` (`weigh`,`id`), KEY `pid` (`pid`) -) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='分类表'; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='分类表'; -- ---------------------------- --- Records of `fa_category` +-- Records of fa_category -- ---------------------------- BEGIN; -INSERT INTO `fa_category` VALUES ('1', '0', 'page', '官方新闻', 'news', 'recommend', '/assets/img/qrcode.png', '', '', 'news', '1495262190', '1495262190', '1', 'normal'), ('2', '0', 'page', '移动应用', 'mobileapp', 'hot', '/assets/img/qrcode.png', '', '', 'mobileapp', '1495262244', '1495262244', '2', 'normal'), ('3', '2', 'page', '微信公众号', 'wechatpublic', 'index', '/assets/img/qrcode.png', '', '', 'wechatpublic', '1495262288', '1495262288', '3', 'normal'), ('4', '2', 'page', 'Android开发', 'android', 'recommend', '/assets/img/qrcode.png', '', '', 'android', '1495262317', '1495262317', '4', 'normal'), ('5', '0', 'page', '软件产品', 'software', 'recommend', '/assets/img/qrcode.png', '', '', 'software', '1495262336', '1499681850', '5', 'normal'), ('6', '5', 'page', '网站建站', 'website', 'recommend', '/assets/img/qrcode.png', '', '', 'website', '1495262357', '1495262357', '6', 'normal'), ('7', '5', 'page', '企业管理软件', 'company', 'index', '/assets/img/qrcode.png', '', '', 'company', '1495262391', '1495262391', '7', 'normal'), ('8', '6', 'page', 'PC端', 'website-pc', 'recommend', '/assets/img/qrcode.png', '', '', 'website-pc', '1495262424', '1495262424', '8', 'normal'), ('9', '6', 'page', '移动端', 'website-mobile', 'recommend', '/assets/img/qrcode.png', '', '', 'website-mobile', '1495262456', '1495262456', '9', 'normal'), ('10', '7', 'page', 'CRM系统 ', 'company-crm', 'recommend', '/assets/img/qrcode.png', '', '', 'company-crm', '1495262487', '1495262487', '10', 'normal'), ('11', '7', 'page', 'SASS平台软件', 'company-sass', 'recommend', '/assets/img/qrcode.png', '', '', 'company-sass', '1495262515', '1495262515', '11', 'normal'), ('12', '0', 'test', '测试1', 'test1', 'recommend', '/assets/img/qrcode.png', '', '', 'test1', '1497015727', '1497015727', '12', 'normal'), ('13', '0', 'test', '测试2', 'test2', 'recommend', '/assets/img/qrcode.png', '', '', 'test2', '1497015738', '1497015738', '13', 'normal'); +INSERT INTO `fa_category` VALUES (1, 0, 'page', '官方新闻', 'news', 'recommend', '/assets/img/qrcode.png', '', '', 'news', 1495262190, 1495262190, 1, 'normal'); +INSERT INTO `fa_category` VALUES (2, 0, 'page', '移动应用', 'mobileapp', 'hot', '/assets/img/qrcode.png', '', '', 'mobileapp', 1495262244, 1495262244, 2, 'normal'); +INSERT INTO `fa_category` VALUES (3, 2, 'page', '微信公众号', 'wechatpublic', 'index', '/assets/img/qrcode.png', '', '', 'wechatpublic', 1495262288, 1495262288, 3, 'normal'); +INSERT INTO `fa_category` VALUES (4, 2, 'page', 'Android开发', 'android', 'recommend', '/assets/img/qrcode.png', '', '', 'android', 1495262317, 1495262317, 4, 'normal'); +INSERT INTO `fa_category` VALUES (5, 0, 'page', '软件产品', 'software', 'recommend', '/assets/img/qrcode.png', '', '', 'software', 1495262336, 1499681850, 5, 'normal'); +INSERT INTO `fa_category` VALUES (6, 5, 'page', '网站建站', 'website', 'recommend', '/assets/img/qrcode.png', '', '', 'website', 1495262357, 1495262357, 6, 'normal'); +INSERT INTO `fa_category` VALUES (7, 5, 'page', '企业管理软件', 'company', 'index', '/assets/img/qrcode.png', '', '', 'company', 1495262391, 1495262391, 7, 'normal'); +INSERT INTO `fa_category` VALUES (8, 6, 'page', 'PC端', 'website-pc', 'recommend', '/assets/img/qrcode.png', '', '', 'website-pc', 1495262424, 1495262424, 8, 'normal'); +INSERT INTO `fa_category` VALUES (9, 6, 'page', '移动端', 'website-mobile', 'recommend', '/assets/img/qrcode.png', '', '', 'website-mobile', 1495262456, 1495262456, 9, 'normal'); +INSERT INTO `fa_category` VALUES (10, 7, 'page', 'CRM系统 ', 'company-crm', 'recommend', '/assets/img/qrcode.png', '', '', 'company-crm', 1495262487, 1495262487, 10, 'normal'); +INSERT INTO `fa_category` VALUES (11, 7, 'page', 'SASS平台软件', 'company-sass', 'recommend', '/assets/img/qrcode.png', '', '', 'company-sass', 1495262515, 1495262515, 11, 'normal'); +INSERT INTO `fa_category` VALUES (12, 0, 'test', '测试1', 'test1', 'recommend', '/assets/img/qrcode.png', '', '', 'test1', 1497015727, 1497015727, 12, 'normal'); +INSERT INTO `fa_category` VALUES (13, 0, 'test', '测试2', 'test2', 'recommend', '/assets/img/qrcode.png', '', '', 'test2', 1497015738, 1497015738, 13, 'normal'); COMMIT; -- ---------------------------- --- Table structure for `fa_config` +-- Table structure for fa_config -- ---------------------------- DROP TABLE IF EXISTS `fa_config`; CREATE TABLE `fa_config` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(30) NOT NULL DEFAULT '' COMMENT '变量名', - `group` varchar(30) NOT NULL DEFAULT '' COMMENT '分组', - `title` varchar(100) NOT NULL DEFAULT '' COMMENT '变量标题', - `tip` varchar(100) NOT NULL DEFAULT '' COMMENT '变量描述', - `type` varchar(30) NOT NULL DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file', - `value` text NOT NULL COMMENT '变量值', - `content` text NOT NULL COMMENT '变量字典数据', - `rule` varchar(100) NOT NULL DEFAULT '' COMMENT '验证规则', - `extend` varchar(255) NOT NULL DEFAULT '' COMMENT '扩展属性', + `name` varchar(30) DEFAULT '' COMMENT '变量名', + `group` varchar(30) DEFAULT '' COMMENT '分组', + `title` varchar(100) DEFAULT '' COMMENT '变量标题', + `tip` varchar(100) DEFAULT '' COMMENT '变量描述', + `type` varchar(30) DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file', + `value` text COMMENT '变量值', + `content` text COMMENT '变量字典数据', + `rule` varchar(100) DEFAULT '' COMMENT '验证规则', + `extend` varchar(255) DEFAULT '' COMMENT '扩展属性', + `setting` varchar(255) DEFAULT '' COMMENT '配置', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='系统配置'; +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='系统配置'; -- ---------------------------- --- Records of `fa_config` +-- Records of fa_config -- ---------------------------- BEGIN; -INSERT INTO `fa_config` VALUES ('1', 'name', 'basic', '站点名称', '请填写站点名称', 'string', 'FastAdmin', '', 'required', ''), ('2', 'beian', 'basic', '备案号', '粤ICP备15054802号-4', 'string', '', '', '', ''), ('3', 'cdnurl', 'basic', 'CDN地址', '如果使用CDN云储存请配置该值', 'string', '', '', '', ''), ('4', 'version', 'basic', '版本号', '如果静态资源有变动请重新配置该值', 'string', '1.0.1', '', 'required', ''), ('5', 'timezone', 'basic', '时区', '', 'string', 'Asia/Shanghai', '', 'required', ''), ('6', 'forbiddenip', 'basic', '禁止访问IP', '一行一条记录', 'text', '', '', '', ''), ('7', 'languages', 'basic', '模块语言', '', 'array', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', ''), ('8', 'fixedpage', 'basic', '后台默认页', '请尽量输入左侧菜单栏存在的链接', 'string', 'dashboard', '', 'required', ''), ('9', 'categorytype', 'dictionary', '分类类型', '', 'array', '{\"default\":\"默认\",\"page\":\"单页\",\"article\":\"文章\",\"test\":\"测试\"}', '', '', ''), ('10', 'configgroup', 'dictionary', '配置分组', '', 'array', '{\"basic\":\"基础配置\",\"email\":\"邮件配置\",\"dictionary\":\"字典配置\",\"user\":\"会员配置\",\"example\":\"示例分组\"}', '', '', ''), ('11', 'mail_type', 'email', '邮件发送方式', '选择邮件发送方式', 'select', '1', '[\"请选择\",\"SMTP\",\"mail()函数\"]', '', ''), ('12', 'mail_smtp_host', 'email', 'SMTP[服务器]', '错误的配置发送邮件会导致服务器超时', 'string', 'smtp.qq.com', '', '', ''), ('13', 'mail_smtp_port', 'email', 'SMTP[端口]', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '465', '', '', ''), ('14', 'mail_smtp_user', 'email', 'SMTP[用户名]', '(填写完整用户名)', 'string', '10000', '', '', ''), ('15', 'mail_smtp_pass', 'email', 'SMTP[密码]', '(填写您的密码)', 'string', 'password', '', '', ''), ('16', 'mail_verify_type', 'email', 'SMTP验证方式', '(SMTP验证方式[推荐SSL])', 'select', '2', '[\"无\",\"TLS\",\"SSL\"]', '', ''), ('17', 'mail_from', 'email', '发件人邮箱', '', 'string', '10000@qq.com', '', '', ''); +INSERT INTO `fa_config` VALUES (1, 'name', 'basic', 'Site name', '请填写站点名称', 'string', '我的网站', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (2, 'beian', 'basic', 'Beian', '粤ICP备15000000号-1', 'string', '', '', '', '', ''); +INSERT INTO `fa_config` VALUES (3, 'cdnurl', 'basic', 'Cdn url', '如果全站静态资源使用第三方云储存请配置该值', 'string', '', '', '', '', ''); +INSERT INTO `fa_config` VALUES (4, 'version', 'basic', 'Version', '如果静态资源有变动请重新配置该值', 'string', '1.0.1', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (5, 'timezone', 'basic', 'Timezone', '', 'string', 'Asia/Shanghai', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (6, 'forbiddenip', 'basic', 'Forbidden ip', '一行一条记录', 'text', '', '', '', '', ''); +INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '请尽量输入左侧菜单栏存在的链接', 'string', 'dashboard', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '', ''); +INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '', ''); +INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '1', '[\"请选择\",\"SMTP\",\"Mail\"]', '', '', ''); +INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', 'smtp.qq.com', '', '', '', ''); +INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '465', '', '', '', ''); +INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '10000', '', '', '', ''); +INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码)', 'string', 'password', '', '', '', ''); +INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', '(SMTP验证方式[推荐SSL])', 'select', '2', '[\"无\",\"TLS\",\"SSL\"]', '', '', ''); +INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '10000@qq.com', '', '', '', ''); COMMIT; -- ---------------------------- --- Table structure for `fa_crontab` +-- Table structure for fa_ems -- ---------------------------- -DROP TABLE IF EXISTS `fa_crontab`; -CREATE TABLE `fa_crontab` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `type` varchar(10) NOT NULL DEFAULT '' COMMENT '事件类型', - `title` varchar(100) NOT NULL DEFAULT '' COMMENT '事件标题', - `content` text NOT NULL COMMENT '事件内容', - `schedule` varchar(100) NOT NULL DEFAULT '' COMMENT 'Crontab格式', - `sleep` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '延迟秒数执行', - `maximums` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大执行次数 0为不限', - `executes` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '已经执行的次数', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `begintime` int(10) NOT NULL DEFAULT '0' COMMENT '开始时间', - `endtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '结束时间', - `executetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最后执行时间', - `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', - `status` enum('completed','expired','hidden','normal') NOT NULL DEFAULT 'normal' COMMENT '状态', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='定时任务表'; +DROP TABLE IF EXISTS `fa_ems`; +CREATE TABLE `fa_ems` ( + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `event` varchar(30) DEFAULT '' COMMENT '事件', + `email` varchar(100) DEFAULT '' COMMENT '邮箱', + `code` varchar(10) DEFAULT '' COMMENT '验证码', + `times` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '验证次数', + `ip` varchar(30) DEFAULT '' COMMENT 'IP', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='邮箱验证码表'; -- ---------------------------- --- Records of `fa_crontab` --- ---------------------------- -BEGIN; -INSERT INTO `fa_crontab` VALUES ('1', 'url', '请求FastAdmin', 'http://www.fastadmin.net', '* * * * *', '0', '0', '1063', '1497070825', '1499788320', '1483200000', '1546272000', '1499788320', '1', 'normal'), ('2', 'sql', '查询一条SQL', 'SELECT 1;', '* * * * *', '0', '0', '1063', '1497071095', '1499788320', '1483200000', '1546272000', '1499788320', '2', 'normal'); -COMMIT; - +-- Table structure for fa_sms -- ---------------------------- --- Table structure for `fa_page` --- ---------------------------- -DROP TABLE IF EXISTS `fa_page`; -CREATE TABLE `fa_page` ( +DROP TABLE IF EXISTS `fa_sms`; +CREATE TABLE `fa_sms` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `category_id` int(10) NOT NULL DEFAULT '0' COMMENT '分类ID', - `title` varchar(50) NOT NULL DEFAULT '' COMMENT '标题', - `keywords` varchar(255) NOT NULL DEFAULT '' COMMENT '关键字', - `flag` set('hot','index','recommend') NOT NULL DEFAULT '' COMMENT '标志', - `image` varchar(255) NOT NULL DEFAULT '' COMMENT '头像', - `content` text NOT NULL COMMENT '内容', - `icon` varchar(50) NOT NULL DEFAULT '' COMMENT '图标', - `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '点击', - `comments` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '评论', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', + `event` varchar(30) DEFAULT '' COMMENT '事件', + `mobile` varchar(20) DEFAULT '' COMMENT '手机号', + `code` varchar(10) DEFAULT '' COMMENT '验证码', + `times` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '验证次数', + `ip` varchar(30) DEFAULT '' COMMENT 'IP', + `createtime` int(10) unsigned DEFAULT '0' COMMENT '创建时间', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='单页表'; - --- ---------------------------- --- Records of `fa_page` --- ---------------------------- -BEGIN; -INSERT INTO `fa_page` VALUES ('1', '4', 'test', 'test', 'recommend', '/assets/img/qrcode.png', '

      test

      ', '', '0', '0', '1499769482', '1499769482', '0', 'normal'); -COMMIT; +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='短信验证码表'; -- ---------------------------- --- Table structure for `fa_test` +-- Table structure for fa_test -- ---------------------------- DROP TABLE IF EXISTS `fa_test`; CREATE TABLE `fa_test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `admin_id` int(10) NOT NULL DEFAULT '0' COMMENT '管理员ID', `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类ID(单选)', `category_ids` varchar(100) NOT NULL COMMENT '分类ID(多选)', - `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', - `user_ids` varchar(100) NOT NULL DEFAULT '' COMMENT '多会员ID', `week` enum('monday','tuesday','wednesday') NOT NULL COMMENT '星期(单选):monday=星期一,tuesday=星期二,wednesday=星期三', - `flag` set('hot','index','recommend') NOT NULL DEFAULT '' COMMENT '标志(多选):hot=热门,index=首页,recommend=推荐', + `flag` set('hot','index','recommend') DEFAULT '' COMMENT '标志(多选):hot=热门,index=首页,recommend=推荐', `genderdata` enum('male','female') NOT NULL DEFAULT 'male' COMMENT '性别(单选):male=男,female=女', `hobbydata` set('music','reading','swimming') NOT NULL COMMENT '爱好(多选):music=音乐,reading=读书,swimming=游泳', - `title` varchar(50) NOT NULL DEFAULT '' COMMENT '标题', + `title` varchar(50) DEFAULT '' COMMENT '标题', `content` text NOT NULL COMMENT '内容', - `image` varchar(100) NOT NULL DEFAULT '' COMMENT '图片', - `images` varchar(1500) NOT NULL DEFAULT '' COMMENT '图片组', - `attachfile` varchar(100) NOT NULL DEFAULT '' COMMENT '附件', - `keywords` varchar(100) NOT NULL DEFAULT '' COMMENT '关键字', - `description` varchar(255) NOT NULL DEFAULT '' COMMENT '描述', + `image` varchar(100) DEFAULT '' COMMENT '图片', + `images` varchar(1500) DEFAULT '' COMMENT '图片组', + `attachfile` varchar(100) DEFAULT '' COMMENT '附件', + `keywords` varchar(100) DEFAULT '' COMMENT '关键字', + `description` varchar(255) DEFAULT '' COMMENT '描述', + `city` varchar(100) DEFAULT '' COMMENT '省市', + `json` varchar(255) DEFAULT NULL COMMENT '配置:key=名称,value=值', `price` float(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '价格', `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '点击', `startdate` date DEFAULT NULL COMMENT '开始日期', `activitytime` datetime DEFAULT NULL COMMENT '活动时间(datetime)', `year` year(4) DEFAULT NULL COMMENT '年', `times` time DEFAULT NULL COMMENT '时间', - `refreshtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '刷新时间(int)', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + `refreshtime` int(10) DEFAULT NULL COMMENT '刷新时间(int)', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `deletetime` int(10) DEFAULT NULL COMMENT '删除时间', `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', + `switch` tinyint(1) NOT NULL DEFAULT '0' COMMENT '开关', `status` enum('normal','hidden') NOT NULL DEFAULT 'normal' COMMENT '状态', `state` enum('0','1','2') NOT NULL DEFAULT '1' COMMENT '状态值:0=禁用,1=正常,2=推荐', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='测试表'; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='测试表'; -- ---------------------------- --- Records of `fa_test` +-- Records of fa_test -- ---------------------------- BEGIN; -INSERT INTO `fa_test` VALUES ('1', '12', '12,13', '3', '3', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '

      我是测试内容

      ', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '描述', '0.00', '0', '2017-07-10', '2017-07-10 18:24:45', '2017', '18:24:45', '1499682285', '1499682526', '1499682526', '0', 'normal', '1'); +INSERT INTO `fa_test` VALUES (1, 0, 12, '12,13', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '

      我是测试内容

      ', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '描述', '广西壮族自治区/百色市/平果县', '{\"a\":\"1\",\"b\":\"2\"}', 0.00, 0, '2017-07-10', '2017-07-10 18:24:45', 2017, '18:24:45', 1499682285, 1499682526, 1499682526, NULL, 0, 1, 'normal', '1'); COMMIT; -- ---------------------------- --- Table structure for `fa_user` +-- Table structure for fa_user -- ---------------------------- DROP TABLE IF EXISTS `fa_user`; CREATE TABLE `fa_user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `username` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名', - `nickname` varchar(50) NOT NULL DEFAULT '' COMMENT '昵称', - `password` varchar(32) NOT NULL DEFAULT '' COMMENT '密码', - `salt` varchar(30) NOT NULL DEFAULT '' COMMENT '密码盐', - `email` varchar(100) NOT NULL DEFAULT '' COMMENT '电子邮箱', - `mobile` varchar(11) NOT NULL DEFAULT '' COMMENT '手机号', - `avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '头像', + `group_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '组别ID', + `username` varchar(32) DEFAULT '' COMMENT '用户名', + `nickname` varchar(50) DEFAULT '' COMMENT '昵称', + `password` varchar(32) DEFAULT '' COMMENT '密码', + `salt` varchar(30) DEFAULT '' COMMENT '密码盐', + `email` varchar(100) DEFAULT '' COMMENT '电子邮箱', + `mobile` varchar(11) DEFAULT '' COMMENT '手机号', + `avatar` varchar(255) DEFAULT '' COMMENT '头像', `level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '等级', `gender` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '性别', - `birthday` date NOT NULL COMMENT '生日', - `score` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '积分', - `prevtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上次登录时间', + `birthday` date DEFAULT NULL COMMENT '生日', + `bio` varchar(100) DEFAULT '' COMMENT '格言', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '余额', + `score` int(10) NOT NULL DEFAULT '0' COMMENT '积分', + `successions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '连续登录天数', + `maxsuccessions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '最大连续登录天数', + `prevtime` int(10) DEFAULT NULL COMMENT '上次登录时间', + `logintime` int(10) DEFAULT NULL COMMENT '登录时间', + `loginip` varchar(50) DEFAULT '' COMMENT '登录IP', `loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数', - `logintime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '登录时间', - `loginip` varchar(50) NOT NULL DEFAULT '' COMMENT '登录IP', - `joinip` varchar(50) NOT NULL DEFAULT '' COMMENT '加入时间', - `jointime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '加入时间', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', + `joinip` varchar(50) DEFAULT '' COMMENT '加入IP', + `jointime` int(10) DEFAULT NULL COMMENT '加入时间', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `token` varchar(50) DEFAULT '' COMMENT 'Token', + `status` varchar(30) DEFAULT '' COMMENT '状态', + `verification` varchar(255) DEFAULT '' COMMENT '验证', PRIMARY KEY (`id`), KEY `username` (`username`), KEY `email` (`email`), KEY `mobile` (`mobile`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='会员表'; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员表'; -- ---------------------------- --- Records of `fa_user` +-- Records of fa_user -- ---------------------------- BEGIN; -INSERT INTO `fa_user` VALUES ('3', 'admin', 'admin', 'c13f62012fd6a8fdf06b3452a94430e5', 'rpR6Bv', 'admin@163.com', '13888888888', '/assets/img/avatar.png', '0', '0', '2017-04-15', '0', '1491822015', '0', '1491822038', '127.0.0.1', '127.0.0.1', '1491461418', 'normal'); +INSERT INTO `fa_user` VALUES (1, 1, 'admin', 'admin', 'c13f62012fd6a8fdf06b3452a94430e5', 'rpR6Bv', 'admin@163.com', '13888888888', '', 0, 0, '2017-04-15', '', 0, 0, 1, 1, 1516170492, 1516171614, '127.0.0.1', 0, '127.0.0.1', 1491461418, 0, 1516171614, '', 'normal',''); COMMIT; -- ---------------------------- --- Table structure for `fa_user_signin` --- ---------------------------- -DROP TABLE IF EXISTS `fa_user_signin`; -CREATE TABLE `fa_user_signin` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', - `successions` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '连续签到次数', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `user_id` (`user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='会员签到表'; - --- ---------------------------- --- Table structure for `fa_user_third` +-- Table structure for fa_user_group -- ---------------------------- -DROP TABLE IF EXISTS `fa_user_third`; -CREATE TABLE `fa_user_third` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', - `platform` enum('weibo','wechat','qq') NOT NULL COMMENT '第三方应用', - `openid` varchar(50) NOT NULL DEFAULT '' COMMENT '第三方唯一ID', - `openname` varchar(50) NOT NULL DEFAULT '' COMMENT '第三方会员昵称', - `access_token` varchar(100) NOT NULL DEFAULT '', - `refresh_token` varchar(100) NOT NULL DEFAULT '', - `expires_in` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '有效期', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `logintime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '登录时间', - `expiretime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '过期时间', - PRIMARY KEY (`id`), - UNIQUE KEY `platform` (`platform`,`openid`), - KEY `user_id` (`user_id`,`platform`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='会员连接表'; - --- ---------------------------- --- Table structure for `fa_version` --- ---------------------------- -DROP TABLE IF EXISTS `fa_version`; -CREATE TABLE `fa_version` ( - `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', - `oldversion` varchar(30) NOT NULL DEFAULT '' COMMENT '旧版本号', - `newversion` varchar(30) NOT NULL DEFAULT '' COMMENT '新版本号', - `packagesize` varchar(30) NOT NULL DEFAULT '' COMMENT '包大小', - `content` varchar(500) NOT NULL DEFAULT '' COMMENT '升级内容', - `downloadurl` varchar(255) NOT NULL DEFAULT '' COMMENT '下载地址', - `enforce` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '强制更新', - `createtime` int(10) NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', +DROP TABLE IF EXISTS `fa_user_group`; +CREATE TABLE `fa_user_group` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) DEFAULT '' COMMENT '组名', + `rules` text COMMENT '权限节点', + `createtime` int(10) DEFAULT NULL COMMENT '添加时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `status` enum('normal','hidden') DEFAULT NULL COMMENT '状态', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='版本表'; +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员组表'; -- ---------------------------- --- Records of `fa_version` +-- Records of fa_user_group -- ---------------------------- BEGIN; -INSERT INTO `fa_version` VALUES ('1', '1.1.1,2', '1.2.1', '20M', '更新内容', 'http://www.downloadurl.com', '1', '1400000000', '0', '0', 'normal'); +INSERT INTO `fa_user_group` VALUES (1, '默认组', '1,2,3,4,5,6,7,8,9,10,11,12', 1515386468, 1516168298, 'normal'); COMMIT; -- ---------------------------- --- Table structure for `fa_wechat_autoreply` +-- Table structure for fa_user_money_log -- ---------------------------- -DROP TABLE IF EXISTS `fa_wechat_autoreply`; -CREATE TABLE `fa_wechat_autoreply` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `title` varchar(100) NOT NULL DEFAULT '' COMMENT '标题', - `text` varchar(100) NOT NULL DEFAULT '' COMMENT '触发文本', - `eventkey` varchar(50) NOT NULL DEFAULT '' COMMENT '响应事件', - `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', +DROP TABLE IF EXISTS `fa_user_money_log`; +CREATE TABLE `fa_user_money_log` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更余额', + `before` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更前余额', + `after` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更后余额', + `memo` varchar(255) DEFAULT '' COMMENT '备注', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='微信自动回复表'; +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员余额变动表'; -- ---------------------------- --- Records of `fa_wechat_autoreply` +-- Table structure for fa_user_rule -- ---------------------------- -BEGIN; -INSERT INTO `fa_wechat_autoreply` VALUES ('1', '输入hello', 'hello', '58c7d908c4570', '123', '1493366855', '1493366855', 'normal'), ('2', '输入你好', '你好', '58fdfaa9e1965', 'sad', '1493704976', '1493704976', 'normal'); -COMMIT; - --- ---------------------------- --- Table structure for `fa_wechat_config` --- ---------------------------- -DROP TABLE IF EXISTS `fa_wechat_config`; -CREATE TABLE `fa_wechat_config` ( +DROP TABLE IF EXISTS `fa_user_rule`; +CREATE TABLE `fa_user_rule` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL DEFAULT '' COMMENT '配置名称', - `title` varchar(50) NOT NULL DEFAULT '' COMMENT '配置标题', - `value` text NOT NULL COMMENT '配置值', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='微信配置表'; + `pid` int(10) DEFAULT NULL COMMENT '父ID', + `name` varchar(50) DEFAULT NULL COMMENT '名称', + `title` varchar(50) DEFAULT '' COMMENT '标题', + `remark` varchar(100) DEFAULT NULL COMMENT '备注', + `ismenu` tinyint(1) DEFAULT NULL COMMENT '是否菜单', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `weigh` int(10) DEFAULT '0' COMMENT '权重', + `status` enum('normal','hidden') DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员规则表'; -- ---------------------------- --- Records of `fa_wechat_config` +-- Records of fa_user_rule -- ---------------------------- BEGIN; -INSERT INTO `fa_wechat_config` VALUES ('1', 'menu', '微信菜单', '[{\"name\":\"FastAdmin\",\"sub_button\":[{\"name\":\"官网\",\"type\":\"view\",\"url\":\"http:\\/\\/www.fastadmin.net\"},{\"name\":\"在线演示\",\"type\":\"view\",\"url\":\"http:\\/\\/demo.fastadmin.net\"},{\"name\":\"文档\",\"type\":\"view\",\"url\":\"http:\\/\\/doc.fastadmin.net\"}]},{\"name\":\"在线客服\",\"type\":\"click\",\"key\":\"58cb852984970\"},{\"name\":\"关于我们\",\"type\":\"click\",\"key\":\"58bf944aa0777\"}]', '1497398820', '1497422985'), ('2', 'service', '客服配置', '{\"onlinetime\":\"09:00-18:00\",\"offlinemsg\":\"请在工作时间联系客服!\",\"nosessionmsg\":\"当前没有客服在线!请稍后重试!\",\"waitformsg\":\"请问有什么可以帮到您?\"}', '1497429674', '1497429674'), ('3', 'signin', '连续登录配置', '{\"s1\":\"100\",\"s2\":\"200\",\"s3\":\"300\",\"sn\":\"500\"}', '1497429711', '1497429711'); +INSERT INTO `fa_user_rule` VALUES (1, 0, 'index', 'Frontend', '', 1, 1516168079, 1516168079, 1, 'normal'); +INSERT INTO `fa_user_rule` VALUES (2, 0, 'api', 'API Interface', '', 1, 1516168062, 1516168062, 2, 'normal'); +INSERT INTO `fa_user_rule` VALUES (3, 1, 'user', 'User Module', '', 1, 1515386221, 1516168103, 12, 'normal'); +INSERT INTO `fa_user_rule` VALUES (4, 2, 'user', 'User Module', '', 1, 1515386221, 1516168092, 11, 'normal'); +INSERT INTO `fa_user_rule` VALUES (5, 3, 'index/user/login', 'Login', '', 0, 1515386247, 1515386247, 5, 'normal'); +INSERT INTO `fa_user_rule` VALUES (6, 3, 'index/user/register', 'Register', '', 0, 1515386262, 1516015236, 7, 'normal'); +INSERT INTO `fa_user_rule` VALUES (7, 3, 'index/user/index', 'User Center', '', 0, 1516015012, 1516015012, 9, 'normal'); +INSERT INTO `fa_user_rule` VALUES (8, 3, 'index/user/profile', 'Profile', '', 0, 1516015012, 1516015012, 4, 'normal'); +INSERT INTO `fa_user_rule` VALUES (9, 4, 'api/user/login', 'Login', '', 0, 1515386247, 1515386247, 6, 'normal'); +INSERT INTO `fa_user_rule` VALUES (10, 4, 'api/user/register', 'Register', '', 0, 1515386262, 1516015236, 8, 'normal'); +INSERT INTO `fa_user_rule` VALUES (11, 4, 'api/user/index', 'User Center', '', 0, 1516015012, 1516015012, 10, 'normal'); +INSERT INTO `fa_user_rule` VALUES (12, 4, 'api/user/profile', 'Profile', '', 0, 1516015012, 1516015012, 3, 'normal'); COMMIT; -- ---------------------------- --- Table structure for `fa_wechat_context` +-- Table structure for fa_user_score_log -- ---------------------------- -DROP TABLE IF EXISTS `fa_wechat_context`; -CREATE TABLE `fa_wechat_context` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `openid` varchar(64) NOT NULL DEFAULT '', - `type` varchar(30) NOT NULL DEFAULT '' COMMENT '类型', - `eventkey` varchar(64) NOT NULL DEFAULT '', - `command` varchar(64) NOT NULL DEFAULT '', - `message` varchar(255) NOT NULL DEFAULT '' COMMENT '内容', - `refreshtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最后刷新时间', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `openid` (`openid`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='微信上下文表'; +DROP TABLE IF EXISTS `fa_user_score_log`; +CREATE TABLE `fa_user_score_log` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `score` int(10) NOT NULL DEFAULT '0' COMMENT '变更积分', + `before` int(10) NOT NULL DEFAULT '0' COMMENT '变更前积分', + `after` int(10) NOT NULL DEFAULT '0' COMMENT '变更后积分', + `memo` varchar(255) DEFAULT '' COMMENT '备注', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员积分变动表'; -- ---------------------------- --- Table structure for `fa_wechat_response` +-- Table structure for fa_user_token -- ---------------------------- -DROP TABLE IF EXISTS `fa_wechat_response`; -CREATE TABLE `fa_wechat_response` ( - `id` int(10) NOT NULL AUTO_INCREMENT, - `title` varchar(100) NOT NULL DEFAULT '' COMMENT '资源名', - `eventkey` varchar(128) NOT NULL DEFAULT '' COMMENT '事件', - `type` enum('text','image','news','voice','video','music','link','app') NOT NULL DEFAULT 'text' COMMENT '类型', - `content` text NOT NULL COMMENT '内容', - `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注', - `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', - `updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', - `status` varchar(30) NOT NULL DEFAULT '' COMMENT '状态', - PRIMARY KEY (`id`), - UNIQUE KEY `event` (`eventkey`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='微信资源表'; +DROP TABLE IF EXISTS `fa_user_token`; +CREATE TABLE `fa_user_token` ( + `token` varchar(50) NOT NULL COMMENT 'Token', + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `expiretime` int(10) DEFAULT NULL COMMENT '过期时间', + PRIMARY KEY (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员Token表'; -- ---------------------------- --- Records of `fa_wechat_response` +-- Table structure for fa_version -- ---------------------------- -BEGIN; -INSERT INTO `fa_wechat_response` VALUES ('1', '签到送积分', '58adaf7876aab', 'app', '{\"app\":\"signin\"}', '', '1487777656', '1487777656', 'normal'), ('2', '关于我们', '58bf944aa0777', 'app', '{\"app\":\"page\",\"id\":\"1\"}', '', '1488950346', '1488950346', 'normal'), ('3', '自动回复1', '58c7d908c4570', 'text', '{\"content\":\"world\"}', '', '1489492232', '1489492232', 'normal'), ('4', '联系客服', '58cb852984970', 'app', '{\"app\":\"service\"}', '', '1489732905', '1489732905', 'normal'), ('5', '自动回复2', '58fdfaa9e1965', 'text', '{\"content\":\"我是FastAdmin!\"}', '', '1493039785', '1493039785', 'normal'); -COMMIT; +DROP TABLE IF EXISTS `fa_version`; +CREATE TABLE `fa_version` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `oldversion` varchar(30) DEFAULT '' COMMENT '旧版本号', + `newversion` varchar(30) DEFAULT '' COMMENT '新版本号', + `packagesize` varchar(30) DEFAULT '' COMMENT '包大小', + `content` varchar(500) DEFAULT '' COMMENT '升级内容', + `downloadurl` varchar(255) DEFAULT '' COMMENT '下载地址', + `enforce` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '强制更新', + `createtime` int(10) DEFAULT NULL COMMENT '创建时间', + `updatetime` int(10) DEFAULT NULL COMMENT '更新时间', + `weigh` int(10) NOT NULL DEFAULT 0 COMMENT '权重', + `status` varchar(30) DEFAULT '' COMMENT '状态', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='版本表'; SET FOREIGN_KEY_CHECKS = 1; diff --git a/application/admin/command/Install/install.html b/application/admin/command/Install/install.html new file mode 100644 index 0000000000000000000000000000000000000000..6c982eaa86135e5cb78e2cdae2c4d036256b8e54 --- /dev/null +++ b/application/admin/command/Install/install.html @@ -0,0 +1,316 @@ + + + + + + {:__('Installing FastAdmin')} + + + + + + + +
      +

      + + + +

      +

      {:__('Installing FastAdmin')}

      +
      + +
      + {if $errInfo} +
      + {$errInfo} +
      + {/if} + + + + +
      +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      +
      + +
      +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      +
      + +
      +
      + + +
      + +
      + +
      + + + +
      +
      + + + + + +
      +
      + + diff --git a/application/admin/command/Install/zh-cn.php b/application/admin/command/Install/zh-cn.php new file mode 100644 index 0000000000000000000000000000000000000000..26914d025a9a0228952fbff7e5717607ad1d707a --- /dev/null +++ b/application/admin/command/Install/zh-cn.php @@ -0,0 +1,34 @@ + '温馨提示', + 'Installing FastAdmin' => '安装FastAdmin', + 'Mysql Hostname' => 'MySQL 数据库地址', + 'Mysql Database' => 'MySQL 数据库名', + 'Mysql Username' => 'MySQL 用户名', + 'Mysql Password' => 'MySQL 密码', + 'Mysql Prefix' => 'MySQL 数据表前缀', + 'Mysql Hostport' => 'MySQL 端口号', + 'Admin Username' => '管理员用户名', + 'Admin Email' => '管理员Email', + 'Admin Password' => '管理员密码', + 'Repeat Password' => '重复管理员密码', + 'Website' => '网站名称', + 'My Website' => '我的网站', + 'Install now' => '点击安装', + 'Installing' => '安装中...', + 'Home' => '访问首页', + 'Dashboard' => '进入后台', + 'Go back' => '返回上一页', + 'Install Successed' => '安装成功!', + 'Security tips' => '温馨提示:请将以下后台登录入口添加到你的收藏夹,为了你的安全,不要泄漏或发送给他人!如有泄漏请及时修改!', + 'Please input correct database' => '请输入正确的数据库名', + 'Please input correct username' => '用户名只能由3-12位数字、字母、下划线组合', + 'Please input correct password' => '密码长度必须在6-16位之间,不能包含空格', + 'The two passwords you entered did not match' => '两次输入的密码不一致', + 'Please input correct website' => '网站名称输入不正确', + 'The current version %s is too low, please use PHP 7.0 or higher' => '当前版本%s过低,请使用PHP7.0以上版本', + 'PDO is not currently installed and cannot be installed' => '当前未开启PDO,无法进行安装', + 'The current permissions are insufficient to write the file %s' => '当前权限不足,无法写入文件%s', + 'Please go to the official website to download the full package or resource package and try to install' => '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装', + 'The system has been installed. If you need to reinstall, please remove %s first' => '当前已经安装成功,如果需要重新安装,请手动移除%s文件', +]; diff --git a/application/admin/command/Menu.php b/application/admin/command/Menu.php old mode 100644 new mode 100755 index 751abadf5e6906e2fa9a07c6ff98e56d324651af..cc21f3bdc1ec00eefe96d93d7a3264448f184556 --- a/application/admin/command/Menu.php +++ b/application/admin/command/Menu.php @@ -12,57 +12,79 @@ use think\console\Input; use think\console\input\Option; use think\console\Output; use think\Exception; +use think\Loader; class Menu extends Command { - protected $model = null; protected function configure() { $this - ->setName('menu') - ->addOption('controller', 'c', Option::VALUE_REQUIRED, 'controller name,use \'all-controller\' when build all menu', null) - ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '') - ->setDescription('Build auth menu from controller'); + ->setName('menu') + ->addOption('controller', 'c', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name,use \'all-controller\' when build all menu', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force delete menu,without tips', null) + ->addOption('equal', 'e', Option::VALUE_OPTIONAL, 'the controller must be equal', null) + ->setDescription('Build auth menu from controller'); + //要执行的controller必须一样,不适用模糊查询 } protected function execute(Input $input, Output $output) { $this->model = new AuthRule(); $adminPath = dirname(__DIR__) . DS; - $moduleName = 'admin'; //控制器名 $controller = $input->getOption('controller') ?: ''; - if (!$controller) - { + if (!$controller) { throw new Exception("please input controller name"); } + $force = $input->getOption('force'); //是否为删除模式 $delete = $input->getOption('delete'); - if ($delete) - { - if ($controller == 'all-controller') - { + //是否控制器完全匹配 + $equal = $input->getOption('equal'); + + + if ($delete) { + if (in_array('all-controller', $controller)) { throw new Exception("could not delete all menu"); } $ids = []; - $list = $this->model->where('name', 'like', "/{$moduleName}/" . strtolower($controller) . "%")->select(); - foreach ($list as $k => $v) - { + $list = $this->model->where(function ($query) use ($controller, $equal) { + foreach ($controller as $index => $item) { + if (stripos($item, '_') !== false) { + $item = Loader::parseName($item, 1); + } + if (stripos($item, '/') !== false) { + $controllerArr = explode('/', $item); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = Loader::parseName($controllerArr[$key]); + } else { + $controllerArr = [Loader::parseName($item)]; + } + $item = str_replace('_', '\_', implode('/', $controllerArr)); + if ($equal) { + $query->whereOr('name', 'eq', $item); + } else { + $query->whereOr('name', 'like', strtolower($item) . "%"); + } + } + })->select(); + foreach ($list as $k => $v) { $output->warning($v->name); $ids[] = $v->id; } - if (!$ids) - { + if (!$ids) { throw new Exception("There is no menu to delete"); } - $readyMenu = []; - $output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: "); - $line = fgets(STDIN); - if (trim($line) != 'yes') - { - throw new Exception("Operation is aborted!"); + if (!$force) { + $output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } } AuthRule::destroy($ids); @@ -71,23 +93,32 @@ class Menu extends Command return; } - if ($controller != 'all-controller') - { - $controllerArr = explode('/', $controller); - end($controllerArr); - $key = key($controllerArr); - $controllerArr[$key] = ucfirst($controllerArr[$key]); - $adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php'; - if (!is_file($adminPath)) - { - $output->error("controller not found"); - return; + if (!in_array('all-controller', $controller)) { + foreach ($controller as $index => $item) { + if (stripos($item, '_') !== false) { + $item = Loader::parseName($item, 1); + } + if (stripos($item, '/') !== false) { + $controllerArr = explode('/', $item); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = ucfirst($controllerArr[$key]); + } else { + $controllerArr = [ucfirst($item)]; + } + $adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php'; + if (!is_file($adminPath)) { + $output->error("controller not found"); + return; + } + $this->importRule($item); } - $this->importRule($controller); - } - else - { - $this->model->destroy([]); + } else { + $authRuleList = AuthRule::select(); + //生成权限规则备份文件 + file_put_contents(RUNTIME_PATH . 'authrule.json', json_encode(collection($authRuleList)->toArray())); + + $this->model->where('id', '>', 0)->delete(); $controllerDir = $adminPath . 'controller' . DS; // 扫描新的节点信息并导入 $treelist = $this->import($this->scandir($controllerDir)); @@ -105,16 +136,11 @@ class Menu extends Command { $result = []; $cdir = scandir($dir); - foreach ($cdir as $value) - { - if (!in_array($value, array(".", ".."))) - { - if (is_dir($dir . DS . $value)) - { + foreach ($cdir as $value) { + if (!in_array($value, array(".", ".."))) { + if (is_dir($dir . DS . $value)) { $result[$value] = $this->scandir($dir . DS . $value); - } - else - { + } else { $result[] = $value; } } @@ -131,19 +157,14 @@ class Menu extends Command public function import($dirarr, $parentdir = []) { $menuarr = []; - foreach ($dirarr as $k => $v) - { - if (is_array($v)) - { + foreach ($dirarr as $k => $v) { + if (is_array($v)) { //当前是文件夹 $nowparentdir = array_merge($parentdir, [$k]); $this->import($v, $nowparentdir); - } - else - { + } else { //只匹配PHP文件 - if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) - { + if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) { continue; } //导入文件 @@ -157,10 +178,16 @@ class Menu extends Command protected function importRule($controller) { - $controllerArr = explode('/', $controller); - end($controllerArr); - $key = key($controllerArr); - $controllerArr[$key] = ucfirst($controllerArr[$key]); + $controller = str_replace('\\', '/', $controller); + if (stripos($controller, '/') !== false) { + $controllerArr = explode('/', $controller); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = ucfirst($controllerArr[$key]); + } else { + $key = 0; + $controllerArr = [ucfirst($controller)]; + } $classSuffix = Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; $className = "\\app\\admin\\controller\\" . implode("\\", $controllerArr) . $classSuffix; @@ -176,37 +203,48 @@ class Menu extends Command $tempClassFile = __DIR__ . DS . $uniqueName . ".php"; file_put_contents($tempClassFile, $classContent); $className = "\\app\\admin\\command\\" . $uniqueName; + + //删除临时文件 + register_shutdown_function(function () use ($tempClassFile) { + if ($tempClassFile) { + //删除临时文件 + @unlink($tempClassFile); + } + }); + //反射机制调用类的注释和方法名 $reflector = new ReflectionClass($className); - if (isset($tempClassFile)) - { - //删除临时文件 - @unlink($tempClassFile); - } - //只匹配公共的方法 $methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC); $classComment = $reflector->getDocComment(); + //判断是否有启用软删除 + $softDeleteMethods = ['destroy', 'restore', 'recyclebin']; + $withSofeDelete = false; + $modelRegexArr = ["/\\\$this\->model\s*=\s*model\(['|\"](\w+)['|\"]\);/", "/\\\$this\->model\s*=\s*new\s+([a-zA-Z\\\]+);/"]; + $modelRegex = preg_match($modelRegexArr[0], $classContent) ? $modelRegexArr[0] : $modelRegexArr[1]; + preg_match_all($modelRegex, $classContent, $matches); + if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) { + \think\Request::instance()->module('admin'); + $model = model($matches[1][0]); + if (in_array('trashed', get_class_methods($model))) { + $withSofeDelete = true; + } + } //忽略的类 - if (stripos($classComment, "@internal") !== FALSE) - { + if (stripos($classComment, "@internal") !== false) { return; } preg_match_all('#(@.*?)\n#s', $classComment, $annotations); $controllerIcon = 'fa fa-circle-o'; $controllerRemark = ''; //判断注释中是否设置了icon值 - if (isset($annotations[1])) - { - foreach ($annotations[1] as $tag) - { - if (stripos($tag, '@icon') !== FALSE) - { + if (isset($annotations[1])) { + foreach ($annotations[1] as $tag) { + if (stripos($tag, '@icon') !== false) { $controllerIcon = substr($tag, stripos($tag, ' ') + 1); } - if (stripos($tag, '@remark') !== FALSE) - { + if (stripos($tag, '@remark') !== false) { $controllerRemark = substr($tag, stripos($tag, ' ') + 1); } } @@ -217,56 +255,73 @@ class Menu extends Command //导入中文语言包 \think\Lang::load(dirname(__DIR__) . DS . 'lang/zh-cn.php'); - //先定入菜单的数据 + //先导入菜单的数据 $pid = 0; - $name = "/admin"; - foreach (explode('/', $controller) as $k => $v) - { - $name .= '/' . strtolower($v); - $title = (!isset($controllerArr[$k + 1]) ? $controllerTitle : ''); - $icon = (!isset($controllerArr[$k + 1]) ? $controllerIcon : 'fa fa-list'); - $remark = (!isset($controllerArr[$k + 1]) ? $controllerRemark : ''); - $title = $title ? $title : __(ucfirst($v) . ' manager'); + foreach ($controllerArr as $k => $v) { + $key = $k + 1; + //驼峰转下划线 + $controllerNameArr = array_slice($controllerArr, 0, $key); + foreach ($controllerNameArr as &$val) { + $val = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $val), "_")); + } + unset($val); + $name = implode('/', $controllerNameArr); + $title = (!isset($controllerArr[$key]) ? $controllerTitle : ''); + $icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list'); + $remark = (!isset($controllerArr[$key]) ? $controllerRemark : ''); + $title = $title ? $title : $v; $rulemodel = $this->model->get(['name' => $name]); - if (!$rulemodel) - { + if (!$rulemodel) { $this->model - ->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal']) - ->isUpdate(false) - ->save(); + ->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal']) + ->isUpdate(false) + ->save(); $pid = $this->model->id; - } - else - { + } else { $pid = $rulemodel->id; } } $ruleArr = []; - foreach ($methods as $m => $n) - { + foreach ($methods as $m => $n) { //过滤特殊的类 - if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') - { + if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') { + continue; + } + //未启用软删除时过滤相关方法 + if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) { continue; } //只匹配符合的方法 - if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) - { + if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) { unset($methods[$m]); continue; } $comment = $reflector->getMethod($n->name)->getDocComment(); //忽略的方法 - if (stripos($comment, "@internal") !== FALSE) - { + if (stripos($comment, "@internal") !== false) { continue; } //过滤掉其它字符 $comment = preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $comment); - $ruleArr[] = array('pid' => $pid, 'name' => $name . "/" . strtolower($n->name), 'icon' => 'fa fa-circle-o', 'title' => $comment ? $comment : $n->name, 'ismenu' => 0, 'status' => 'normal'); + $title = $comment ? $comment : ucfirst($n->name); + + //获取主键,作为AuthRule更新依据 + $id = $this->getAuthRulePK($name . "/" . strtolower($n->name)); + + $ruleArr[] = array('id' => $id, 'pid' => $pid, 'name' => $name . "/" . strtolower($n->name), 'icon' => 'fa fa-circle-o', 'title' => $title, 'ismenu' => 0, 'status' => 'normal'); } - $this->model->saveAll($ruleArr); + $this->model->isUpdate(false)->saveAll($ruleArr); } + //获取主键 + protected function getAuthRulePK($name) + { + if (!empty($name)) { + $id = $this->model + ->where('name', $name) + ->value('id'); + return $id ? $id : null; + } + } } diff --git a/application/admin/command/Min.php b/application/admin/command/Min.php index 8f8a458c63db5e08d65dc7cc17736c887e6f71ce..afc81646aea0fd121422ec4f135d7b0b30b992a9 100644 --- a/application/admin/command/Min.php +++ b/application/admin/command/Min.php @@ -27,20 +27,20 @@ class Min extends Command ->setName('min') ->addOption('module', 'm', Option::VALUE_REQUIRED, 'module name(frontend or backend),use \'all\' when build all modules', null) ->addOption('resource', 'r', Option::VALUE_REQUIRED, 'resource name(js or css),use \'all\' when build all resources', null) + ->addOption('optimize', 'o', Option::VALUE_OPTIONAL, 'optimize type(uglify|closure|none)', 'none') ->setDescription('Compress js and css file'); } protected function execute(Input $input, Output $output) { - $module = $input->getOption('module') ? : ''; - $resource = $input->getOption('resource') ? : ''; + $module = $input->getOption('module') ?: ''; + $resource = $input->getOption('resource') ?: ''; + $optimize = $input->getOption('optimize') ?: 'none'; - if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) - { + if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) { throw new Exception('Please input correct module name'); } - if (!$resource || !in_array($resource, ['js', 'css', 'all'])) - { + if (!$resource || !in_array($resource, ['js', 'css', 'all'])) { throw new Exception('Please input correct resource name'); } @@ -51,58 +51,55 @@ class Min extends Command $publicPath = ROOT_PATH . 'public' . DS; $tempFile = $minPath . 'temp.js'; - // Winsows下请手动配置配置该值 - $nodeExec = ""; - - if (!$nodeExec) - { - if (IS_WIN) - { - throw new Exception("node environment not found!please check http://doc.fastadmin.net/faq.html !"); - } - - try - { - $nodeExec = exec("which node"); - if (!$nodeExec) - { - throw new Exception("node environment not found!please install node first!"); + $nodeExec = ''; + + if (!$nodeExec) { + if (IS_WIN) { + // Winsows下请手动配置配置该值,一般将该值配置为 '"C:\Program Files\nodejs\node.exe"',除非你的Node安装路径有变更 + $nodeExec = 'C:\Program Files\nodejs\node.exe'; + if (file_exists($nodeExec)) { + $nodeExec = '"' . $nodeExec . '"'; + } else { + // 如果 '"C:\Program Files\nodejs\node.exe"' 不存在,可能是node安装路径有变更 + // 但安装node会自动配置环境变量,直接执行 '"node.exe"' 提高第一次使用压缩打包的成功率 + $nodeExec = '"node.exe"'; + } + } else { + try { + $nodeExec = exec("which node"); + if (!$nodeExec) { + throw new Exception("node environment not found!please install node first!"); + } + } catch (Exception $e) { + throw new Exception($e->getMessage()); } - } - catch (Exception $e) - { - throw new Exception($e->getMessage()); } } - foreach ($moduleArr as $mod) - { - foreach ($resourceArr as $res) - { + foreach ($moduleArr as $mod) { + foreach ($resourceArr as $res) { $data = [ 'publicPath' => $publicPath, 'jsBaseName' => str_replace('{module}', $mod, $this->options['jsBaseName']), 'jsBaseUrl' => $this->options['jsBaseUrl'], 'cssBaseName' => str_replace('{module}', $mod, $this->options['cssBaseName']), 'cssBaseUrl' => $this->options['cssBaseUrl'], - 'jsBasePath' => ROOT_PATH . $this->options['jsBaseUrl'], - 'cssBasePath' => ROOT_PATH . $this->options['cssBaseUrl'], + 'jsBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['jsBaseUrl']), + 'cssBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['cssBaseUrl']), + 'optimize' => $optimize, 'ds' => DS, ]; //源文件 $from = $data["{$res}BasePath"] . $data["{$res}BaseName"] . '.' . $res; - if (!is_file($from)) - { + if (!is_file($from)) { $output->error("{$res} source file not found!file:{$from}"); continue; } - if ($res == "js") - { + if ($res == "js") { $content = file_get_contents($from); - preg_match("/require\.config\(\{[\n]+(.*?)\n\}\);/is", $content, $matches); - if (!isset($matches[1])) - { + preg_match("/require\.config\(\{[\r\n]?[\n]?+(.*?)[\r\n]?[\n]?}\);/is", $content, $matches); + if (!isset($matches[1])) { $output->error("js config not found!"); continue; } @@ -115,11 +112,17 @@ class Min extends Command $output->info("Compress " . $data["{$res}BaseName"] . ".{$res}"); // 执行压缩 - echo exec("{$nodeExec} {$minPath}r.js -o {$tempFile} >> {$minPath}node.log"); + $command = "{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\""; + if ($output->isDebug()) { + $output->warning($command); + } + echo exec($command); } } - @unlink($tempFile); + if (!$output->isDebug()) { + @unlink($tempFile); + } $output->info("Build Successed!"); } @@ -134,16 +137,14 @@ class Min extends Command protected function writeToFile($name, $data, $pathname) { $search = $replace = []; - foreach ($data as $k => $v) - { + foreach ($data as $k => $v) { $search[] = "{%{$k}%}"; $replace[] = $v; } $stub = file_get_contents($this->getStub($name)); $content = str_replace($search, $replace, $stub); - if (!is_dir(dirname($pathname))) - { + if (!is_dir(dirname($pathname))) { mkdir(strtolower(dirname($pathname)), 0755, true); } return file_put_contents($pathname, $content); @@ -156,7 +157,6 @@ class Min extends Command */ protected function getStub($name) { - return __DIR__ . '/Min/stubs/' . $name . '.stub'; + return __DIR__ . DS . 'Min' . DS . 'stubs' . DS . $name . '.stub'; } - } diff --git a/application/admin/command/Min/stubs/css.stub b/application/admin/command/Min/stubs/css.stub index e2cafa3b75c6292b5b5e249c1212155bb433cbfe..17211a150b2a871aad73819442cd24e4aff43c09 100644 --- a/application/admin/command/Min/stubs/css.stub +++ b/application/admin/command/Min/stubs/css.stub @@ -1,5 +1,6 @@ ({ cssIn: "{%cssBasePath%}{%cssBaseName%}.css", out: "{%cssBasePath%}{%cssBaseName%}.min.css", - optimizeCss: "default" + optimizeCss: "default", + optimize: "{%optimize%}" }) \ No newline at end of file diff --git a/application/admin/command/Min/stubs/js.stub b/application/admin/command/Min/stubs/js.stub index 67a49a4e15b62b7bf5e3e85d6305cdf93bea2df1..8e7a210f2e94319d3ed367fa4f7b5665806192f0 100644 --- a/application/admin/command/Min/stubs/js.stub +++ b/application/admin/command/Min/stubs/js.stub @@ -2,7 +2,8 @@ {%config%} , optimizeCss: "standard", - optimize: "none", //可使用uglify|closure|none + optimize: "{%optimize%}", //可使用uglify|closure|none + preserveLicenseComments: false, removeCombined: false, baseUrl: "{%jsBasePath%}", //JS文件所在的基础目录 name: "{%jsBaseName%}", //来源文件,不包含后缀 diff --git a/application/admin/common.php b/application/admin/common.php old mode 100644 new mode 100755 index 86800c8d10471b0d8671739d147980e53b73d5a9..a6f18429c8715632ea22328bcc1dae8130b6d0db --- a/application/admin/common.php +++ b/application/admin/common.php @@ -5,133 +5,222 @@ use fast\Form; use fast\Tree; use think\Db; -/** - * 生成下拉列表 - * @param string $name - * @param mixed $options - * @param mixed $selected - * @param mixed $attr - * @return string - */ -function build_select($name, $options, $selected = [], $attr = []) -{ - $options = is_array($options) ? $options : explode(',', $options); - $selected = is_array($selected) ? $selected : explode(',', $selected); - return Form::select($name, $options, $selected, $attr); +if (!function_exists('build_select')) { + + /** + * 生成下拉列表 + * @param string $name + * @param mixed $options + * @param mixed $selected + * @param mixed $attr + * @return string + */ + function build_select($name, $options, $selected = [], $attr = []) + { + $options = is_array($options) ? $options : explode(',', $options); + $selected = is_array($selected) ? $selected : explode(',', $selected); + return Form::select($name, $options, $selected, $attr); + } } -/** - * 生成单选按钮组 - * @param string $name - * @param array $list - * @param mixed $selected - * @return string - */ -function build_radios($name, $list = [], $selected = null) -{ - $html = []; - $selected = is_null($selected) ? key($list) : $selected; - $selected = is_array($selected) ? $selected : explode(',', $selected); - foreach ($list as $k => $v) +if (!function_exists('build_radios')) { + + /** + * 生成单选按钮组 + * @param string $name + * @param array $list + * @param mixed $selected + * @return string + */ + function build_radios($name, $list = [], $selected = null) { - $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + $html = []; + $selected = is_null($selected) ? key($list) : $selected; + $selected = is_array($selected) ? $selected : explode(',', $selected); + foreach ($list as $k => $v) { + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + } + return '
      ' . implode(' ', $html) . '
      '; } - return implode(' ', $html); } -/** - * 生成复选按钮组 - * @param string $name - * @param array $list - * @param mixed $selected - * @return string - */ -function build_checkboxs($name, $list = [], $selected = null) -{ - $html = []; - $selected = is_null($selected) ? [] : $selected; - $selected = is_array($selected) ? $selected : explode(',', $selected); - foreach ($list as $k => $v) +if (!function_exists('build_checkboxs')) { + + /** + * 生成复选按钮组 + * @param string $name + * @param array $list + * @param mixed $selected + * @return string + */ + function build_checkboxs($name, $list = [], $selected = null) { - $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + $html = []; + $selected = is_null($selected) ? [] : $selected; + $selected = is_array($selected) ? $selected : explode(',', $selected); + foreach ($list as $k => $v) { + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + } + return '
      ' . implode(' ', $html) . '
      '; } - return implode(' ', $html); } -/** - * 生成分类下拉列表框 - * @param string $name - * @param string $type - * @param mixed $selected - * @param array $attr - * @return string - */ -function build_category_select($name, $type, $selected = null, $attr = [], $header = []) -{ - $tree = Tree::instance(); - $tree->init(Category::getCategoryArray($type), 'pid'); - $categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); - $categorydata = $header ? $header : []; - foreach ($categorylist as $k => $v) + +if (!function_exists('build_category_select')) { + + /** + * 生成分类下拉列表框 + * @param string $name + * @param string $type + * @param mixed $selected + * @param array $attr + * @param array $header + * @return string + */ + function build_category_select($name, $type, $selected = null, $attr = [], $header = []) { - $categorydata[$v['id']] = $v['name']; + $tree = Tree::instance(); + $tree->init(Category::getCategoryArray($type), 'pid'); + $categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); + $categorydata = $header ? $header : []; + foreach ($categorylist as $k => $v) { + $categorydata[$v['id']] = $v['name']; + } + $attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr); + return build_select($name, $categorydata, $selected, $attr); } - $attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr); - return build_select($name, $categorydata, $selected, $attr); } -/** - * 生成表格操作按钮栏 - * @param array $btns 按钮组 - * @param array $attr 按钮属性值 - * @return string - */ -function build_toolbar($btns = NULL, $attr = []) -{ - $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'delete']; - $btns = is_array($btns) ? $btns : explode(',', $btns); - $btnAttr = [ - 'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', ''], - 'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add')], - 'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit')], - 'delete' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete')], - ]; - $btnAttr = array_merge($btnAttr, $attr); - $html = []; - foreach ($btns as $k => $v) +if (!function_exists('build_toolbar')) { + + /** + * 生成表格操作按钮栏 + * @param array $btns 按钮组 + * @param array $attr 按钮属性值 + * @return string + */ + function build_toolbar($btns = null, $attr = []) { - if (!isset($btnAttr[$v])) - { - continue; + $auth = \app\admin\library\Auth::instance(); + $controller = str_replace('.', '/', strtolower(think\Request::instance()->controller())); + $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import']; + $btns = is_array($btns) ? $btns : explode(',', $btns); + $index = array_search('delete', $btns); + if ($index !== false) { + $btns[$index] = 'del'; } - list($href, $class, $icon, $text) = $btnAttr[$v]; - $html[] = ' ' . $text . ''; + $btnAttr = [ + 'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', '', __('Refresh')], + 'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add'), __('Add')], + 'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit'), __('Edit')], + 'del' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete'), __('Delete')], + 'import' => ['javascript:;', 'btn btn-info btn-import', 'fa fa-upload', __('Import'), __('Import')], + ]; + $btnAttr = array_merge($btnAttr, $attr); + $html = []; + foreach ($btns as $k => $v) { + //如果未定义或没有权限 + if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}"))) { + continue; + } + list($href, $class, $icon, $text, $title) = $btnAttr[$v]; + //$extend = $v == 'import' ? 'id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"' : ''; + //$html[] = ' ' . $text . ''; + if ($v == 'import') { + $template = str_replace('/', '_', $controller); + $download = ''; + if (file_exists("./template/{$template}.xlsx")) { + $download .= "
    • XLSX模版
    • "; + } + if (file_exists("./template/{$template}.xls")) { + $download .= "
    • XLS模版
    • "; + } + if (file_exists("./template/{$template}.csv")) { + $download .= empty($download) ? '' : "
    • "; + $download .= "
    • CSV模版
    • "; + } + $download .= empty($download) ? '' : "\n "; + if (!empty($download)) { + $html[] = << + + + +
      +EOT; + } else { + $html[] = ' ' . $text . ''; + } + } else { + $html[] = ' ' . $text . ''; + } + } + return implode(' ', $html); } - return implode(' ', $html); } -/** - * 生成页面Heading - * - * @param string $title - * @param string $content - * @return string - */ -function build_heading($title = NULL, $content = NULL) -{ - if (is_null($title) && is_null($content)) +if (!function_exists('build_heading')) { + + /** + * 生成页面Heading + * + * @param string $path 指定的path + * @return string + */ + function build_heading($path = null, $container = true) { - $path = request()->pathinfo(); - $path = $path[0] == '/' ? $path : '/' . $path; + $title = $content = ''; + if (is_null($path)) { + $action = request()->action(); + $controller = str_replace('.', '/', request()->controller()); + $path = strtolower($controller . ($action && $action != 'index' ? '/' . $action : '')); + } // 根据当前的URI自动匹配父节点的标题和备注 $data = Db::name('auth_rule')->where('name', $path)->field('title,remark')->find(); - if ($data) - { - $title = $data['title']; - $content = $data['remark']; + if ($data) { + $title = __($data['title']); + $content = __($data['remark']); } + if (!$content) { + return ''; + } + $result = '
      ' . $title . '' . $content . '
      '; + if ($container) { + $result = '
      ' . $result . '
      '; + } + return $result; + } +} + +if (!function_exists('build_suffix_image')) { + /** + * 生成文件后缀图片 + * @param string $suffix 后缀 + * @param null $background + * @return string + */ + function build_suffix_image($suffix, $background = null) + { + $suffix = mb_substr(strtoupper($suffix), 0, 4); + $total = unpack('L', hash('adler32', $suffix, true))[1]; + $hue = $total % 360; + list($r, $g, $b) = hsv2rgb($hue / 360, 0.3, 0.9); + + $background = $background ? $background : "rgb({$r},{$g},{$b})"; + + $icon = << + + + + + + {$suffix} + +EOT; + return $icon; } - if (!$content) - return ''; - return '
      ' . $title . '' . $content . '
      '; } diff --git a/application/admin/controller/Addon.php b/application/admin/controller/Addon.php new file mode 100644 index 0000000000000000000000000000000000000000..a0bc0562cb09eb16ea4945c92998cba0b0d66bd9 --- /dev/null +++ b/application/admin/controller/Addon.php @@ -0,0 +1,363 @@ +https://www.fastadmin.net/store.html + */ +class Addon extends Backend +{ + protected $model = null; + protected $noNeedRight = ['get_table_list']; + + public function _initialize() + { + parent::_initialize(); + if (!$this->auth->isSuperAdmin() && in_array($this->request->action(), ['install', 'uninstall', 'local', 'upgrade'])) { + $this->error(__('Access is allowed only to the super management group')); + } + } + + /** + * 查看 + */ + public function index() + { + $addons = get_addon_list(); + foreach ($addons as $k => &$v) { + $config = get_addon_config($v['name']); + $v['config'] = $config ? 1 : 0; + $v['url'] = str_replace($this->request->server('SCRIPT_NAME'), '', $v['url']); + } + $this->assignconfig(['addons' => $addons, 'api_url' => config('fastadmin.api_url'), 'faversion' => config('fastadmin.version')]); + return $this->view->fetch(); + } + + /** + * 配置 + */ + public function config($name = null) + { + $name = $name ? $name : $this->request->get("name"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + if (!is_dir(ADDON_PATH . $name)) { + $this->error(__('Directory not found')); + } + $info = get_addon_info($name); + $config = get_addon_fullconfig($name); + if (!$info) { + $this->error(__('No Results were found')); + } + if ($this->request->isPost()) { + $params = $this->request->post("row/a", [], 'trim'); + if ($params) { + foreach ($config as $k => &$v) { + if (isset($params[$v['name']])) { + if ($v['type'] == 'array') { + $params[$v['name']] = is_array($params[$v['name']]) ? $params[$v['name']] : (array)json_decode($params[$v['name']], true); + $value = $params[$v['name']]; + } else { + $value = is_array($params[$v['name']]) ? implode(',', $params[$v['name']]) : $params[$v['name']]; + } + $v['value'] = $value; + } + } + try { + //更新配置文件 + set_addon_fullconfig($name, $config); + Service::refresh(); + $this->success(); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + $tips = []; + foreach ($config as $index => &$item) { + if ($item['name'] == '__tips__') { + $tips = $item; + unset($config[$index]); + } + } + $this->view->assign("addon", ['info' => $info, 'config' => $config, 'tips' => $tips]); + $configFile = ADDON_PATH . $name . DS . 'config.html'; + $viewFile = is_file($configFile) ? $configFile : ''; + return $this->view->fetch($viewFile); + } + + /** + * 安装 + */ + public function install() + { + $name = $this->request->post("name"); + $force = (int)$this->request->post("force"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + + $info = []; + try { + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $version = $this->request->post("version"); + $faversion = $this->request->post("faversion"); + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'version' => $version, + 'faversion' => $faversion + ]; + $info = Service::install($name, $force, $extend); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage()), $e->getCode()); + } + $this->success(__('Install successful'), '', ['addon' => $info]); + } + + /** + * 卸载 + */ + public function uninstall() + { + $name = $this->request->post("name"); + $force = (int)$this->request->post("force"); + $droptables = (int)$this->request->post("droptables"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + //只有开启调试且为超级管理员才允许删除相关数据库 + $tables = []; + if ($droptables && Config::get("app_debug") && $this->auth->isSuperAdmin()) { + $tables = get_addon_tables($name); + } + try { + Service::uninstall($name, $force); + if ($tables) { + $prefix = Config::get('database.prefix'); + //删除插件关联表 + foreach ($tables as $index => $table) { + //忽略非插件标识的表名 + if (!preg_match("/^{$prefix}{$name}/", $table)) { + continue; + } + Db::execute("DROP TABLE IF EXISTS `{$table}`"); + } + } + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Uninstall successful')); + } + + /** + * 禁用启用 + */ + public function state() + { + $name = $this->request->post("name"); + $action = $this->request->post("action"); + $force = (int)$this->request->post("force"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + try { + $action = $action == 'enable' ? $action : 'disable'; + //调用启用、禁用的方法 + Service::$action($name, $force); + Cache::rm('__menu__'); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Operate successful')); + } + + /** + * 本地上传 + */ + public function local() + { + Config::set('default_return_type', 'json'); + + $info = []; + $file = $this->request->file('file'); + try { + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $faversion = $this->request->post("faversion"); + if (!$uid || !$token) { + throw new Exception(__('Please login and try to install')); + } + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'faversion' => $faversion + ]; + $info = Service::local($file, $extend); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Offline installed tips'), '', ['addon' => $info]); + } + + /** + * 更新插件 + */ + public function upgrade() + { + $name = $this->request->post("name"); + $addonTmpDir = RUNTIME_PATH . 'addons' . DS; + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + if (!is_dir($addonTmpDir)) { + @mkdir($addonTmpDir, 0755, true); + } + + $info = []; + try { + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $version = $this->request->post("version"); + $faversion = $this->request->post("faversion"); + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'version' => $version, + 'faversion' => $faversion + ]; + //调用更新的方法 + $info = Service::upgrade($name, $extend); + Cache::rm('__menu__'); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Operate successful'), '', ['addon' => $info]); + } + + /** + * 已装插件 + */ + public function downloaded() + { + $offset = (int)$this->request->get("offset"); + $limit = (int)$this->request->get("limit"); + $filter = $this->request->get("filter"); + $search = $this->request->get("search"); + $search = htmlspecialchars(strip_tags($search)); + $onlineaddons = Cache::get("onlineaddons"); + if (!is_array($onlineaddons) && config('fastadmin.api_url')) { + $onlineaddons = []; + $result = Http::sendRequest(config('fastadmin.api_url') . '/addon/index', [], 'GET', [ + CURLOPT_HTTPHEADER => ['Accept-Encoding:gzip'], + CURLOPT_ENCODING => "gzip" + ]); + if ($result['ret']) { + $json = (array)json_decode($result['msg'], true); + $rows = isset($json['rows']) ? $json['rows'] : []; + foreach ($rows as $index => $row) { + $onlineaddons[$row['name']] = $row; + } + } + Cache::set("onlineaddons", $onlineaddons, 600); + } + $filter = (array)json_decode($filter, true); + $addons = get_addon_list(); + $list = []; + foreach ($addons as $k => $v) { + if ($search && stripos($v['name'], $search) === false && stripos($v['title'], $search) === false && stripos($v['intro'], $search) === false) { + continue; + } + + if (isset($onlineaddons[$v['name']])) { + $v = array_merge($v, $onlineaddons[$v['name']]); + } else { + $v['category_id'] = 0; + $v['flag'] = ''; + $v['banner'] = ''; + $v['image'] = ''; + $v['donateimage'] = ''; + $v['demourl'] = ''; + $v['price'] = __('None'); + $v['screenshots'] = []; + $v['releaselist'] = []; + } + $v['url'] = addon_url($v['name']); + $v['url'] = str_replace($this->request->server('SCRIPT_NAME'), '', $v['url']); + $v['createtime'] = filemtime(ADDON_PATH . $v['name']); + if ($filter && isset($filter['category_id']) && is_numeric($filter['category_id']) && $filter['category_id'] != $v['category_id']) { + continue; + } + $list[] = $v; + } + $total = count($list); + if ($limit) { + $list = array_slice($list, $offset, $limit); + } + $result = array("total" => $total, "rows" => $list); + + $callback = $this->request->get('callback') ? "jsonp" : "json"; + return $callback($result); + } + + /** + * 获取插件相关表 + */ + public function get_table_list() + { + $name = $this->request->post("name"); + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + $tables = get_addon_tables($name); + $prefix = Config::get('database.prefix'); + foreach ($tables as $index => $table) { + //忽略非插件标识的表名 + if (!preg_match("/^{$prefix}{$name}/", $table)) { + unset($tables[$index]); + } + } + $tables = array_values($tables); + $this->success('', null, ['tables' => $tables]); + } +} diff --git a/application/admin/controller/Ajax.php b/application/admin/controller/Ajax.php index 8a1ac5f52e0ad78b566f2bd19bc32aae47cccba0..dd58cf117148130fe04ef8c28e7a1fba97aee031 100644 --- a/application/admin/controller/Ajax.php +++ b/application/admin/controller/Ajax.php @@ -1,308 +1,275 @@ -request->filter(['strip_tags', 'htmlspecialchars']); - } - - /** - * 加载语言包 - */ - public function lang() - { - header('Content-Type: application/javascript'); - $callback = $this->request->get('callback'); - $controllername = input("controllername"); - $this->loadlang($controllername); - //强制输出JSON Object - $result = 'define(' . json_encode(Lang::get(), JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE) . ');'; - return $result; - } - - /** - * 上传文件 - */ - public function upload() - { - $this->code = -1; - $file = $this->request->file('file'); - if (empty($file)) - { - $this->msg = "未上传文件或超出服务器上传限制"; - return; - } - - //判断是否已经存在附件 - $sha1 = $file->hash(); - $uploaded = model("attachment")->where('sha1', $sha1)->find(); - if ($uploaded) - { - $this->code = 1; - $this->data = [ - 'url' => $uploaded['url'] - ]; - return; - } - - $upload = Config::get('upload'); - - preg_match('/(\d+)(\w+)/', $upload['maxsize'], $matches); - $type = strtolower($matches[2]); - $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3]; - $size = (int) $upload['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0); - $fileInfo = $file->getInfo(); - $suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION)); - $suffix = $suffix ? $suffix : 'file'; - $replaceArr = [ - '{year}' => date("Y"), - '{mon}' => date("m"), - '{day}' => date("d"), - '{hour}' => date("H"), - '{min}' => date("i"), - '{sec}' => date("s"), - '{random}' => Random::alnum(16), - '{random32}' => Random::alnum(32), - '{filename}' => $suffix ? substr($fileInfo['name'], 0, strripos($fileInfo['name'], '.')) : $fileInfo['name'], - '{suffix}' => $suffix, - '{.suffix}' => $suffix ? '.' . $suffix : '', - '{filemd5}' => md5_file($fileInfo['tmp_name']), - ]; - $savekey = $upload['savekey']; - $savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey); - - $uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1); - $fileName = substr($savekey, strripos($savekey, '/') + 1); - // - $splInfo = $file->validate(['size' => $size])->move(ROOT_PATH . '/public' . $uploadDir, $fileName); - if ($splInfo) - { - $imagewidth = $imageheight = 0; - if (in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'])) - { - $imgInfo = getimagesize($splInfo->getPathname()); - $imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth; - $imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight; - } - $params = array( - 'filesize' => $fileInfo['size'], - 'imagewidth' => $imagewidth, - 'imageheight' => $imageheight, - 'imagetype' => $suffix, - 'imageframes' => 0, - 'mimetype' => $fileInfo['type'], - 'url' => $uploadDir . $splInfo->getSaveName(), - 'uploadtime' => time(), - 'storage' => 'local', - 'sha1' => $sha1, - ); - model("attachment")->create(array_filter($params)); - $this->code = 1; - $this->data = [ - 'url' => $uploadDir . $splInfo->getSaveName() - ]; - } - else - { - // 上传失败获取错误信息 - $this->data = $file->getError(); - } - } - - /** - * 通用排序 - */ - public function weigh() - { - //排序的数组 - $ids = $this->request->post("ids"); - //拖动的记录ID - $changeid = $this->request->post("changeid"); - //操作字段 - $field = $this->request->post("field"); - //操作的数据表 - $table = $this->request->post("table"); - //排序的方式 - $orderway = $this->request->post("orderway", 'strtolower'); - $orderway = $orderway == 'asc' ? 'ASC' : 'DESC'; - $sour = $weighdata = []; - $ids = explode(',', $ids); - $prikey = 'id'; - $pid = $this->request->post("pid"); - //限制更新的字段 - $field = in_array($field, ['weigh']) ? $field : 'weigh'; - - // 如果设定了pid的值,此时只匹配满足条件的ID,其它忽略 - if ($pid !== '') - { - $hasids = []; - $list = Db::name($table)->where($prikey, 'in', $ids)->where('pid', 'in', $pid)->field('id,pid')->select(); - foreach ($list as $k => $v) - { - $hasids[] = $v['id']; - } - $ids = array_values(array_intersect($ids, $hasids)); - } - - //直接修复排序 - $one = Db::name($table)->field("{$field},COUNT(*) AS nums")->group($field)->having('nums > 1')->find(); - if ($one) - { - $list = Db::name($table)->field("$prikey,$field")->order($field, $orderway)->select(); - foreach ($list as $k => $v) - { - Db::name($table)->where($prikey, $v[$prikey])->update([$field => $k + 1]); - } - $this->code = 1; - } - else - { - $list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select(); - foreach ($list as $k => $v) - { - $sour[] = $v[$prikey]; - $weighdata[$v[$prikey]] = $v[$field]; - } - $position = array_search($changeid, $ids); - $desc_id = $sour[$position]; //移动到目标的ID值,取出所处改变前位置的值 - $sour_id = $changeid; - $desc_value = $weighdata[$desc_id]; - $sour_value = $weighdata[$sour_id]; - //echo "移动的ID:{$sour_id}\n"; - //echo "替换的ID:{$desc_id}\n"; - $weighids = array(); - $temp = array_values(array_diff_assoc($ids, $sour)); - foreach ($temp as $m => $n) - { - if ($n == $sour_id) - { - $offset = $desc_id; - } - else - { - if ($sour_id == $temp[0]) - { - $offset = isset($temp[$m + 1]) ? $temp[$m + 1] : $sour_id; - } - else - { - $offset = isset($temp[$m - 1]) ? $temp[$m - 1] : $sour_id; - } - } - $weighids[$n] = $weighdata[$offset]; - Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]); - } - $this->code = 1; - } - } - - /** - * 清空系统缓存 - */ - public function wipecache() - { - $wipe_cache_type = ['TEMP_PATH', 'LOG_PATH', 'CACHE_PATH']; - foreach ($wipe_cache_type as $item) - { - $dir = constant($item); - if (!is_dir($dir)) - continue; - $files = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($files as $fileinfo) - { - $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink'); - $todo($fileinfo->getRealPath()); - } - - //rmdir($dir); - } - Cache::clear(); - $this->code = 1; - } - - /** - * 读取分类数据,联动列表 - */ - public function category() - { - $type = $this->request->get('type'); - $pid = $this->request->get('pid'); - $where = ['status' => 'normal']; - $categorylist = null; - if ($pid !== '') - { - if ($type) - { - $where['type'] = $type; - } - if ($pid) - { - $where['pid'] = $pid; - } - - $categorylist = Db::name('category')->where($where)->field('id as value,name')->order('weigh desc,id desc')->select(); - } - $this->code = 1; - $this->data = $categorylist; - return; - } - - /** - * 读取省市区数据,联动列表 - */ - public function area() - { - $province = $this->request->get('province'); - $city = $this->request->get('city'); - $where = ['pid' => 0, 'level' => 1]; - $provincelist = null; - if ($province !== '') - { - if ($province) - { - $where['pid'] = $province; - $where['level'] = 2; - } - if ($city !== '') - { - if ($city) - { - $where['pid'] = $city; - $where['level'] = 3; - } - $provincelist = Db::name('area')->where($where)->field('id as value,name')->select(); - } - } - $this->code = 1; - $this->data = $provincelist; - return; - } - -} +request->filter(['trim', 'strip_tags', 'htmlspecialchars']); + } + + /** + * 加载语言包 + */ + public function lang() + { + header('Content-Type: application/javascript'); + header("Cache-Control: public"); + header("Pragma: cache"); + + $offset = 30 * 60 * 60 * 24; // 缓存一个月 + header("Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT"); + + $controllername = input("controllername"); + //默认只加载了控制器对应的语言名,你还根据控制器名来加载额外的语言包 + $this->loadlang($controllername); + return jsonp(Lang::get(), 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]); + } + + /** + * 上传文件 + */ + public function upload() + { + Config::set('default_return_type', 'json'); + //必须设定cdnurl为空,否则cdnurl函数计算错误 + Config::set('upload.cdnurl', ''); + $chunkid = $this->request->post("chunkid"); + if ($chunkid) { + if (!Config::get('upload.chunking')) { + $this->error(__('Chunk file disabled')); + } + $action = $this->request->post("action"); + $chunkindex = $this->request->post("chunkindex/d"); + $chunkcount = $this->request->post("chunkcount/d"); + $filename = $this->request->post("filename"); + $method = $this->request->method(true); + if ($action == 'merge') { + $attachment = null; + //合并分片文件 + try { + $upload = new Upload(); + $attachment = $upload->merge($chunkid, $chunkcount, $filename); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(__('Uploaded successful'), '', ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]); + } elseif ($method == 'clean') { + //删除冗余的分片文件 + try { + $upload = new Upload(); + $upload->clean($chunkid); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + //上传分片文件 + //默认普通上传文件 + $file = $this->request->file('file'); + try { + $upload = new Upload($file); + $upload->chunk($chunkid, $chunkindex, $chunkcount); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + } else { + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + + $this->success(__('Uploaded successful'), '', ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]); + } + + } + + /** + * 通用排序 + */ + public function weigh() + { + //排序的数组 + $ids = $this->request->post("ids"); + //拖动的记录ID + $changeid = $this->request->post("changeid"); + //操作字段 + $field = $this->request->post("field"); + //操作的数据表 + $table = $this->request->post("table"); + if (!Validate::is($table, "alphaDash")) { + $this->error(); + } + //主键 + $pk = $this->request->post("pk"); + //排序的方式 + $orderway = strtolower($this->request->post("orderway", "")); + $orderway = $orderway == 'asc' ? 'ASC' : 'DESC'; + $sour = $weighdata = []; + $ids = explode(',', $ids); + $prikey = $pk && preg_match("/^[a-z0-9\-_]+$/i", $pk) ? $pk : (Db::name($table)->getPk() ?: 'id'); + $pid = $this->request->post("pid", ""); + //限制更新的字段 + $field = in_array($field, ['weigh']) ? $field : 'weigh'; + + // 如果设定了pid的值,此时只匹配满足条件的ID,其它忽略 + if ($pid !== '') { + $hasids = []; + $list = Db::name($table)->where($prikey, 'in', $ids)->where('pid', 'in', $pid)->field("{$prikey},pid")->select(); + foreach ($list as $k => $v) { + $hasids[] = $v[$prikey]; + } + $ids = array_values(array_intersect($ids, $hasids)); + } + + $list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select(); + foreach ($list as $k => $v) { + $sour[] = $v[$prikey]; + $weighdata[$v[$prikey]] = $v[$field]; + } + $position = array_search($changeid, $ids); + $desc_id = $sour[$position]; //移动到目标的ID值,取出所处改变前位置的值 + $sour_id = $changeid; + $weighids = array(); + $temp = array_values(array_diff_assoc($ids, $sour)); + foreach ($temp as $m => $n) { + if ($n == $sour_id) { + $offset = $desc_id; + } else { + if ($sour_id == $temp[0]) { + $offset = isset($temp[$m + 1]) ? $temp[$m + 1] : $sour_id; + } else { + $offset = isset($temp[$m - 1]) ? $temp[$m - 1] : $sour_id; + } + } + if (!isset($weighdata[$offset])) { + continue; + } + $weighids[$n] = $weighdata[$offset]; + Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]); + } + $this->success(); + } + + /** + * 清空系统缓存 + */ + public function wipecache() + { + $type = $this->request->request("type"); + switch ($type) { + case 'all': + case 'content': + rmdirs(CACHE_PATH, false); + Cache::clear(); + if ($type == 'content') { + break; + } + case 'template': + rmdirs(TEMP_PATH, false); + if ($type == 'template') { + break; + } + case 'addons': + Service::refresh(); + if ($type == 'addons') { + break; + } + } + + \think\Hook::listen("wipecache_after"); + $this->success(); + } + + /** + * 读取分类数据,联动列表 + */ + public function category() + { + $type = $this->request->get('type', ''); + $pid = $this->request->get('pid', ''); + $where = ['status' => 'normal']; + $categorylist = null; + if ($pid || $pid === '0') { + $where['pid'] = $pid; + } + if ($type) { + $where['type'] = $type; + } + + $categorylist = Db::name('category')->where($where)->field('id as value,name')->order('weigh desc,id desc')->select(); + + $this->success('', '', $categorylist); + } + + /** + * 读取省市区数据,联动列表 + */ + public function area() + { + $params = $this->request->get("row/a"); + if (!empty($params)) { + $province = isset($params['province']) ? $params['province'] : ''; + $city = isset($params['city']) ? $params['city'] : ''; + } else { + $province = $this->request->get('province', ''); + $city = $this->request->get('city', ''); + } + $where = ['pid' => 0, 'level' => 1]; + $provincelist = null; + if ($province !== '') { + $where['pid'] = $province; + $where['level'] = 2; + if ($city !== '') { + $where['pid'] = $city; + $where['level'] = 3; + } + } + $provincelist = Db::name('area')->where($where)->field('id as value,name')->select(); + $this->success('', '', $provincelist); + } + + /** + * 生成后缀图标 + */ + public function icon() + { + $suffix = $this->request->request("suffix"); + header('Content-type: image/svg+xml'); + $suffix = $suffix ? $suffix : "FILE"; + echo build_suffix_image($suffix); + exit; + } + +} diff --git a/application/admin/controller/Category.php b/application/admin/controller/Category.php index c1cb54d2b6b123e0b9612925b2964c251ad4ca3d..5172a10a9103be361543b80e583bb7e02cbb8cee 100644 --- a/application/admin/controller/Category.php +++ b/application/admin/controller/Category.php @@ -1,69 +1,158 @@ -request->filter(['strip_tags']); - $this->model = model('Category'); - - $tree = Tree::instance(); - $tree->init($this->model->order('weigh desc,id desc')->select(), 'pid'); - $this->categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); - $categorydata = [0 => __('None')]; - foreach ($this->categorylist as $k => $v) - { - $categorydata[$v['id']] = $v['name']; - } - $this->view->assign("flagList", $this->model->getFlagList()); - $this->view->assign("typeList", CategoryModel::getTypeList()); - $this->view->assign("parentList", $categorydata); - } - - /** - * 查看 - */ - public function index() - { - if ($this->request->isAjax()) - { - - //构造父类select列表选项数据 - $list = $this->categorylist; - $total = count($list); - $result = array("total" => 1, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * Selectpage搜索 - * - * @internal - */ - public function selectpage() - { - return parent::selectpage(); - } - -} +系统配置->字典配置中添加 + */ +class Category extends Backend +{ + + /** + * @var \app\common\model\Category + */ + protected $model = null; + protected $categorylist = []; + protected $noNeedRight = ['selectpage']; + + public function _initialize() + { + parent::_initialize(); + $this->model = model('app\common\model\Category'); + + $tree = Tree::instance(); + $tree->init(collection($this->model->order('weigh desc,id desc')->select())->toArray(), 'pid'); + $this->categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); + $categorydata = [0 => ['type' => 'all', 'name' => __('None')]]; + foreach ($this->categorylist as $k => $v) { + $categorydata[$v['id']] = $v; + } + $typeList = CategoryModel::getTypeList(); + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("typeList", $typeList); + $this->view->assign("parentList", $categorydata); + $this->assignconfig('typeList', $typeList); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $search = $this->request->request("search"); + $type = $this->request->request("type"); + + //构造父类select列表选项数据 + $list = []; + + foreach ($this->categorylist as $k => $v) { + if ($search) { + if ($v['type'] == $type && stripos($v['name'], $search) !== false || stripos($v['nickname'], $search) !== false) { + if ($type == "all" || $type == null) { + $list = $this->categorylist; + } else { + $list[] = $v; + } + } + } else { + if ($type == "all" || $type == null) { + $list = $this->categorylist; + } elseif ($v['type'] == $type) { + $list[] = $v; + } + } + } + + $total = count($list); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + if (!in_array($row[$this->dataLimitField], $adminIds)) { + $this->error(__('You have no permission')); + } + } + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + + if ($params['pid'] != $row['pid']) { + $childrenIds = Tree::instance()->init(collection(\app\common\model\Category::select())->toArray())->getChildrenIds($row['id'], true); + if (in_array($params['pid'], $childrenIds)) { + $this->error(__('Can not change the parent to child or itself')); + } + } + + try { + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this->model)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate; + $row->validate($validate); + } + $result = $row->allowField(true)->save($params); + if ($result !== false) { + $this->success(); + } else { + $this->error($row->getError()); + } + } catch (\think\exception\PDOException $e) { + $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + + /** + * Selectpage搜索 + * + * @internal + */ + public function selectpage() + { + return parent::selectpage(); + } +} diff --git a/application/admin/controller/Dashboard.php b/application/admin/controller/Dashboard.php index 7017b757a133ac2ce279fd0c0e1223cae3bebb9d..4175baf848d38f89ce78f1d136c6b90629e98538 100644 --- a/application/admin/controller/Dashboard.php +++ b/application/admin/controller/Dashboard.php @@ -1,48 +1,56 @@ -view->assign([ - 'totaluser' => 35200, - 'totalviews' => 219390, - 'totalorder' => 32143, - 'totalorderamount' => 174800, - 'todayuserlogin' => 321, - 'todayusersignup' => 430, - 'todayorder' => 2324, - 'todayunsettleorder' => 132, - 'sevendnu' => '80%', - 'sevendau' => '32%', - 'paylist' => $paylist, - 'createlist' => $createlist, - ]); - - - return $this->view->fetch(); - } - -} +view->assign([ + 'totaluser' => 35200, + 'totalviews' => 219390, + 'totalorder' => 32143, + 'totalorderamount' => 174800, + 'todayuserlogin' => 321, + 'todayusersignup' => 430, + 'todayorder' => 2324, + 'unsettleorder' => 132, + 'sevendnu' => '80%', + 'sevendau' => '32%', + 'paylist' => $paylist, + 'createlist' => $createlist, + 'addonversion' => $addonVersion, + 'uploadmode' => $uploadmode + ]); + + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php index b2f2048f4dbde1a0ca620f0fe32afe5826c5b6d0..ae3aebbf6a1aef4325f929952ab0e54c2f546c27 100644 --- a/application/admin/controller/Index.php +++ b/application/admin/controller/Index.php @@ -1,109 +1,122 @@ -auth->getSidebar([ - 'dashboard' => 'hot', - 'auth' => ['new', 'red', 'badge'], - 'auth/admin' => 12, - 'auth/rule' => 4, - 'general' => ['18', 'purple'], - ], $this->view->site['fixedpage']); - $this->view->assign('menulist', $menulist); - $this->view->assign('title', __('Home')); - return $this->view->fetch(); - } - - /** - * 管理员登录 - */ - public function login() - { - $url = $this->request->get('url', 'index/index'); - if ($this->auth->isLogin()) - { - $this->error(__("You've logged in, do not login again"), $url); - return; - } - if ($this->request->isPost()) - { - $username = $this->request->post('username'); - $password = $this->request->post('password'); - $keeplogin = $this->request->post('keeplogin'); - $token = $this->request->post('__token__'); - $rule = [ - 'username' => 'require|length:3,30', - 'password' => 'require|length:3,30', - '__token__' => 'token', - ]; - $data = [ - 'username' => $username, - 'password' => $password, - '__token__' => $token, - ]; - $validate = new Validate($rule); - $result = $validate->check($data); - if (!$result) - { - $this->error($validate->getError(), $url, ['token' => $this->request->token()]); - return; - } - \app\admin\model\AdminLog::setTitle(__('Login')); - $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0); - if ($result === true) - { - $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]); - return; - } - else - { - $this->error(__('Username or password is incorrect'), $url, ['token' => $this->request->token()]); - } - return; - } - - // 根据客户端的cookie,判断是否可以自动登录 - if ($this->auth->autologin()) - { - $this->redirect($url); - } - $this->view->assign('title', __('Login')); - return $this->view->fetch(); - } - - /** - * 注销登录 - */ - public function logout() - { - $this->auth->logout(); - $this->success(__('Logout successful'), 'index/login'); - return; - } - -} +request->filter('trim,strip_tags,htmlspecialchars'); + } + + /** + * 后台首页 + */ + public function index() + { + //左侧菜单 + list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([ + 'dashboard' => 'hot', + 'addon' => ['new', 'red', 'badge'], + 'auth/rule' => __('Menu'), + 'general' => ['new', 'purple'], + ], $this->view->site['fixedpage']); + $action = $this->request->request('action'); + if ($this->request->isPost()) { + if ($action == 'refreshmenu') { + $this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]); + } + } + $this->view->assign('menulist', $menulist); + $this->view->assign('navlist', $navlist); + $this->view->assign('fixedmenu', $fixedmenu); + $this->view->assign('referermenu', $referermenu); + $this->view->assign('title', __('Home')); + return $this->view->fetch(); + } + + /** + * 管理员登录 + */ + public function login() + { + $url = $this->request->get('url', 'index/index'); + if ($this->auth->isLogin()) { + $this->success(__("You've logged in, do not login again"), $url); + } + if ($this->request->isPost()) { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $keeplogin = $this->request->post('keeplogin'); + $token = $this->request->post('__token__'); + $rule = [ + 'username' => 'require|length:3,30', + 'password' => 'require|length:3,30', + '__token__' => 'require|token', + ]; + $data = [ + 'username' => $username, + 'password' => $password, + '__token__' => $token, + ]; + if (Config::get('fastadmin.login_captcha')) { + $rule['captcha'] = 'require|captcha'; + $data['captcha'] = $this->request->post('captcha'); + } + $validate = new Validate($rule, [], ['username' => __('Username'), 'password' => __('Password'), 'captcha' => __('Captcha')]); + $result = $validate->check($data); + if (!$result) { + $this->error($validate->getError(), $url, ['token' => $this->request->token()]); + } + AdminLog::setTitle(__('Login')); + $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0); + if ($result === true) { + Hook::listen("admin_login_after", $this->request); + $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]); + } else { + $msg = $this->auth->getError(); + $msg = $msg ? $msg : __('Username or password is incorrect'); + $this->error($msg, $url, ['token' => $this->request->token()]); + } + } + + // 根据客户端的cookie,判断是否可以自动登录 + if ($this->auth->autologin()) { + $this->redirect($url); + } + $background = Config::get('fastadmin.login_background'); + $background = $background ? (stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background) : ''; + $this->view->assign('background', $background); + $this->view->assign('title', __('Login')); + Hook::listen("admin_login_init", $this->request); + return $this->view->fetch(); + } + + /** + * 退出登录 + */ + public function logout() + { + $this->auth->logout(); + Hook::listen("admin_logout_after", $this->request); + $this->success(__('Logout successful'), 'index/login'); + } + +} diff --git a/application/admin/controller/Page.php b/application/admin/controller/Page.php deleted file mode 100644 index aeacb90bfd63605e6fc96a0f26b9f49a3aeb324d..0000000000000000000000000000000000000000 --- a/application/admin/controller/Page.php +++ /dev/null @@ -1,51 +0,0 @@ -model = model('Page'); - } - - /** - * 查看 - */ - public function index() - { - if ($this->request->isAjax()) - { - list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->with("category") - ->where($where) - ->order($sort, $order) - ->count(); - $list = $this->model - ->with("category") - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - -} diff --git a/application/admin/controller/User.php b/application/admin/controller/User.php deleted file mode 100644 index cd7748a283a44ea8335d2a9587bd4469ae97d6fd..0000000000000000000000000000000000000000 --- a/application/admin/controller/User.php +++ /dev/null @@ -1,27 +0,0 @@ -model = model('User'); - } - -} diff --git a/application/admin/controller/Version.php b/application/admin/controller/Version.php deleted file mode 100644 index e0360f7ca456bb3635e1e7c33371ca1f0c5b840a..0000000000000000000000000000000000000000 --- a/application/admin/controller/Version.php +++ /dev/null @@ -1,26 +0,0 @@ -model = model('Version'); - } - -} diff --git a/application/admin/controller/auth/Admin.php b/application/admin/controller/auth/Admin.php index f4a548728d7768f72c5973112f4c6db5fbfbb525..bd7f3b2ff4fc15139fd96df25b183edb277dbb99 100644 --- a/application/admin/controller/auth/Admin.php +++ b/application/admin/controller/auth/Admin.php @@ -2,9 +2,12 @@ namespace app\admin\controller\auth; +use app\admin\model\AuthGroup; +use app\admin\model\AuthGroupAccess; use app\common\controller\Backend; use fast\Random; use fast\Tree; +use think\Validate; /** * 管理员管理 @@ -15,34 +18,48 @@ use fast\Tree; class Admin extends Backend { + /** + * @var \app\admin\model\Admin + */ protected $model = null; - //当前登录管理员所有子节点组别 - protected $childrenIds = []; + protected $selectpageFields = 'id,username,nickname,avatar'; + protected $searchFields = 'id,username,nickname'; + protected $childrenGroupIds = []; + protected $childrenAdminIds = []; public function _initialize() { parent::_initialize(); $this->model = model('Admin'); - $groups = $this->auth->getGroups(); - - // 取出所有分组 - $grouplist = model('AuthGroup')->all(['status' => 'normal']); - $objlist = []; - foreach ($groups as $K => $v) - { - // 取出包含自己的所有子节点 - $childrenlist = Tree::instance()->init($grouplist)->getChildren($v['id'], TRUE); - $obj = Tree::instance()->init($childrenlist)->getTreeArray($v['pid']); - $objlist = array_merge($objlist, Tree::instance()->getTreeList($obj)); - } + $this->childrenAdminIds = $this->auth->getChildrenAdminIds(true); + $this->childrenGroupIds = $this->auth->getChildrenGroupIds(true); + + $groupList = collection(AuthGroup::where('id', 'in', $this->childrenGroupIds)->select())->toArray(); + + Tree::instance()->init($groupList); $groupdata = []; - foreach ($objlist as $k => $v) - { - $groupdata[$v['id']] = $v['name']; + if ($this->auth->isSuperAdmin()) { + $result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0)); + foreach ($result as $k => $v) { + $groupdata[$v['id']] = $v['name']; + } + } else { + $result = []; + $groups = $this->auth->getGroups(); + foreach ($groups as $m => $n) { + $childlist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['id'])); + $temp = []; + foreach ($childlist as $k => $v) { + $temp[$v['id']] = $v['name']; + } + $result[__($n['name'])] = $temp; + } + $groupdata = $result; } - $this->childrenIds = array_keys($groupdata); + $this->view->assign('groupdata', $groupdata); + $this->assignconfig("admin", ['id' => $this->auth->id]); } /** @@ -50,44 +67,46 @@ class Admin extends Backend */ public function index() { - if ($this->request->isAjax()) - { - $groupData = model('AuthGroup')->where('status', 'normal')->column('id,name'); - - $childrenAdminIds = []; - $authGroupList = model('AuthGroupAccess') - ->field('uid,group_id') - ->where('group_id', 'in', $this->childrenIds) - ->select(); - + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + $childrenGroupIds = $this->childrenGroupIds; + $groupName = AuthGroup::where('id', 'in', $childrenGroupIds) + ->column('id,name'); + $authGroupList = AuthGroupAccess::where('group_id', 'in', $childrenGroupIds) + ->field('uid,group_id') + ->select(); + $adminGroupName = []; - foreach ($authGroupList as $k => $v) - { - $childrenAdminIds[] = $v['uid']; - if (isset($groupData[$v['group_id']])) - $adminGroupName[$v['uid']][$v['group_id']] = $groupData[$v['group_id']]; + foreach ($authGroupList as $k => $v) { + if (isset($groupName[$v['group_id']])) { + $adminGroupName[$v['uid']][$v['group_id']] = $groupName[$v['group_id']]; + } + } + $groups = $this->auth->getGroups(); + foreach ($groups as $m => $n) { + $adminGroupName[$this->auth->id][$n['id']] = $n['name']; } list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->where($where) - ->where('id', 'in', $childrenAdminIds) - ->order($sort, $order) - ->count(); $list = $this->model - ->where($where) - ->where('id', 'in', $childrenAdminIds) - ->field(['password', 'salt', 'token'], true) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - foreach ($list as $k => &$v) - { + ->where($where) + ->where('id', 'in', $this->childrenAdminIds) + ->field(['password', 'salt', 'token'], true) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $k => &$v) { $groups = isset($adminGroupName[$v['id']]) ? $adminGroupName[$v['id']] : []; $v['groups'] = implode(',', array_keys($groups)); $v['groups_text'] = implode(',', array_values($groups)); } - $result = array("total" => $total, "rows" => $list); + unset($v); + $result = array("total" => $list->total(), "rows" => $list->items()); return json($result); } @@ -99,31 +118,32 @@ class Admin extends Backend */ public function add() { - if ($this->request->isPost()) - { - $this->code = -1; + if ($this->request->isPost()) { + $this->token(); $params = $this->request->post("row/a"); - if ($params) - { + if ($params) { + if (!Validate::is($params['password'], '\S{6,16}')) { + $this->error(__("Please input correct password")); + } $params['salt'] = Random::alnum(); $params['password'] = md5(md5($params['password']) . $params['salt']); $params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。 - - $admin = $this->model->create($params); + $result = $this->model->validate('Admin.add')->save($params); + if ($result === false) { + $this->error($this->model->getError()); + } $group = $this->request->post("group/a"); //过滤不允许的组别,避免越权 - $group = array_intersect($this->childrenIds, $group); + $group = array_intersect($this->childrenGroupIds, $group); $dataset = []; - foreach ($group as $value) - { - $dataset[] = ['uid' => $admin->id, 'group_id' => $value]; + foreach ($group as $value) { + $dataset[] = ['uid' => $this->model->id, 'group_id' => $value]; } model('AuthGroupAccess')->saveAll($dataset); - $this->code = 1; + $this->success(); } - - return; + $this->error(); } return $this->view->fetch(); } @@ -131,27 +151,39 @@ class Admin extends Backend /** * 编辑 */ - public function edit($ids = NULL) + public function edit($ids = null) { $row = $this->model->get(['id' => $ids]); - if (!$row) + if (!$row) { $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $this->code = -1; + } + if (!in_array($row->id, $this->childrenAdminIds)) { + $this->error(__('You have no permission')); + } + if ($this->request->isPost()) { + $this->token(); $params = $this->request->post("row/a"); - if ($params) - { - if ($params['password']) - { + if ($params) { + if ($params['password']) { + if (!Validate::is($params['password'], '\S{6,16}')) { + $this->error(__("Please input correct password")); + } $params['salt'] = Random::alnum(); $params['password'] = md5(md5($params['password']) . $params['salt']); - } - else - { + } else { unset($params['password'], $params['salt']); } - $row->save($params); + //这里需要针对username和email做唯一验证 + $adminValidate = \think\Loader::validate('Admin'); + $adminValidate->rule([ + 'username' => 'require|regex:\w{3,12}|unique:admin,username,' . $row->id, + 'email' => 'require|email|unique:admin,email,' . $row->id, + 'password' => 'regex:\S{32}', + ]); + $result = $row->validate('Admin.edit')->save($params); + if ($result === false) { + $this->error($row->getError()); + } // 先移除所有权限 model('AuthGroupAccess')->where('uid', $row->id)->delete(); @@ -159,23 +191,20 @@ class Admin extends Backend $group = $this->request->post("group/a"); // 过滤不允许的组别,避免越权 - $group = array_intersect($this->childrenIds, $group); + $group = array_intersect($this->childrenGroupIds, $group); $dataset = []; - foreach ($group as $value) - { + foreach ($group as $value) { $dataset[] = ['uid' => $row->id, 'group_id' => $value]; } model('AuthGroupAccess')->saveAll($dataset); - $this->code = 1; + $this->success(); } - - return; + $this->error(); } $grouplist = $this->auth->getGroups($row['id']); $groupids = []; - foreach ($grouplist as $k => $v) - { + foreach ($grouplist as $k => $v) { $groupids[] = $v['id']; } $this->view->assign("row", $row); @@ -188,32 +217,31 @@ class Admin extends Backend */ public function del($ids = "") { - $this->code = -1; - if ($ids) - { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $ids = array_intersect($this->childrenAdminIds, array_filter(explode(',', $ids))); // 避免越权删除管理员 - $childrenGroupIds = $this->childrenIds; - $adminList = $this->model->where('id', 'in', $ids)->where('id', 'in', function($query) use($childrenGroupIds) { - $query->name('auth_group_access')->where('group_id', 'in', $childrenGroupIds)->field('uid'); - })->select(); - if ($adminList) - { + $childrenGroupIds = $this->childrenGroupIds; + $adminList = $this->model->where('id', 'in', $ids)->where('id', 'in', function ($query) use ($childrenGroupIds) { + $query->name('auth_group_access')->where('group_id', 'in', $childrenGroupIds)->field('uid'); + })->select(); + if ($adminList) { $deleteIds = []; - foreach ($adminList as $k => $v) - { + foreach ($adminList as $k => $v) { $deleteIds[] = $v->id; } - $deleteIds = array_diff($deleteIds, [$this->auth->id]); - if ($deleteIds) - { + $deleteIds = array_values(array_diff($deleteIds, [$this->auth->id])); + if ($deleteIds) { $this->model->destroy($deleteIds); model('AuthGroupAccess')->where('uid', 'in', $deleteIds)->delete(); - $this->code = 1; + $this->success(); } } } - - return; + $this->error(__('You have no permission')); } /** @@ -223,7 +251,16 @@ class Admin extends Backend public function multi($ids = "") { // 管理员禁止批量操作 - $this->code = -1; + $this->error(); } + /** + * 下拉搜索 + */ + public function selectpage() + { + $this->dataLimit = 'auth'; + $this->dataLimitField = 'id'; + return parent::selectpage(); + } } diff --git a/application/admin/controller/auth/Adminlog.php b/application/admin/controller/auth/Adminlog.php index d33cff883ecd77ecace92914ce86541ee2bdc02d..998fdc8ac0df6aa967ba0b53a4bb08aa117eb025 100644 --- a/application/admin/controller/auth/Adminlog.php +++ b/application/admin/controller/auth/Adminlog.php @@ -2,46 +2,37 @@ namespace app\admin\controller\auth; +use app\admin\model\AuthGroup; use app\common\controller\Backend; -use fast\Tree; /** * 管理员日志 * - * @icon fa fa-users + * @icon fa fa-users * @remark 管理员可以查看自己所拥有的权限的管理员日志 */ class Adminlog extends Backend { + /** + * @var \app\admin\model\AdminLog + */ protected $model = null; - //当前登录管理员所有子节点组别 - protected $childrenIds = []; + protected $childrenGroupIds = []; + protected $childrenAdminIds = []; public function _initialize() { parent::_initialize(); $this->model = model('AdminLog'); - $groups = $this->auth->getGroups(); - - // 取出所有分组 - $grouplist = model('AuthGroup')->all(['status' => 'normal']); - $objlist = []; - foreach ($groups as $K => $v) - { - // 取出包含自己的所有子节点 - $childrenlist = Tree::instance()->init($grouplist)->getChildren($v['id'], TRUE); - $obj = Tree::instance()->init($childrenlist)->getTreeArray($v['pid']); - $objlist = array_merge($objlist, Tree::instance()->getTreeList($obj)); - } - $groupdata = []; - foreach ($objlist as $k => $v) - { - $groupdata[$v['id']] = $v['name']; - } - $this->childrenIds = array_keys($groupdata); - $this->view->assign('groupdata', $groupdata); + $this->childrenAdminIds = $this->auth->getChildrenAdminIds(true); + $this->childrenGroupIds = $this->auth->getChildrenGroupIds($this->auth->isSuperAdmin() ? true : false); + + $groupName = AuthGroup::where('id', 'in', $this->childrenGroupIds) + ->column('id,name'); + + $this->view->assign('groupdata', $groupName); } /** @@ -49,40 +40,32 @@ class Adminlog extends Backend */ public function index() { - if ($this->request->isAjax()) - { - $childrenAdminIds = model('AuthGroupAccess') - ->field('uid') - ->where('group_id', 'in', $this->childrenIds) - ->column('uid'); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->where($where) - ->where('admin_id', 'in', $childrenAdminIds) - ->order($sort, $order) - ->count(); - $list = $this->model - ->where($where) - ->where('admin_id', 'in', $childrenAdminIds) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $result = array("total" => $total, "rows" => $list); + ->where($where) + ->where('admin_id', 'in', $this->childrenAdminIds) + ->order($sort, $order) + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); return json($result); } return $this->view->fetch(); } - + /** * 详情 */ public function detail($ids) { $row = $this->model->get(['id' => $ids]); - if (!$row) + if (!$row) { $this->error(__('No Results were found')); + } $this->view->assign("row", $row->toArray()); return $this->view->fetch(); } @@ -93,16 +76,16 @@ class Adminlog extends Backend */ public function add() { - $this->code = -1; + $this->error(); } /** * 编辑 * @internal */ - public function edit($ids = NULL) + public function edit($ids = null) { - $this->code = -1; + $this->error(); } /** @@ -110,29 +93,27 @@ class Adminlog extends Backend */ public function del($ids = "") { - $this->code = -1; - if ($ids) - { - $childrenGroupIds = $this->childrenIds; - $adminList = $this->model->where('id', 'in', $ids)->where('admin_id', 'in', function($query) use($childrenGroupIds) { - $query->name('auth_group_access')->field('uid'); - })->select(); - if ($adminList) - { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $childrenGroupIds = $this->childrenGroupIds; + $adminList = $this->model->where('id', 'in', $ids)->where('admin_id', 'in', function ($query) use ($childrenGroupIds) { + $query->name('auth_group_access')->field('uid'); + })->select(); + if ($adminList) { $deleteIds = []; - foreach ($adminList as $k => $v) - { + foreach ($adminList as $k => $v) { $deleteIds[] = $v->id; } - if ($deleteIds) - { + if ($deleteIds) { $this->model->destroy($deleteIds); - $this->code = 1; + $this->success(); } } } - - return; + $this->error(); } /** @@ -142,7 +123,11 @@ class Adminlog extends Backend public function multi($ids = "") { // 管理员禁止批量操作 - $this->code = -1; + $this->error(); } + public function selectpage() + { + return parent::selectpage(); + } } diff --git a/application/admin/controller/auth/Group.php b/application/admin/controller/auth/Group.php index 744f8ced8a54b29a59d8f2a4545ec2d97e2abb27..218ae8dd6eb505e2daa76464866b80de1dc1237a 100644 --- a/application/admin/controller/auth/Group.php +++ b/application/admin/controller/auth/Group.php @@ -2,21 +2,27 @@ namespace app\admin\controller\auth; +use app\admin\model\AuthGroup; use app\common\controller\Backend; use fast\Tree; +use think\Db; +use think\Exception; /** * 角色组 * - * @icon fa fa-group + * @icon fa fa-group * @remark 角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别下级的角色组或管理员 */ class Group extends Backend { + /** + * @var \app\admin\model\AuthGroup + */ protected $model = null; - //当前登录管理员所有子节点组别 - protected $childrenIds = []; + //当前登录管理员所有子组别 + protected $childrenGroupIds = []; //当前组别列表数据 protected $groupdata = []; //无需要权限判断的方法 @@ -27,27 +33,29 @@ class Group extends Backend parent::_initialize(); $this->model = model('AuthGroup'); - $groups = $this->auth->getGroups(); + $this->childrenGroupIds = $this->auth->getChildrenGroupIds(true); - // 取出所有分组 - $grouplist = model('AuthGroup')->all(['status' => 'normal']); - $objlist = []; - foreach ($groups as $K => $v) - { - // 取出包含自己的所有子节点 - $childrenlist = Tree::instance()->init($grouplist)->getChildren($v['id'], TRUE); - $obj = Tree::instance()->init($childrenlist)->getTreeArray($v['pid']); - $objlist = array_merge($objlist, Tree::instance()->getTreeList($obj)); - } + $groupList = collection(AuthGroup::where('id', 'in', $this->childrenGroupIds)->select())->toArray(); - $groupdata = []; - foreach ($objlist as $k => $v) - { - $groupdata[$v['id']] = $v['name']; + Tree::instance()->init($groupList); + $result = []; + if ($this->auth->isSuperAdmin()) { + $result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0)); + } else { + $groups = $this->auth->getGroups(); + foreach ($groups as $m => $n) { + $result = array_merge($result, Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['pid']))); + } } - $this->groupdata = $groupdata; - $this->childrenIds = array_keys($groupdata); - $this->view->assign('groupdata', $groupdata); + $groupName = []; + foreach ($result as $k => $v) { + $groupName[$v['id']] = $v['name']; + } + + $this->groupdata = $groupName; + $this->assignconfig("admin", ['id' => $this->auth->id, 'group_ids' => $this->auth->getGroupIds()]); + + $this->view->assign('groupdata', $this->groupdata); } /** @@ -55,14 +63,19 @@ class Group extends Backend */ public function index() { - if ($this->request->isAjax()) - { + if ($this->request->isAjax()) { + $list = AuthGroup::all(array_keys($this->groupdata)); + $list = collection($list)->toArray(); + $groupList = []; + foreach ($list as $k => $v) { + $groupList[$v['id']] = $v; + } $list = []; - foreach ($this->groupdata as $k => $v) - { - $data = $this->model->get($k); - $data->name = $v; - $list[] = $data; + foreach ($this->groupdata as $k => $v) { + if (isset($groupList[$k])) { + $groupList[$k]['name'] = $v; + $list[] = $groupList[$k]; + } } $total = count($list); $result = array("total" => $total, "rows" => $list); @@ -77,22 +90,16 @@ class Group extends Backend */ public function add() { - if ($this->request->isPost()) - { - $this->code = -1; + if ($this->request->isPost()) { + $this->token(); $params = $this->request->post("row/a", [], 'strip_tags'); $params['rules'] = explode(',', $params['rules']); - if (!in_array($params['pid'], $this->childrenIds)) - { - $this->code = -1; - $this->msg = __(''); - return; + if (!in_array($params['pid'], $this->childrenGroupIds)) { + $this->error(__('The parent group exceeds permission limit')); } $parentmodel = model("AuthGroup")->get($params['pid']); - if (!$parentmodel) - { - $this->msg = __('The parent group can not found'); - return; + if (!$parentmodel) { + $this->error(__('The parent group can not found')); } // 父级别的规则节点 $parentrules = explode(',', $parentmodel->rules); @@ -104,13 +111,11 @@ class Group extends Backend // 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限 $rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules); $params['rules'] = implode(',', $rules); - if ($params) - { + if ($params) { $this->model->create($params); - $this->code = 1; + $this->success(); } - - return; + $this->error(); } return $this->view->fetch(); } @@ -118,28 +123,31 @@ class Group extends Backend /** * 编辑 */ - public function edit($ids = NULL) + public function edit($ids = null) { + if (!in_array($ids, $this->childrenGroupIds)) { + $this->error(__('You have no permission')); + } $row = $this->model->get(['id' => $ids]); - if (!$row) + if (!$row) { $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $this->code = -1; + } + if ($this->request->isPost()) { + $this->token(); $params = $this->request->post("row/a", [], 'strip_tags'); - // 父节点不能是它自身的子节点 - if (!in_array($params['pid'], $this->childrenIds)) - { - $this->msg = __('The parent group can not be its own child'); - return; + //父节点不能是非权限内节点 + if (!in_array($params['pid'], $this->childrenGroupIds)) { + $this->error(__('The parent group exceeds permission limit')); + } + // 父节点不能是它自身的子节点或自己本身 + if (in_array($params['pid'], Tree::instance()->getChildrenIds($row->id, true))) { + $this->error(__('The parent group can not be its own child or itself')); } $params['rules'] = explode(',', $params['rules']); $parentmodel = model("AuthGroup")->get($params['pid']); - if (!$parentmodel) - { - $this->msg = __('The parent group can not found'); - return; + if (!$parentmodel) { + $this->error(__('The parent group can not found')); } // 父级别的规则节点 $parentrules = explode(',', $parentmodel->rules); @@ -151,12 +159,25 @@ class Group extends Backend // 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限 $rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules); $params['rules'] = implode(',', $rules); - if ($params) - { - $row->save($params); - $this->code = 1; + if ($params) { + Db::startTrans(); + try { + $row->save($params); + $children_auth_groups = model("AuthGroup")->all(['id' => ['in', implode(',', (Tree::instance()->getChildrenIds($row->id)))]]); + $childparams = []; + foreach ($children_auth_groups as $key => $children_auth_group) { + $childparams[$key]['id'] = $children_auth_group->id; + $childparams[$key]['rules'] = implode(',', array_intersect(explode(',', $children_auth_group->rules), $rules)); + } + model("AuthGroup")->saveAll($childparams); + Db::commit(); + $this->success(); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } } - + $this->error(); return; } $this->view->assign("row", $row); @@ -168,12 +189,14 @@ class Group extends Backend */ public function del($ids = "") { - $this->code = -1; - if ($ids) - { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { $ids = explode(',', $ids); $grouplist = $this->auth->getGroups(); - $group_ids = array_map(function($group) { + $group_ids = array_map(function ($group) { return $group['id']; }, $grouplist); // 移除掉当前管理员所在组别 @@ -182,35 +205,29 @@ class Group extends Backend // 循环判断每一个组别是否可删除 $grouplist = $this->model->where('id', 'in', $ids)->select(); $groupaccessmodel = model('AuthGroupAccess'); - foreach ($grouplist as $k => $v) - { + foreach ($grouplist as $k => $v) { // 当前组别下有管理员 $groupone = $groupaccessmodel->get(['group_id' => $v['id']]); - if ($groupone) - { + if ($groupone) { $ids = array_diff($ids, [$v['id']]); continue; } // 当前组别下有子组别 $groupone = $this->model->get(['pid' => $v['id']]); - if ($groupone) - { + if ($groupone) { $ids = array_diff($ids, [$v['id']]); continue; } } - if (!$ids) - { - $this->msg = __('You can not delete group that contain child group and administrators'); - return; + if (!$ids) { + $this->error(__('You can not delete group that contain child group and administrators')); } $count = $this->model->where('id', 'in', $ids)->delete(); - if ($count) - { - $this->code = 1; + if ($count) { + $this->success(); } } - return; + $this->error(); } /** @@ -220,13 +237,12 @@ class Group extends Backend public function multi($ids = "") { // 组别禁止批量操作 - $this->code = -1; - return; + $this->error(); } /** * 读取角色权限树 - * + * * @internal */ public function roletree() @@ -236,75 +252,69 @@ class Group extends Backend $model = model('AuthGroup'); $id = $this->request->post("id"); $pid = $this->request->post("pid"); - $parentgroupmodel = $model->get($pid); - $currentgroupmodel = NULL; - if ($id) - { - $currentgroupmodel = $model->get($id); + $parentGroupModel = $model->get($pid); + $currentGroupModel = null; + if ($id) { + $currentGroupModel = $model->get($id); } - if (($pid || $parentgroupmodel) && (!$id || $currentgroupmodel)) - { - $id = $id ? $id : NULL; - $ruleList = collection(model('AuthRule')->order('weigh', 'desc')->select())->toArray(); + if (($pid || $parentGroupModel) && (!$id || $currentGroupModel)) { + $id = $id ? $id : null; + $ruleList = collection(model('AuthRule')->order('weigh', 'desc')->order('id', 'asc')->select())->toArray(); //读取父类角色所有节点列表 $parentRuleList = []; - if (in_array('*', explode(',', $parentgroupmodel->rules))) - { + if (in_array('*', explode(',', $parentGroupModel->rules))) { $parentRuleList = $ruleList; - } - else - { - $parent_rule_ids = explode(',', $parentgroupmodel->rules); - foreach ($ruleList as $k => $v) - { - if (in_array($v['id'], $parent_rule_ids)) - { + } else { + $parentRuleIds = explode(',', $parentGroupModel->rules); + foreach ($ruleList as $k => $v) { + if (in_array($v['id'], $parentRuleIds)) { $parentRuleList[] = $v; } } } + $ruleTree = new Tree(); + $groupTree = new Tree(); //当前所有正常规则列表 - Tree::instance()->init($ruleList); + $ruleTree->init($parentRuleList); + //角色组列表 + $groupTree->init(collection(model('AuthGroup')->where('id', 'in', $this->childrenGroupIds)->select())->toArray()); //读取当前角色下规则ID集合 - $admin_rule_ids = $this->auth->getRuleIds(); + $adminRuleIds = $this->auth->getRuleIds(); //是否是超级管理员 $superadmin = $this->auth->isSuperAdmin(); //当前拥有的规则ID集合 - $current_rule_ids = $id ? explode(',', $currentgroupmodel->rules) : []; + $currentRuleIds = $id ? explode(',', $currentGroupModel->rules) : []; - if (!$id || !in_array($pid, Tree::instance()->getChildrenIds($id, TRUE))) - { - $ruleList = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'name'); + if (!$id || !in_array($pid, $this->childrenGroupIds) || !in_array($pid, $groupTree->getChildrenIds($id, true))) { + $parentRuleList = $ruleTree->getTreeList($ruleTree->getTreeArray(0), 'name'); $hasChildrens = []; - foreach ($ruleList as $k => $v) - { - if ($v['haschild']) + foreach ($parentRuleList as $k => $v) { + if ($v['haschild']) { $hasChildrens[] = $v['id']; + } } - $nodelist = []; - foreach ($parentRuleList as $k => $v) - { - if (!$superadmin && !in_array($v['id'], $admin_rule_ids)) + $parentRuleIds = array_map(function ($item) { + return $item['id']; + }, $parentRuleList); + $nodeList = []; + foreach ($parentRuleList as $k => $v) { + if (!$superadmin && !in_array($v['id'], $adminRuleIds)) { continue; - $state = array('selected' => in_array($v['id'], $current_rule_ids) && !in_array($v['id'], $hasChildrens)); - $nodelist[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => $v['title'], 'type' => 'menu', 'state' => $state); + } + if ($v['pid'] && !in_array($v['pid'], $parentRuleIds)) { + continue; + } + $state = array('selected' => in_array($v['id'], $currentRuleIds) && !in_array($v['id'], $hasChildrens)); + $nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state); } - $this->code = 1; - $this->data = $nodelist; + $this->success('', null, $nodeList); + } else { + $this->error(__('Can not change the parent to child')); } - else - { - $this->code = -1; - $this->data = __('Can not change the parent to child'); - } - } - else - { - $this->code = -1; - $this->data = __('Group not found'); + } else { + $this->error(__('Group not found')); } } - } diff --git a/application/admin/controller/auth/Rule.php b/application/admin/controller/auth/Rule.php index a77c9c97c1477fe5c6eb2a8d95e528202062a21f..310d38309006ad89161ac2bcde464255cb0dda07 100644 --- a/application/admin/controller/auth/Rule.php +++ b/application/admin/controller/auth/Rule.php @@ -2,6 +2,7 @@ namespace app\admin\controller\auth; +use app\admin\model\AuthRule; use app\common\controller\Backend; use fast\Tree; use think\Cache; @@ -9,12 +10,15 @@ use think\Cache; /** * 规则管理 * - * @icon fa fa-list + * @icon fa fa-list * @remark 规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过控制台进行生成规则节点 */ class Rule extends Backend { + /** + * @var \app\admin\model\AuthRule + */ protected $model = null; protected $rulelist = []; protected $multiFields = 'ismenu,status'; @@ -22,17 +26,26 @@ class Rule extends Backend public function _initialize() { parent::_initialize(); + if (!$this->auth->isSuperAdmin()) { + $this->error(__('Access is allowed only to the super management group')); + } $this->model = model('AuthRule'); // 必须将结果集转换为数组 - Tree::instance()->init(collection($this->model->order('weigh', 'desc')->select())->toArray()); + $ruleList = collection($this->model->field('condition,remark,createtime,updatetime', true)->order('weigh DESC,id ASC')->select())->toArray(); + foreach ($ruleList as $k => &$v) { + $v['title'] = __($v['title']); + } + unset($v); + Tree::instance()->init($ruleList); $this->rulelist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'title'); $ruledata = [0 => __('None')]; - foreach ($this->rulelist as $k => $v) - { - if (!$v['ismenu']) + foreach ($this->rulelist as $k => &$v) { + if (!$v['ismenu']) { continue; + } $ruledata[$v['id']] = $v['title']; } + unset($v); $this->view->assign('ruledata', $ruledata); } @@ -41,8 +54,7 @@ class Rule extends Backend */ public function index() { - if ($this->request->isAjax()) - { + if ($this->request->isAjax()) { $list = $this->rulelist; $total = count($this->rulelist); @@ -58,23 +70,21 @@ class Rule extends Backend */ public function add() { - if ($this->request->isPost()) - { - $this->code = -1; + if ($this->request->isPost()) { + $this->token(); $params = $this->request->post("row/a", [], 'strip_tags'); - if ($params) - { - if (!$params['ismenu'] && !$params['pid']) - { - $this->msg = __('The non-menu rule must have parent'); - return; + if ($params) { + if (!$params['ismenu'] && !$params['pid']) { + $this->error(__('The non-menu rule must have parent')); + } + $result = $this->model->validate()->save($params); + if ($result === false) { + $this->error($this->model->getError()); } - $this->model->create($params); Cache::rm('__menu__'); - $this->code = 1; + $this->success(); } - - return; + $this->error(); } return $this->view->fetch(); } @@ -82,28 +92,38 @@ class Rule extends Backend /** * 编辑 */ - public function edit($ids = NULL) + public function edit($ids = null) { $row = $this->model->get(['id' => $ids]); - if (!$row) + if (!$row) { $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $this->code = -1; + } + if ($this->request->isPost()) { + $this->token(); $params = $this->request->post("row/a", [], 'strip_tags'); - if ($params) - { - if (!$params['ismenu'] && !$params['pid']) - { - $this->msg = __('The non-menu rule must have parent'); - return; + if ($params) { + if (!$params['ismenu'] && !$params['pid']) { + $this->error(__('The non-menu rule must have parent')); + } + if ($params['pid'] != $row['pid']) { + $childrenIds = Tree::instance()->init(collection(AuthRule::select())->toArray())->getChildrenIds($row['id']); + if (in_array($params['pid'], $childrenIds)) { + $this->error(__('Can not change the parent to child')); + } + } + //这里需要针对name做唯一验证 + $ruleValidate = \think\Loader::validate('AuthRule'); + $ruleValidate->rule([ + 'name' => 'require|format|unique:AuthRule,name,' . $row->id, + ]); + $result = $row->validate()->save($params); + if ($result === false) { + $this->error($row->getError()); } - $row->save($params); Cache::rm('__menu__'); - $this->code = 1; + $this->success(); } - - return; + $this->error(); } $this->view->assign("row", $row); return $this->view->fetch(); @@ -114,24 +134,22 @@ class Rule extends Backend */ public function del($ids = "") { - $this->code = -1; - if ($ids) - { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { $delIds = []; - foreach (explode(',', $ids) as $k => $v) - { - $delIds = array_merge($delIds, Tree::instance()->getChildrenIds($v, TRUE)); + foreach (explode(',', $ids) as $k => $v) { + $delIds = array_merge($delIds, Tree::instance()->getChildrenIds($v, true)); } $delIds = array_unique($delIds); $count = $this->model->where('id', 'in', $delIds)->delete(); - if ($count) - { + if ($count) { Cache::rm('__menu__'); - $this->code = 1; + $this->success(); } } - - return; + $this->error(); } - } diff --git a/application/admin/controller/example/Bootstraptable.php b/application/admin/controller/example/Bootstraptable.php deleted file mode 100644 index 914c84e1918d177dc1fba1aaaa6c98408b9d90b6..0000000000000000000000000000000000000000 --- a/application/admin/controller/example/Bootstraptable.php +++ /dev/null @@ -1,70 +0,0 @@ -model = model('AdminLog'); - } - - /** - * 查看 - */ - public function index() - { - if ($this->request->isAjax()) - { - list($where, $sort, $order, $offset, $limit) = $this->buildparams(NULL); - $total = $this->model - ->where($where) - ->order($sort, $order) - ->count(); - $list = $this->model - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * 详情 - */ - public function detail($ids) - { - $row = $this->model->get(['id' => $ids]); - if (!$row) - $this->error(__('No Results were found')); - $this->view->assign("row", $row->toArray()); - return $this->view->fetch(); - } - - /** - * 变更 - * @internal - */ - public function change() - { - $this->code = 1; - } - -} diff --git a/application/admin/controller/example/Colorbadge.php b/application/admin/controller/example/Colorbadge.php deleted file mode 100644 index 4d0bc2132499b3beac8ea5fcda164dafba5e2072..0000000000000000000000000000000000000000 --- a/application/admin/controller/example/Colorbadge.php +++ /dev/null @@ -1,24 +0,0 @@ -model = model('AdminLog'); - } - -} diff --git a/application/admin/controller/example/Controllerjump.php b/application/admin/controller/example/Controllerjump.php deleted file mode 100644 index 2bd1540e907e37e7a3b1264f30fea88592ee7cff..0000000000000000000000000000000000000000 --- a/application/admin/controller/example/Controllerjump.php +++ /dev/null @@ -1,24 +0,0 @@ -model = model('AdminLog'); - } - -} diff --git a/application/admin/controller/example/Cxselect.php b/application/admin/controller/example/Cxselect.php deleted file mode 100644 index 391c0977ce38b8d4a7d14ac62bd6d6b79ab6721e..0000000000000000000000000000000000000000 --- a/application/admin/controller/example/Cxselect.php +++ /dev/null @@ -1,23 +0,0 @@ -loadlang('general/attachment'); - $this->loadlang('general/crontab'); - return $this->view->fetch(); - } - -} diff --git a/application/admin/controller/example/Relationmodel.php b/application/admin/controller/example/Relationmodel.php deleted file mode 100644 index ae42b7694fd0f22d66f9568e617d537d0d6bf72e..0000000000000000000000000000000000000000 --- a/application/admin/controller/example/Relationmodel.php +++ /dev/null @@ -1,52 +0,0 @@ -model = model('AdminLog'); - } - - /** - * 查看 - */ - public function index() - { - $this->relationSearch = true; - $this->searchFields = "admin.username,id"; - if ($this->request->isAjax()) - { - list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->with("admin") - ->where($where) - ->order($sort, $order) - ->count(); - $list = $this->model - ->with("admin") - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - -} diff --git a/application/admin/controller/example/Tabletemplate.php b/application/admin/controller/example/Tabletemplate.php deleted file mode 100644 index fcc5c35f75bbac7f5e14690975867e5ef66f3415..0000000000000000000000000000000000000000 --- a/application/admin/controller/example/Tabletemplate.php +++ /dev/null @@ -1,60 +0,0 @@ -model = model('AdminLog'); - } - - /** - * 查看 - */ - public function index() - { - if ($this->request->isAjax()) - { - list($where, $sort, $order, $offset, $limit) = $this->buildparams(NULL); - $total = $this->model - ->where($where) - ->order($sort, $order) - ->count(); - $list = $this->model - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * 详情 - */ - public function detail($ids) - { - $row = $this->model->get(['id' => $ids]); - if (!$row) - $this->error(__('No Results were found')); - $this->view->assign("row", $row->toArray()); - return $this->view->fetch(); - } - -} diff --git a/application/admin/controller/general/Attachment.php b/application/admin/controller/general/Attachment.php index cb549ffd8d3b83b81f7b6de23b061479f5ecd297..d73c25ceff590d390c6b725d1ce69f5db44a727d 100644 --- a/application/admin/controller/general/Attachment.php +++ b/application/admin/controller/general/Attachment.php @@ -1,79 +1,124 @@ -model = model('Attachment'); - } - - /** - * 查看 - */ - public function index() - { - if ($this->request->isAjax()) - { - list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->where($where) - ->order($sort, $order) - ->count(); - - $list = $this->model - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - $cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root()); - foreach ($list as $k => &$v) - { - $v['fullurl'] = ($v['storage'] == 'local' ? $cdnurl : $this->view->config['upload']['cdnurl']) . $v['url']; - } - unset($v); - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * 选择附件 - */ - public function select() - { - if ($this->request->isAjax()) - { - return $this->index(); - } - return $this->view->fetch(); - } - - /** - * 添加 - */ - public function add() - { - if ($this->request->isAjax()) - { - $this->code = -1; - } - return $this->view->fetch(); - } - -} +model = model('Attachment'); + $this->view->assign("mimetypeList", \app\common\model\Attachment::getMimetypeList()); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + $mimetypeQuery = []; + $filter = $this->request->request('filter'); + $filterArr = (array)json_decode($filter, true); + if (isset($filterArr['mimetype']) && preg_match("/[]\,|\*]/", $filterArr['mimetype'])) { + $this->request->get(['filter' => json_encode(array_diff_key($filterArr, ['mimetype' => '']))]); + $mimetypeQuery = function ($query) use ($filterArr) { + $mimetypeArr = explode(',', $filterArr['mimetype']); + foreach ($mimetypeArr as $index => $item) { + if (stripos($item, "/*") !== false) { + $query->whereOr('mimetype', 'like', str_replace("/*", "/", $item) . '%'); + } else { + $query->whereOr('mimetype', 'like', '%' . $item . '%'); + } + } + }; + } + + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->where($mimetypeQuery) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + $cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root()); + foreach ($list as $k => &$v) { + $v['fullurl'] = ($v['storage'] == 'local' ? $cdnurl : $this->view->config['upload']['cdnurl']) . $v['url']; + } + unset($v); + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 选择附件 + */ + public function select() + { + if ($this->request->isAjax()) { + return $this->index(); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isAjax()) { + $this->error(); + } + return $this->view->fetch(); + } + + /** + * 删除附件 + * @param array $ids + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + \think\Hook::add('upload_delete', function ($params) { + if ($params['storage'] == 'local') { + $attachmentFile = ROOT_PATH . '/public' . $params['url']; + if (is_file($attachmentFile)) { + @unlink($attachmentFile); + } + } + }); + $attachmentlist = $this->model->where('id', 'in', $ids)->select(); + foreach ($attachmentlist as $attachment) { + \think\Hook::listen("upload_delete", $attachment); + $attachment->delete(); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + +} diff --git a/application/admin/controller/general/Config.php b/application/admin/controller/general/Config.php index ef634058d62835fb2ce0430db6e3abd58f05ac74..f28040f6c8da9c419b6fa301d3298cf361eb0acd 100644 --- a/application/admin/controller/general/Config.php +++ b/application/admin/controller/general/Config.php @@ -1,263 +1,314 @@ -model = model('Config'); - } - - public function index() - { - $siteList = []; - $groupList = ConfigModel::getGroupList(); - foreach ($groupList as $k => $v) - { - $siteList[$k]['name'] = $k; - $siteList[$k]['title'] = $v; - $siteList[$k]['list'] = []; - } - - foreach ($this->model->all() as $k => $v) - { - if (!isset($siteList[$v['group']])) - { - continue; - } - $value = $v->toArray(); - if (in_array($value['type'], ['select', 'selects', 'checkbox', 'radio'])) - { - $value['value'] = explode(',', $value['value']); - } - if ($value['type'] == 'array') - { - $value['value'] = (array) json_decode($value['value'], TRUE); - } - $value['content'] = json_decode($value['content'], TRUE); - $siteList[$v['group']]['list'][] = $value; - } - $index = 0; - foreach ($siteList as $k => &$v) - { - $v['active'] = !$index ? true : false; - $index++; - } - $this->view->assign('siteList', $siteList); - $this->view->assign('typeList', ConfigModel::getTypeList()); - $this->view->assign('groupList', ConfigModel::getGroupList()); - return $this->view->fetch(); - } - - /** - * 添加 - */ - public function add() - { - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - if ($params) - { - foreach ($params as $k => &$v) - { - $v = is_array($v) ? implode(',', $v) : $v; - } - try - { - if ($params['content'] && in_array($params['type'], ['select', 'selects', 'checkbox', 'radio'])) - { - $content = explode("\r\n", $params['content']); - $arr = []; - foreach ($content as $k => &$v) - { - if (stripos($v, "|") !== false) - { - $item = explode('|', $v); - $arr[$item[0]] = $item[1]; - } - } - $params['content'] = $arr ? json_encode($arr, JSON_UNESCAPED_UNICODE) : ''; - } - else - { - $params['content'] = ''; - } - $result = $this->model->create($params); - if ($result !== false) - { - try - { - $this->refreshFile(); - $this->code = 1; - } - catch (Exception $e) - { - $this->msg = $e->getMessage(); - } - } - else - { - $this->msg = $this->model->getError(); - } - } - catch (Exception $e) - { - $this->msg = $e->getMessage(); - } - } - else - { - $this->msg = __('Parameter %s can not be empty', ''); - } - - return; - } - return $this->view->fetch(); - } - - public function edit($ids = NULL) - { - $this->code = -1; - if ($this->request->isPost()) - { - $params = $this->request->post("row/a"); - if ($params) - { - $configList = []; - foreach ($this->model->all() as $k => $v) - { - if (isset($params[$v['name']])) - { - if ($v['type'] == 'array') - { - $fieldarr = $valuearr = []; - $field = $params[$v['name']]['field']; - $value = $params[$v['name']]['value']; - - foreach ($field as $m => $n) - { - if ($n != '') - { - $fieldarr[] = $field[$m]; - $valuearr[] = $value[$m]; - } - } - $params[$v['name']] = array_combine($fieldarr, $valuearr); - $value = json_encode($params[$v['name']], JSON_UNESCAPED_UNICODE); - } - else - { - $value = is_array($params[$v['name']]) ? implode(',', $params[$v['name']]) : $params[$v['name']]; - } - - $configList[] = ['id' => $v['id'], 'value' => $value]; - } - } - $this->model->saveAll($configList); - try - { - $this->refreshFile(); - $this->code = 1; - } - catch (Exception $e) - { - $this->msg = $e->getMessage(); - } - } - else - { - $this->msg = __('Parameter %s can not be empty', ''); - } - - return; - } - } - - protected function refreshFile() - { - $config = []; - foreach ($this->model->all() as $k => $v) - { - - $value = $v->toArray(); - if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) - { - $value['value'] = explode(',', $value['value']); - } - if ($value['type'] == 'array') - { - $value['value'] = (array) json_decode($value['value'], TRUE); - } - $config[$value['name']] = $value['value']; - } - file_put_contents(APP_PATH . 'extra' . DS . 'site.php', 'request->post("row/a"); - if ($params) - { - - $config = $this->model->get($params); - if (!$config) - { - return json(['ok' => '']); - } - else - { - return json(['error' => __('Name already exist')]); - } - } - else - { - return json(['error' => __('Invalid parameters')]); - } - } - - /** - * 发送测试邮件 - * @internal - */ - public function emailtest() - { - $receiver = $this->request->request("receiver"); - $email = new Email; - $result = $email - ->to($receiver) - ->subject(__("This is a test mail")) - ->message('
      ' . __('This is a test mail content') . '
      ') - ->send(); - if ($result) - { - $this->code = 1; - } - else - { - $this->code = -1; - $this->msg = $email->getError(); - } - } - -} +model = model('Config'); + ConfigModel::event('before_write', function ($row) { + if (isset($row['name']) && $row['name'] == 'name' && preg_match("/fast" . "admin/i", $row['value'])) { + throw new Exception(__("Site name incorrect")); + } + }); + } + + /** + * 查看 + */ + public function index() + { + $siteList = []; + $groupList = ConfigModel::getGroupList(); + foreach ($groupList as $k => $v) { + $siteList[$k]['name'] = $k; + $siteList[$k]['title'] = $v; + $siteList[$k]['list'] = []; + } + + foreach ($this->model->all() as $k => $v) { + if (!isset($siteList[$v['group']])) { + continue; + } + $value = $v->toArray(); + $value['title'] = __($value['title']); + if (in_array($value['type'], ['select', 'selects', 'checkbox', 'radio'])) { + $value['value'] = explode(',', $value['value']); + } + $value['content'] = json_decode($value['content'], true); + $value['tip'] = htmlspecialchars($value['tip']); + $siteList[$v['group']]['list'][] = $value; + } + $index = 0; + foreach ($siteList as $k => &$v) { + $v['active'] = !$index ? true : false; + $index++; + } + $this->view->assign('siteList', $siteList); + $this->view->assign('typeList', ConfigModel::getTypeList()); + $this->view->assign('ruleList', ConfigModel::getRegexList()); + $this->view->assign('groupList', ConfigModel::getGroupList()); + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a", [], 'trim'); + if ($params) { + foreach ($params as $k => &$v) { + $v = is_array($v) && $k !== 'setting' ? implode(',', $v) : $v; + } + if (in_array($params['type'], ['select', 'selects', 'checkbox', 'radio', 'array'])) { + $params['content'] = json_encode(ConfigModel::decode($params['content']), JSON_UNESCAPED_UNICODE); + } else { + $params['content'] = ''; + } + try { + $result = $this->model->create($params); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + if ($result !== false) { + try { + $this->refreshFile(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + $this->error($this->model->getError()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + return $this->view->fetch(); + } + + /** + * 编辑 + * @param null $ids + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + $row = $this->request->post("row/a", [], 'trim'); + if ($row) { + $configList = []; + foreach ($this->model->all() as $v) { + if (isset($row[$v['name']])) { + $value = $row[$v['name']]; + if (is_array($value) && isset($value['field'])) { + $value = json_encode(ConfigModel::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $v['value'] = $value; + $configList[] = $v->toArray(); + } + } + try { + $this->model->allowField(true)->saveAll($configList); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + try { + $this->refreshFile(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + } + + /** + * 删除 + * @param string $ids + */ + public function del($ids = "") + { + $name = $this->request->post('name'); + $config = ConfigModel::getByName($name); + if ($name && $config) { + try { + $config->delete(); + $this->refreshFile(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + $this->error(__('Invalid parameters')); + } + } + + /** + * 刷新配置文件 + */ + protected function refreshFile() + { + $config = []; + foreach ($this->model->all() as $k => $v) { + $value = $v->toArray(); + if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) { + $value['value'] = explode(',', $value['value']); + } + if ($value['type'] == 'array') { + $value['value'] = (array)json_decode($value['value'], true); + } + $config[$value['name']] = $value['value']; + } + file_put_contents( + CONF_PATH . 'extra' . DS . 'site.php', + 'request->post("row/a"); + if ($params) { + $config = $this->model->get($params); + if (!$config) { + $this->success(); + } else { + $this->error(__('Name already exist')); + } + } else { + $this->error(__('Invalid parameters')); + } + } + + /** + * 规则列表 + * @internal + */ + public function rulelist() + { + //主键 + $primarykey = $this->request->request("keyField"); + //主键值 + $keyValue = $this->request->request("keyValue", ""); + + $keyValueArr = array_filter(explode(',', $keyValue)); + $regexList = \app\common\model\Config::getRegexList(); + $list = []; + foreach ($regexList as $k => $v) { + if ($keyValueArr) { + if (in_array($k, $keyValueArr)) { + $list[] = ['id' => $k, 'name' => $v]; + } + } else { + $list[] = ['id' => $k, 'name' => $v]; + } + } + return json(['list' => $list]); + } + + /** + * 发送测试邮件 + * @internal + */ + public function emailtest() + { + $row = $this->request->post('row/a'); + $receiver = $this->request->post("receiver"); + if ($receiver) { + if (!Validate::is($receiver, "email")) { + $this->error(__('Please input correct email')); + } + \think\Config::set('site', array_merge(\think\Config::get('site'), $row)); + $email = new Email; + $result = $email + ->to($receiver) + ->subject(__("This is a test mail")) + ->message('
      ' . __('This is a test mail content') . '
      ') + ->send(); + if ($result) { + $this->success(); + } else { + $this->error($email->getError()); + } + } else { + $this->error(__('Invalid parameters')); + } + } + + public function selectpage() + { + $id = $this->request->get("id/d"); + $config = \app\common\model\Config::get($id); + if (!$config) { + $this->error(__('Invalid parameters')); + } + $setting = $config['setting']; + //自定义条件 + $custom = isset($setting['conditions']) ? (array)json_decode($setting['conditions'], true) : []; + $custom = array_filter($custom); + + $this->request->request(['showField' => $setting['field'], 'keyField' => $setting['primarykey'], 'custom' => $custom, 'searchField' => [$setting['field'], $setting['primarykey']]]); + $this->model = \think\Db::connect()->setTable($setting['table']); + return parent::selectpage(); + } + + /** + * 获取表列表 + * @internal + */ + public function get_table_list() + { + $tableList = []; + $dbname = \think\Config::get('database.database'); + $tableList = \think\Db::query("SELECT `TABLE_NAME` AS `name`,`TABLE_COMMENT` AS `title` FROM `information_schema`.`TABLES` where `TABLE_SCHEMA` = '{$dbname}';"); + $this->success('', null, ['tableList' => $tableList]); + } + + /** + * 获取表字段列表 + * @internal + */ + public function get_fields_list() + { + $table = $this->request->request('table'); + $dbname = \think\Config::get('database.database'); + //从数据库中获取表字段信息 + $sql = "SELECT `COLUMN_NAME` AS `name`,`COLUMN_COMMENT` AS `title`,`DATA_TYPE` AS `type` FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $fieldList = Db::query($sql, [$dbname, $table]); + $this->success("", null, ['fieldList' => $fieldList]); + } +} diff --git a/application/admin/controller/general/Crontab.php b/application/admin/controller/general/Crontab.php deleted file mode 100644 index 73dfdd0820cb00053fcbdeab86c07d27fe1b1b98..0000000000000000000000000000000000000000 --- a/application/admin/controller/general/Crontab.php +++ /dev/null @@ -1,100 +0,0 @@ -model = model('Crontab'); - $this->view->assign('typedata', \app\common\model\Crontab::getTypeList()); - } - - /** - * 查看 - */ - public function index() - { - if ($this->request->isAjax()) - { - list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->where($where) - ->order($sort, $order) - ->count(); - - $list = $this->model - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - foreach ($list as $k => &$v) - { - $cron = CronExpression::factory($v['schedule']); - $v['nexttime'] = $cron->getNextRunDate()->getTimestamp(); - } - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * 判断Crontab格式是否正确 - * @internal - */ - public function check_schedule() - { - $row = $this->request->post("row/a"); - $schedule = isset($row['schedule']) ? $row['schedule'] : ''; - if (CronExpression::isValidExpression($schedule)) - { - return json(['ok' => '']); - } - else - { - return json(['error' => __('Crontab format invalid')]); - } - } - - /** - * 根据Crontab表达式读取未来七次的时间 - * @internal - */ - public function get_schedule_future() - { - $time = []; - $schedule = $this->request->post('schedule'); - $days = (int) $this->request->post('days'); - try - { - $cron = CronExpression::factory($schedule); - for ($i = 0; $i < $days; $i++) - { - $time[] = $cron->getNextRunDate(null, $i)->format('Y-m-d H:i:s'); - } - } - catch (\Exception $e) - { - - } - - return json(['futuretime' => $time]); - } - -} diff --git a/application/admin/controller/general/Database.php b/application/admin/controller/general/Database.php deleted file mode 100644 index 2d31a014b890ded5151398c51b1a25cb0497dc36..0000000000000000000000000000000000000000 --- a/application/admin/controller/general/Database.php +++ /dev/null @@ -1,211 +0,0 @@ - $row) - { - $tables[] = ['name' => reset($row), 'rows' => 0]; - } - $data['tables'] = $tables; - $data['saved_sql'] = []; - $this->view->assign($data); - return $this->view->fetch(); - } - - /** - * SQL查询 - */ - public function query() - { - $do_action = $this->request->post('do_action'); - - echo ''; - if ($do_action == '') - exit(__('Invalid parameters')); - - $tablename = $this->request->post("tablename/a"); - - if (in_array($do_action, array('doquery', 'optimizeall', 'repairall'))) - { - $this->$do_action(); - } - else if (count($tablename) == 0) - { - exit(__('Invalid parameters')); - } - else - { - foreach ($tablename as $table) - { - $this->$do_action($table); - echo "
      "; - } - } - } - - private function viewinfo($name) - { - $row = Db::query("SHOW CREATE TABLE `{$name}`"); - $row = array_values($row[0]); - $info = $row[1]; - echo "{$info};"; - } - - private function viewdata($name = '') - { - $sqlquery = "SELECT * FROM `{$name}`"; - $this->doquery($sqlquery); - } - - private function optimize($name = '') - { - if (Db::execute("OPTIMIZE TABLE `{$name}`")) - { - echo __('Optimize table %s done', $name); - } - else - { - echo __('Optimize table %s fail', $name); - } - } - - private function optimizeall($name = '') - { - $list = Db::query("SHOW TABLES"); - foreach ($list as $key => $row) - { - $name = reset($row); - if (Db::execute("OPTIMIZE TABLE {$name}")) - { - echo __('Optimize table %s done', $name); - } - else - { - echo __('Optimize table %s fail', $name); - } - echo "
      "; - } - } - - private function repair($name = '') - { - if (Db::execute("REPAIR TABLE `{$name}`")) - { - echo __('Repair table %s done', $name); - } - else - { - echo __('Repair table %s fail', $name); - } - } - - private function repairall($name = '') - { - $list = Db::query("SHOW TABLES"); - foreach ($list as $key => $row) - { - $name = reset($row); - if (Db::execute("REPAIR TABLE {$name}")) - { - echo __('Repair table %s done', $name); - } - else - { - echo __('Repair table %s fail', $name); - } - echo "
      "; - } - } - - private function doquery($sql = null) - { - $sqlquery = $sql ? $sql : $this->request->post('sqlquery'); - if ($sqlquery == '') - exit(__('SQL can not be empty')); - $sqlquery = str_replace("\r", "", $sqlquery); - $sqls = preg_split("/;[ \t]{0,}\n/i", $sqlquery); - $maxreturn = 100; - $r = ''; - foreach ($sqls as $key => $val) - { - if (trim($val) == '') - continue; - $val = rtrim($val, ';'); - $r .= "SQL:{$val} "; - if (preg_match("/^(select|explain)(.*)/i ", $val)) - { - Debug::remark("begin"); - $limit = stripos(strtolower($val), "limit") !== false ? true : false; - $count = Db::execute($val); - if ($count > 0) - { - $resultlist = Db::query($val . (!$limit && $count > $maxreturn ? ' LIMIT ' . $maxreturn : '')); - } - else - { - $resultlist = []; - } - Debug::remark("end"); - $time = Debug::getRangeTime('begin', 'end', 4); - - $usedseconds = __('Query took %s seconds', $time) . "
      "; - if ($count <= 0) - { - $r .= __('Query returned an empty result'); - } - else - { - $r .= (__('Total:%s', $count) . (!$limit && $count > $maxreturn ? ',' . __('Max output:%s', $maxreturn) : "")); - } - $r = $r . ',' . $usedseconds; - $j = 0; - foreach ($resultlist as $m => $n) - { - $j++; - if (!$limit && $j > $maxreturn) - break; - $r .= "
      "; - $r .= "" . __('Row:%s', $j) . "
      "; - foreach ($n as $k => $v) - { - $r .= "{$k}:{$v}
      \r\n"; - } - } - } - else - { - Debug::remark("begin"); - $count = Db::execute($val); - Debug::remark("end"); - $time = Debug::getRangeTime('begin', 'end', 4); - $r .= __('Query affected %s rows and took %s seconds', $count, $time) . "
      "; - } - } - echo $r; - } - -} diff --git a/application/admin/controller/general/Profile.php b/application/admin/controller/general/Profile.php index 39344b19de407543d16b2835d93ca2e5a992cc9a..10fc51f6d3e37265dcc5ba18a4f02c7376cd586e 100644 --- a/application/admin/controller/general/Profile.php +++ b/application/admin/controller/general/Profile.php @@ -1,80 +1,81 @@ -request->isAjax()) - { - $model = model('AdminLog'); - list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - - $total = $model - ->where($where) - ->where('admin_id', $this->auth->id) - ->order($sort, $order) - ->count(); - - $list = $model - ->where($where) - ->where('admin_id', $this->auth->id) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * 更新个人信息 - */ - public function update() - { - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - $params = array_filter(array_intersect_key($params, array_flip(array('email', 'nickname', 'password', 'avatar')))); - unset($v); - if (isset($params['password'])) - { - $params['salt'] = Random::alnum(); - $params['password'] = md5(md5($params['password']) . $params['salt']); - } - if ($params) - { - model('admin')->where('id', $this->auth->id)->update($params); - //因为个人资料面板读取的Session显示,修改自己资料后同时更新Session - $admin = Session::get('admin'); - $admin_id = $admin ? $admin->id : 0; - if($this->auth->id==$admin_id){ - $admin = model('admin')->get(['id' => $admin_id]); - Session::set("admin", $admin); - } - $this->code = 1; - } - } - return; - } - -} +request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + $this->model = model('AdminLog'); + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->where($where) + ->where('admin_id', $this->auth->id) + ->order($sort, $order) + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 更新个人信息 + */ + public function update() + { + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a"); + $params = array_filter(array_intersect_key( + $params, + array_flip(array('email', 'nickname', 'password', 'avatar')) + )); + unset($v); + if (!Validate::is($params['email'], "email")) { + $this->error(__("Please input correct email")); + } + if (isset($params['password'])) { + if (!Validate::is($params['password'], "/^[\S]{6,16}$/")) { + $this->error(__("Please input correct password")); + } + $params['salt'] = Random::alnum(); + $params['password'] = md5(md5($params['password']) . $params['salt']); + } + $exist = Admin::where('email', $params['email'])->where('id', '<>', $this->auth->id)->find(); + if ($exist) { + $this->error(__("Email already exists")); + } + if ($params) { + $admin = Admin::get($this->auth->id); + $admin->save($params); + //因为个人资料面板读取的Session显示,修改自己资料后同时更新Session + Session::set("admin", $admin->toArray()); + $this->success(); + } + $this->error(); + } + return; + } +} diff --git a/application/admin/controller/user/Group.php b/application/admin/controller/user/Group.php new file mode 100644 index 0000000000000000000000000000000000000000..b4c38c1b27aad1484d62aa63627d6d207531b7ba --- /dev/null +++ b/application/admin/controller/user/Group.php @@ -0,0 +1,52 @@ +model = model('UserGroup'); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + $nodeList = \app\admin\model\UserRule::getTreeList(); + $this->assign("nodeList", $nodeList); + return parent::add(); + } + + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $rules = explode(',', $row['rules']); + $nodeList = \app\admin\model\UserRule::getTreeList($rules); + $this->assign("nodeList", $nodeList); + return parent::edit($ids); + } + +} diff --git a/application/admin/controller/user/Rule.php b/application/admin/controller/user/Rule.php new file mode 100644 index 0000000000000000000000000000000000000000..611d68718d33aa33b033f406497a58f1f4248b31 --- /dev/null +++ b/application/admin/controller/user/Rule.php @@ -0,0 +1,108 @@ +model = model('UserRule'); + $this->view->assign("statusList", $this->model->getStatusList()); + // 必须将结果集转换为数组 + $ruleList = collection($this->model->order('weigh', 'desc')->select())->toArray(); + foreach ($ruleList as $k => &$v) { + $v['title'] = __($v['title']); + $v['remark'] = __($v['remark']); + } + unset($v); + Tree::instance()->init($ruleList); + $this->rulelist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'title'); + $ruledata = [0 => __('None')]; + foreach ($this->rulelist as $k => &$v) { + if (!$v['ismenu']) { + continue; + } + $ruledata[$v['id']] = $v['title']; + } + $this->view->assign('ruledata', $ruledata); + } + + /** + * 查看 + */ + public function index() + { + if ($this->request->isAjax()) { + $list = $this->rulelist; + $total = count($this->rulelist); + + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::edit($ids); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $delIds = []; + foreach (explode(',', $ids) as $k => $v) { + $delIds = array_merge($delIds, Tree::instance()->getChildrenIds($v, true)); + } + $delIds = array_unique($delIds); + $count = $this->model->where('id', 'in', $delIds)->delete(); + if ($count) { + $this->success(); + } + } + $this->error(); + } + +} diff --git a/application/admin/controller/user/User.php b/application/admin/controller/user/User.php new file mode 100644 index 0000000000000000000000000000000000000000..ba373e5678141e3f0c5e988339ad85f2b6541864 --- /dev/null +++ b/application/admin/controller/user/User.php @@ -0,0 +1,105 @@ +model = model('User'); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $list = $this->model + ->with('group') + ->where($where) + ->order($sort, $order) + ->paginate($limit); + foreach ($list as $k => $v) { + $v->avatar = $v->avatar ? cdnurl($v->avatar, true) : letter_avatar($v->nickname); + $v->hidden(['password', 'salt']); + } + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + $row = $this->model->get($ids); + $this->modelValidate = true; + if (!$row) { + $this->error(__('No Results were found')); + } + $this->view->assign('groupList', build_select('row[group_id]', \app\admin\model\UserGroup::column('id,name'), $row['group_id'], ['class' => 'form-control selectpicker'])); + return parent::edit($ids); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + $row = $this->model->get($ids); + $this->modelValidate = true; + if (!$row) { + $this->error(__('No Results were found')); + } + Auth::instance()->delete($row['id']); + $this->success(); + } + +} diff --git a/application/admin/controller/wechat/Autoreply.php b/application/admin/controller/wechat/Autoreply.php deleted file mode 100644 index 0de21275577d086ca3d43e7ffea321d8011733c5..0000000000000000000000000000000000000000 --- a/application/admin/controller/wechat/Autoreply.php +++ /dev/null @@ -1,74 +0,0 @@ -model = model('WechatAutoreply'); - } - - /** - * 编辑 - */ - public function edit($ids = NULL) - { - $row = $this->model->get(['id' => $ids]); - if (!$row) - $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - if ($params) - { - $row->save($params); - $this->code = 1; - } - return; - } - $response = WechatResponse::get(['eventkey' => $row['eventkey']]); - $this->view->assign("response", $response); - $this->view->assign("row", $row); - return $this->view->fetch(); - } - - /** - * 判断文本是否唯一 - * @internal - */ - public function check_text_unique() - { - $row = $this->request->post("row/a"); - $except = $this->request->post("except"); - $text = isset($row['text']) ? $row['text'] : ''; - if ($this->model->where('text', $text)->where(function($query) use($except) { - if ($except) - { - $query->where('text', '<>', $except); - } - })->count() == 0) - { - return json(['ok' => '']); - } - else - { - return json(['error' => __('Text already exists')]); - } - } - -} diff --git a/application/admin/controller/wechat/Config.php b/application/admin/controller/wechat/Config.php deleted file mode 100644 index bf9af55daa622840dcda421bd98d4da27302b73b..0000000000000000000000000000000000000000 --- a/application/admin/controller/wechat/Config.php +++ /dev/null @@ -1,164 +0,0 @@ -model = model('WechatConfig'); - } - - /** - * 添加 - */ - public function add() - { - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - if ($params) - { - foreach ($params as $k => &$v) - { - $v = is_array($v) ? implode(',', $v) : $v; - } - - if ($params['mode'] == 'json') - { - //JSON字段 - $fieldarr = $valuearr = []; - $field = $this->request->post('field/a'); - $value = $this->request->post('value/a'); - foreach ($field as $k => $v) - { - if ($v != '') - { - $fieldarr[] = $field[$k]; - $valuearr[] = $value[$k]; - } - } - $params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE); - } - unset($params['mode']); - try - { - //是否采用模型验证 - if ($this->modelValidate) - { - $name = basename(str_replace('\\', '/', get_class($this->model))); - $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate; - $this->model->validate($validate); - } - $result = $this->model->save($params); - if ($result !== false) - { - $this->code = 1; - } - else - { - $this->msg = $this->model->getError(); - } - } - catch (\think\exception\PDOException $e) - { - $this->msg = $e->getMessage(); - } - } - else - { - $this->msg = __('Parameter %s can not be empty', ''); - } - - return; - } - return $this->view->fetch(); - } - - /** - * 编辑 - */ - public function edit($ids = NULL) - { - $row = $this->model->get($ids); - if (!$row) - $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - if ($params) - { - foreach ($params as $k => &$v) - { - $v = is_array($v) ? implode(',', $v) : $v; - } - - if ($params['mode'] == 'json') - { - //JSON字段 - $fieldarr = $valuearr = []; - $field = $this->request->post('field/a'); - $value = $this->request->post('value/a'); - foreach ($field as $k => $v) - { - if ($v != '') - { - $fieldarr[] = $field[$k]; - $valuearr[] = $value[$k]; - } - } - $params['value'] = json_encode(array_combine($fieldarr, $valuearr), JSON_UNESCAPED_UNICODE); - } - unset($params['mode']); - try - { - //是否采用模型验证 - if ($this->modelValidate) - { - $name = basename(str_replace('\\', '/', get_class($this->model))); - $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate; - $row->validate($validate); - } - $result = $row->save($params); - if ($result !== false) - { - $this->code = 1; - } - else - { - $this->msg = $row->getError(); - } - } - catch (think\exception\PDOException $e) - { - $this->msg = $e->getMessage(); - } - } - else - { - $this->msg = __('Parameter %s can not be empty', ''); - } - - return; - } - $this->view->assign("row", $row); - $this->view->assign("value", (array) json_decode($row->value, true)); - return $this->view->fetch(); - } - -} diff --git a/application/admin/controller/wechat/Menu.php b/application/admin/controller/wechat/Menu.php deleted file mode 100644 index 1a1b7132701fd5cbf84d52c778e0a9d99065b024..0000000000000000000000000000000000000000 --- a/application/admin/controller/wechat/Menu.php +++ /dev/null @@ -1,111 +0,0 @@ -wechatcfg = \app\common\model\WechatConfig::get(['name' => 'menu']); - } - - /** - * 查看 - */ - public function index() - { - $responselist = array(); - $all = WechatResponse::all(); - foreach ($all as $k => $v) - { - $responselist[$v['eventkey']] = $v['title']; - } - $this->view->assign('responselist', $responselist); - $this->view->assign('menu', (array) json_decode($this->wechatcfg->value, TRUE)); - return $this->view->fetch(); - } - - /** - * 修改 - */ - public function edit($ids = NULL) - { - $menu = $this->request->post("menu"); - $menu = (array) json_decode($menu, TRUE); - $this->wechatcfg->value = json_encode($menu, JSON_UNESCAPED_UNICODE); - $this->wechatcfg->save(); - $this->code = 1; - return; - } - - /** - * 同步 - */ - public function sync($ids = NULL) - { - $this->code = -1; - $app = new Application(Config::get('wechat')); - - try - { - $hasError = false; - $menu = json_decode($this->wechatcfg->value, TRUE); - foreach ($menu as $k => $v) - { - if (isset($v['sub_button'])) - { - foreach ($v['sub_button'] as $m => $n) - { - if (isset($n['key']) && !$n['key']) - { - $hasError = true; - break 2; - } - } - } - else if (isset($v['key']) && !$v['key']) - { - $hasError = true; - break; - } - } - if (!$hasError) - { - $ret = $app->menu->add($menu); - if ($ret->errcode == 0) - { - $this->code = 1; - } - else - { - $this->msg = $ret->errmsg; - } - } - else - { - $this->msg = __('Invalid parameters'); - } - } - catch (Exception $e) - { - $this->msg = $e->getMessage(); - } - return; - } - -} diff --git a/application/admin/controller/wechat/Response.php b/application/admin/controller/wechat/Response.php deleted file mode 100644 index d1bbb1a7dc8bfd66248e00aae32de4c23f9aca23..0000000000000000000000000000000000000000 --- a/application/admin/controller/wechat/Response.php +++ /dev/null @@ -1,86 +0,0 @@ -model = model('WechatResponse'); - } - - /** - * 选择素材 - */ - public function select() - { - return $this->view->fetch(); - } - - /** - * 添加 - */ - public function add() - { - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid(); - $params['content'] = json_encode($params['content']); - $params['createtime'] = time(); - if ($params) - { - $this->model->save($params); - $this->code = 1; - $this->content = $params; - } - - return; - } - $appConfig = Wechat::appConfig(); - $this->view->applist = $appConfig; - return $this->view->fetch(); - } - - /** - * 编辑 - */ - public function edit($ids = NULL) - { - $row = $this->model->get($ids); - if (!$row) - $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $this->code = -1; - $params = $this->request->post("row/a"); - $params['eventkey'] = isset($params['eventkey']) && $params['eventkey'] ? $params['eventkey'] : uniqid(); - $params['content'] = json_encode($params['content']); - if ($params) - { - $row->save($params); - $this->code = 1; - } - return; - } - $this->view->assign("row", $row); - $appConfig = Wechat::appConfig(); - $this->view->applist = $appConfig; - return $this->view->fetch(); - } - -} diff --git a/application/admin/lang/zh-cn.php b/application/admin/lang/zh-cn.php old mode 100644 new mode 100755 index d9ad4c23ef64c11a4f906b256816a3eb00f41350..e0a44d4e081b8087730cae491d10c7cd28630d2b --- a/application/admin/lang/zh-cn.php +++ b/application/admin/lang/zh-cn.php @@ -7,14 +7,21 @@ return [ 'Password' => '密码', 'Sign up' => '注 册', 'Sign in' => '登 录', - 'Sign out' => '注 销', + 'Sign out' => '退 出', 'Keep login' => '保持会话', 'Guest' => '游客', 'Welcome' => '%s,你好!', + 'View' => '查看', 'Add' => '添加', 'Edit' => '编辑', + 'Del' => '删除', 'Delete' => '删除', + 'Import' => '导入', + 'Export' => '导出', + 'All' => '全部', 'Detail' => '详情', + 'Multi' => '批量更新', + 'Setting' => '配置', 'Move' => '移动', 'Name' => '名称', 'Status' => '状态', @@ -25,8 +32,19 @@ return [ 'Article' => '文章', 'Page' => '单页', 'OK' => '确定', + 'Apply' => '应用', 'Cancel' => '取消', + 'Clear' => '清空', + 'Custom Range' => '自定义', + 'Today' => '今天', + 'Yesterday' => '昨天', + 'Last 7 days' => '最近7天', + 'Last 30 days' => '最近30天', + 'Last month' => '上月', + 'This month' => '本月', 'Loading' => '加载中', + 'Money' => '余额', + 'Score' => '积分', 'More' => '更多', 'Yes' => '是', 'No' => '否', @@ -38,8 +56,11 @@ return [ 'Execute' => '执行', 'Close' => '关闭', 'Choose' => '选择', + 'Go' => '跳转', 'Search' => '搜索', 'Refresh' => '刷新', + 'Install' => '安装', + 'Uninstall' => '卸载', 'First' => '首页', 'Previous' => '上一页', 'Next' => '下一页', @@ -48,25 +69,21 @@ return [ 'Home' => '主页', 'Online' => '在线', 'Login' => '登录', - 'Logout' => '注销', + 'Logout' => '退出', 'Profile' => '个人资料', 'Index' => '首页', 'Hot' => '热门', 'Recommend' => '推荐', - 'Dashboard' => '控制台', 'Upload' => '上传', + 'Uploading' => '上传中', 'Code' => '编号', 'Message' => '内容', 'Line' => '行号', 'File' => '文件', 'Menu' => '菜单', - 'Name' => '名称', - 'Weigh' => '权重', 'Type' => '类型', 'Title' => '标题', 'Content' => '内容', - 'Status' => '状态', - 'Operate' => '操作', 'Append' => '追加', 'Select' => '选择', 'Memo' => '备注', @@ -79,18 +96,17 @@ return [ 'End time' => '结束时间', 'Create time' => '创建时间', 'Update time' => '更新时间', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Deletetime' => '删除时间', 'Flag' => '标志', + 'Drag to sort' => '拖动进行排序', 'Redirect now' => '立即跳转', - 'Operation completed' => '操作成功!', - 'Operation failed' => '操作失败!', - 'Unknown data format' => '未知的数据格式!', - 'Network error' => '网络错误!', - 'Auth manager' => '权限管理', - 'General manager' => '常规管理', - 'Example manager' => '示例管理', - 'Wechat manager' => '微信管理', + 'Key' => '键', + 'Value' => '值', 'Common search' => '普通搜索', 'Search %s' => '搜索 %s', + 'View %s' => '查看 %s', '%d second%s ago' => '%d秒前', '%d minute%s ago' => '%d分钟前', '%d hour%s ago' => '%d小时前', @@ -98,19 +114,99 @@ return [ '%d week%s ago' => '%d周前', '%d month%s ago' => '%d月前', '%d year%s ago' => '%d年前', - // 'Set to normal' => '设为正常', 'Set to hidden' => '设为隐藏', + 'Recycle bin' => '回收站', + 'Restore' => '还原', + 'Restore all' => '还原全部', + 'Destroy' => '销毁', + 'Destroy all' => '清空回收站', + 'Nothing need restore' => '没有需要还原的数据', + //提示 + 'Go back' => '返回首页', + 'Jump now' => '立即跳转', + 'Click to search %s' => '点击搜索 %s', + 'Click to toggle' => '点击切换', + 'Operation completed' => '操作成功!', + 'Operation failed' => '操作失败!', + 'Unknown data format' => '未知的数据格式!', + 'Network error' => '网络错误!', 'Invalid parameters' => '未知参数', 'No results were found' => '记录未找到', + 'No rows were inserted' => '未插入任何行', + 'No rows were deleted' => '未删除任何行', + 'No rows were updated' => '未更新任何行', 'Parameter %s can not be empty' => '参数%s不能为空', 'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?', 'Are you sure you want to delete this item?' => '确定删除此项?', 'Are you sure you want to delete or turncate?' => '确定删除或清空?', + 'Are you sure you want to truncate?' => '确定清空?', + 'Token verification error' => 'Token验证错误!', 'You have no permission' => '你没有权限访问', 'Please enter your username' => '请输入你的用户名', 'Please enter your password' => '请输入你的密码', 'Please login first' => '请登录后操作', + 'Uploaded successful' => '上传成功', + 'You can upload up to %d file%s' => '你最多还可以上传%d个文件', + 'You can choose up to %d file%s' => '你最多还可以选择%d个文件', + 'Chunk file write error' => '分片写入失败', + 'Chunk file info error' => '分片文件错误', + 'Chunk file merge error' => '分片合并错误', + 'Chunk file disabled' => '未开启分片上传功能', + 'Cancel upload' => '取消上传', + 'Upload canceled' => '上传已取消', + 'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制', + 'Uploaded file format is limited' => '上传文件格式受限制', + 'Uploaded file is not a valid image' => '上传文件不是有效的图片文件', + 'Are you sure you want to cancel this upload?' => '确定取消上传?', + 'Remove file' => '移除文件', + 'You can only upload a maximum of %s files' => '你最多允许上传 %s 个文件', + 'You can\'t upload files of this type' => '不允许上传的文件类型', + 'Server responded with %s code' => '服务端响应(Code:%s)', + 'File is too big (%sMiB), Max filesize: %sMiB' => '当前上传(%sM),最大允许上传文件大小:%sM', 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', + 'Click to uncheck all' => '点击取消全部', + 'Multiple selection mode: %s checked' => '跨页选择模式,已选 %s 项', + //菜单 + 'Dashboard' => '控制台', + 'General' => '常规管理', + 'Category' => '分类管理', + 'Addon' => '插件管理', + 'Auth' => '权限管理', + 'Config' => '系统配置', + 'Attachment' => '附件管理', + 'Admin' => '管理员管理', + 'Admin log' => '管理员日志', + 'Group' => '角色组', + 'Rule' => '菜单规则', + 'User' => '会员管理', + 'User group' => '会员分组', + 'User rule' => '会员规则', + 'Select attachment' => '选择附件', + 'Update profile' => '更新个人信息', + 'Local install' => '本地安装', + 'Update state' => '禁用启用', + 'Admin group' => '超级管理组', + 'Second group' => '二级管理组', + 'Third group' => '三级管理组', + 'Second group 2' => '二级管理组2', + 'Third group 2' => '三级管理组2', + 'Dashboard tips' => '用于展示当前系统中的统计数据、统计报表及重要实时数据', + 'Config tips' => '可以在此增改系统的变量和分组,也可以自定义分组和变量', + 'Category tips' => '用于统一管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加', + 'Attachment tips' => '主要用于管理上传到服务器或第三方存储的数据', + 'Addon tips' => '可在线安装、卸载、禁用、启用插件,同时支持添加本地插件。', + 'Admin tips' => '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成', + 'Admin log tips' => '管理员可以查看自己所拥有的权限的管理员日志', + 'Group tips' => '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员', + 'Rule tips' => '规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过命令行进行生成规则节点', + 'Access is allowed only to the super management group' => '仅超级管理组能访问', + 'Local addon' => '本地插件', + // 前台菜单 + 'Frontend' => '前台', + 'API Interface' => 'API接口', + 'User Module' => '会员模块', + 'Register' => '注册', + 'User Center' => '会员中心', ]; diff --git a/application/admin/lang/zh-cn/addon.php b/application/admin/lang/zh-cn/addon.php new file mode 100755 index 0000000000000000000000000000000000000000..afdbec7600ca55c5bd582c89d843b9c093eba596 --- /dev/null +++ b/application/admin/lang/zh-cn/addon.php @@ -0,0 +1,106 @@ + 'ID', + 'Title' => '名称', + 'Value' => '配置值', + 'Array key' => '键', + 'Array value' => '值', + 'File' => '文件', + 'Donate' => '打赏作者', + 'Warmtips' => '温馨提示', + 'Pay now' => '立即支付', + 'Offline install' => '离线安装', + 'Refresh addon cache' => '刷新插件缓存', + 'Userinfo' => '会员信息', + 'Online store' => '在线商店', + 'Local addon' => '本地插件', + 'Conflict tips' => '此插件中发现和现有系统中部分文件发现冲突!以下文件将会被影响,请备份好相关文件后再继续操作', + 'Login tips' => '此处登录账号为FastAdmin官网账号', + 'Logined tips' => '你好!%s
      当前你已经登录,将同步保存你的购买记录', + 'Pay tips' => '扫码支付后如果仍然无法立即下载,请不要重复支付,请稍后再重试安装!', + 'Pay click tips' => '请点击这里在新窗口中进行支付!', + 'Pay new window tips' => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!', + 'Upgrade tips' => '确认升级《%s》

      1、请务必做好代码和数据库备份!备份!备份!
      2、升级后如出现冗余数据,请根据需要移除即可!
      3、不建议在生产环境升级,请在本地完成升级测试

      如有重要数据请备份后再操作!', + 'Offline installed tips' => '安装成功!清除浏览器缓存和框架缓存后生效!', + 'Online installed tips' => '安装成功!清除浏览器缓存和框架缓存后生效!', + 'Not login tips' => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!', + 'Please login and try to install' => '请登录后再进行离线安装!', + 'Not installed tips' => '请安装后再访问插件前台页面!', + 'Not enabled tips' => '插件已经禁用,请启用后再访问插件前台页面!', + 'New version tips' => '发现新版本:%s 点击查看更新日志', + 'Store now available tips' => '插件市场暂不可用,是否切换到本地插件?', + 'Switch to the local' => '切换到本地插件', + 'try to reload' => '重新尝试加载', + 'Please disable the add before trying to upgrade' => '请先禁用插件再进行升级', + 'Please disable the add before trying to uninstall' => '请先禁用插件再进行卸载', + 'Login now' => '立即登录', + 'Continue install' => '继续安装', + 'View addon home page' => '查看插件介绍和帮助', + 'View addon index page' => '查看插件前台首页', + 'View addon screenshots' => '点击查看插件截图', + 'Click to toggle status' => '点击切换插件状态', + 'Click to contact developer' => '点击与插件开发者取得联系', + 'My addons' => '我购买的插件', + 'Index' => '前台', + 'All' => '全部', + 'Uncategoried' => '未归类', + 'Recommend' => '推荐', + 'Hot' => '热门', + 'New' => '新', + 'Paying' => '付费', + 'Free' => '免费', + 'Sale' => '折扣', + 'No image' => '暂无缩略图', + 'Price' => '价格', + 'Downloads' => '下载', + 'Author' => '作者', + 'Identify' => '标识', + 'Homepage' => '主页', + 'Intro' => '介绍', + 'Version' => '版本', + 'New version' => '新版本', + 'Createtime' => '添加时间', + 'Releasetime' => '更新时间', + 'Detail' => '插件详情', + 'Document' => '文档', + 'Demo' => '演示', + 'Feedback' => '反馈BUG', + 'Install' => '安装', + 'Uninstall' => '卸载', + 'Upgrade' => '升级', + 'Setting' => '配置', + 'Disable' => '禁用', + 'Enable' => '启用', + 'Your username or email' => '你的手机号、用户名或邮箱', + 'Your password' => '你的密码', + 'Login FastAdmin' => '登录', + 'Login' => '登录', + 'Logout' => '退出登录', + 'Register' => '注册账号', + 'You\'re not login' => '当前未登录', + 'Continue uninstall' => '继续卸载', + 'Continue operate' => '继续操作', + 'Install successful' => '安装成功', + 'Uninstall successful' => '卸载成功', + 'Operate successful' => '操作成功', + 'Addon name incorrect' => '插件名称不正确', + 'Addon info file was not found' => '插件配置文件未找到', + 'Addon info file data incorrect' => '插件配置信息不正确', + 'Addon already exists' => '插件已经存在', + 'Addon package download failed' => '插件下载失败', + 'Conflicting file found' => '发现冲突文件', + 'Invalid addon package' => '未验证的插件', + 'No permission to write temporary files' => '没有权限写入临时文件', + 'The addon file does not exist' => '插件主启动程序不存在', + 'The configuration file content is incorrect' => '配置文件不完整', + 'Unable to open the zip file' => '无法打开ZIP文件', + 'Unable to extract the file' => '无法解压ZIP文件', + 'Unable to open file \'%s\' for writing' => '文件(%s)没有写入权限', + 'Are you sure you want to unstall %s?' => '确认卸载《%s》?', + 'Delete all the addon file and cannot be recovered!' => '卸载将会删除所有插件文件且不可找回!!!', + 'Delete all the addon database and cannot be recovered!' => '删除所有插件相关数据表且不可找回!!!', + 'Please backup important data manually before uninstall!' => '如有重要数据请备份后再操作!!!', + 'The following data tables will be deleted' => '以下插件数据表将会被删除', + 'The Addon did not create a data table' => '插件未创建任何数据表', +]; diff --git a/application/admin/lang/zh-cn/ajax.php b/application/admin/lang/zh-cn/ajax.php new file mode 100644 index 0000000000000000000000000000000000000000..0b67a5fe4742a02757d939368cc05d4c246502f8 --- /dev/null +++ b/application/admin/lang/zh-cn/ajax.php @@ -0,0 +1,3 @@ + '所属组别', - 'Login time' => '最后登录', + 'Group' => '所属组别', + 'Loginfailure' => '登录失败次数', + 'Login time' => '最后登录', + 'Please input correct username' => '用户名只能由3-12位数字、字母、下划线组合', + 'Please input correct password' => '密码长度必须在6-16位之间,不能包含空格', ]; diff --git a/application/admin/lang/zh-cn/auth/group.php b/application/admin/lang/zh-cn/auth/group.php index 4c25c859f08fd36a0fac75db126102f9c371b86d..3a63f5860e905d01015bd40e4f955146292d034e 100644 --- a/application/admin/lang/zh-cn/auth/group.php +++ b/application/admin/lang/zh-cn/auth/group.php @@ -7,4 +7,6 @@ return [ 'Can not change the parent to child' => '父组别不能是它的子组别', 'Can not change the parent to self' => '父组别不能是它的子组别', 'You can not delete group that contain child group and administrators' => '你不能删除含有子组和管理员的组', + 'The parent group exceeds permission limit' => '父组别超出权限范围', + 'The parent group can not be its own child or itself' => '父组别不能是它的子组别及本身', ]; diff --git a/application/admin/lang/zh-cn/auth/rule.php b/application/admin/lang/zh-cn/auth/rule.php index b1b7694cade87229bf480e8275355dad9cc96b0b..1c533fd54cb3af44b396f313772612f7473d5d82 100644 --- a/application/admin/lang/zh-cn/auth/rule.php +++ b/application/admin/lang/zh-cn/auth/rule.php @@ -1,13 +1,20 @@ '显示全部', - 'Condition' => '规则条件', - 'Remark' => '备注', - 'Icon' => '图标', - 'Alert' => '警告', - 'Name' => '规则URL', - 'Ismenu' => '菜单', - 'The non-menu rule must have parent' => '非菜单规则节点必须有父级', - 'If not necessary, use the command line to build rule' => '非必要情况下请直接使用命令行php think menu来生成', + 'Toggle all' => '显示全部', + 'Condition' => '规则条件', + 'Remark' => '备注', + 'Icon' => '图标', + 'Alert' => '警告', + 'Name' => '规则', + 'Controller/Action' => '控制器名/方法名', + 'Ismenu' => '菜单', + 'Search icon' => '搜索图标', + 'Toggle menu visible' => '点击切换菜单显示', + 'Toggle sub menu' => '点击切换子菜单', + 'Menu tips' => '父级菜单无需匹配控制器和方法,子级菜单请使用控制器名', + 'Node tips' => '控制器/方法名,如果有目录请使用 目录名/控制器名/方法名', + 'The non-menu rule must have parent' => '非菜单规则节点必须有父级', + 'Can not change the parent to child' => '父组别不能是它的子组别', + 'Name only supports letters, numbers, underscore and slash' => 'URL规则只能是小写字母、数字、下划线和/组成', ]; diff --git a/application/admin/lang/zh-cn/category.php b/application/admin/lang/zh-cn/category.php index e76074803fb55f6eea774cbc002b6d19c7bec55d..20922745952ecc454aa7d597385abe489ade8bf5 100644 --- a/application/admin/lang/zh-cn/category.php +++ b/application/admin/lang/zh-cn/category.php @@ -1,15 +1,18 @@ 'ID', - 'Pid' => '父ID', - 'Type' => '栏目类型', - 'Image' => '图片', - 'Keywords' => '关键字', - 'Description' => '描述', - 'Diyname' => '自定义名称', - 'Createtime' => '创建时间', - 'Updatetime' => '更新时间', - 'Weigh' => '权重', - 'Status' => '状态' + 'Id' => 'ID', + 'Pid' => '父ID', + 'Type' => '类型', + 'All' => '全部', + 'Image' => '图片', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Diyname' => '自定义名称', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Weigh' => '权重', + 'Category warmtips' => '温馨提示:栏目类型请前往常规管理->系统配置->字典配置中进行管理', + 'Can not change the parent to child or itself' => '父组别不能是它的子组别或它自己', + 'Status' => '状态' ]; diff --git a/application/admin/lang/zh-cn/dashboard.php b/application/admin/lang/zh-cn/dashboard.php new file mode 100644 index 0000000000000000000000000000000000000000..0008f0796cd4ca1d214400635183fb985936cd67 --- /dev/null +++ b/application/admin/lang/zh-cn/dashboard.php @@ -0,0 +1,47 @@ + '自定义', + 'Pid' => '父ID', + 'Type' => '栏目类型', + 'Image' => '图片', + 'Total user' => '总会员数', + 'Total view' => '总访问数', + 'Total order' => '总订单数', + 'Total order amount' => '总金额', + 'Today user signup' => '今日注册', + 'Today user login' => '今日登录', + 'Today order' => '今日订单', + 'Unsettle order' => '未处理订单', + 'Seven dnu' => '七日新增', + 'Seven dau' => '七日活跃', + 'Custom zone' => '这里是你的自定义数据', + 'Sales' => '成交数', + 'Orders' => '订单数', + 'Real time' => '实时', + 'Category count' => '分类统计', + 'Category count tips' => '当前分类总记录数', + 'Attachment count' => '附件统计', + 'Attachment count tips' => '当前上传的附件数量', + 'Article count' => '文章统计', + 'News count' => '新闻统计', + 'Comment count' => '评论次数', + 'Like count' => '点赞次数', + 'Recent news' => '最新新闻', + 'Recent discussion' => '最新发贴', + 'Server info' => '服务器信息', + 'PHP version' => 'PHP版本', + 'Fastadmin version' => '主框架版本', + 'Fastadmin addon version' => '插件版本', + 'Thinkphp version' => 'ThinkPHP版本', + 'Sapi name' => '运行方式', + 'Debug mode' => '调试模式', + 'Software' => '环境信息', + 'Upload mode' => '上传模式', + 'Upload url' => '上传URL', + 'Upload cdn url' => '上传CDN', + 'Cdn url' => '静态资源CDN', + 'Timezone' => '时区', + 'Language' => '语言', + 'View more' => '查看更多', +]; diff --git a/application/admin/lang/zh-cn/general/attachment.php b/application/admin/lang/zh-cn/general/attachment.php index ad0c8047bd74e7f97282bdec7fefdedd30f7bd6d..90fe66d72bdfe3e7f3438f3d69d3ca177b2d79da 100644 --- a/application/admin/lang/zh-cn/general/attachment.php +++ b/application/admin/lang/zh-cn/general/attachment.php @@ -1,17 +1,31 @@ '物理路径', - 'Imagewidth' => '宽度', - 'Imageheight' => '宽度', - 'Imagetype' => '图片类型', - 'Imageframes' => '图片帧数', - 'Preview' => '预览', - 'Filesize' => '文件大小', - 'Mimetype' => 'Mime类型', - 'Extparam' => '透传数据', - 'Createtime' => '创建日期', - 'Uploadtime' => '上传时间', - 'Storage' => '存储引擎', - 'Upload by summernote' => '从编辑器上传' + 'Id' => 'ID', + 'Admin_id' => '管理员ID', + 'User_id' => '会员ID', + 'Url' => '物理路径', + 'Imagewidth' => '宽度', + 'Imageheight' => '高度', + 'Imagetype' => '图片类型', + 'Imageframes' => '图片帧数', + 'Preview' => '预览', + 'Filename' => '文件名', + 'Filesize' => '文件大小', + 'Mimetype' => 'Mime类型', + 'Image' => '图片', + 'Audio' => '音频', + 'Video' => '视频', + 'Text' => '文档', + 'Application' => '应用', + 'Zip' => '压缩包', + 'Extparam' => '透传数据', + 'Createtime' => '创建日期', + 'Uploadtime' => '上传时间', + 'Storage' => '存储引擎', + 'Upload to third' => '上传到第三方', + 'Upload to local' => '上传到本地', + 'Upload to third by chunk' => '上传到第三方(分片模式)', + 'Upload to local by chunk' => '上传到本地(分片模式)', + 'Upload from editor' => '从编辑器上传' ]; diff --git a/application/admin/lang/zh-cn/general/config.php b/application/admin/lang/zh-cn/general/config.php index 60511256e565e4e47a7b960c351da519b2049d77..81d79c0f0336eb00d7b307c674c6e75663af0b60 100644 --- a/application/admin/lang/zh-cn/general/config.php +++ b/application/admin/lang/zh-cn/general/config.php @@ -10,30 +10,68 @@ return [ 'Basic' => '基础配置', 'Email' => '邮件配置', 'Attachment' => '附件配置', + 'Dictionary' => '字典配置', 'User' => '会员配置', 'Example' => '示例分组', 'Extend' => '扩展属性', 'String' => '字符', 'Text' => '文本', + 'Editor' => '编辑器', 'Number' => '数字', 'Date' => '日期', 'Time' => '时间', 'Datetime' => '日期时间', + 'Datetimerange' => '日期时间区间', 'Image' => '图片', 'Images' => '图片(多)', 'File' => '文件', 'Files' => '文件(多)', 'Select' => '列表', 'Selects' => '列表(多选)', + 'Switch' => '开关', 'Checkbox' => '复选', 'Radio' => '单选', 'Array' => '数组', 'Array key' => '键名', 'Array value' => '键值', + 'City' => '城市地区', + 'Selectpage' => '关联表', + 'Selectpages' => '关联表(多选)', + 'Custom' => '自定义', + 'Please select table' => '关联表', + 'Selectpage table' => '关联表', + 'Selectpage primarykey' => '存储字段', + 'Selectpage field' => '显示字段', + 'Selectpage conditions' => '筛选条件', + 'Field title' => '字段名', + 'Field value' => '字段值', 'Content' => '数据列表', 'Rule' => '校验规则', + 'Site name' => '站点名称', + 'Beian' => '备案号', + 'Cdn url' => 'CDN地址', + 'Version' => '版本号', + 'Timezone' => '时区', + 'Forbidden ip' => '禁止IP', + 'Languages' => '语言', + 'Fixed page' => '后台固定页', + 'Category type' => '分类类型', + 'Config group' => '配置分组', + 'Rule tips' => '校验规则使用请参考Nice-validator文档', + 'Extend tips' => '扩展属性支持{id}、{name}、{group}、{title}、{value}、{content}、{rule}替换', + 'Mail type' => '邮件发送方式', + 'Mail smtp host' => 'SMTP服务器', + 'Mail smtp port' => 'SMTP端口', + 'Mail smtp user' => 'SMTP用户名', + 'Mail smtp password' => 'SMTP密码', + 'Mail vertify type' => 'SMTP验证方式', + 'Mail from' => '发件人邮箱', + 'Site name incorrect' => '网站名称错误', 'Name already exist' => '变量名称已经存在', + 'Add new config' => '点击添加新的配置', 'Send a test message' => '发送测试邮件', - 'This is a test mail content' => '这是一封测试邮件,用于测试邮件配置是否正常!', - 'This is a test mail' => '这是一封测试邮件', + 'This is a test mail content' => '这是一封来自FastAdmin校验邮件,用于校验邮件配置是否正常!', + 'This is a test mail' => '这是一封来自FastAdmin的邮件', + 'Please input your email' => '请输入测试接收者邮箱', + 'Please input correct email' => '请输入正确的邮箱地址', ]; diff --git a/application/admin/lang/zh-cn/general/crontab.php b/application/admin/lang/zh-cn/general/crontab.php deleted file mode 100644 index 041aab784386164324d4f399cdd4e4652f40e23f..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/general/crontab.php +++ /dev/null @@ -1,16 +0,0 @@ - '任务标题', - 'Maximums' => '最多执行', - 'Sleep' => '延迟秒数', - 'Schedule' => '执行周期', - 'Executes' => '执行次数', - 'Execute time' => '最后执行时间', - 'Request Url' => '请求URL', - 'Execute Sql Script' => '执行SQL', - 'Execute Shell' => '执行Shell', - 'Crontab format invalid' => 'Crontab格式错误', - 'Next execute time' => '下次预计时间', - 'The next %s times the execution time' => '接下来 %s 次的执行时间', -]; diff --git a/application/admin/lang/zh-cn/general/database.php b/application/admin/lang/zh-cn/general/database.php deleted file mode 100644 index 1defa653bb09de5a0bb33f1843123905827efb8b..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/general/database.php +++ /dev/null @@ -1,30 +0,0 @@ - '查询结果', - 'Basic query' => '基础查询', - 'View structure' => '查看表结构', - 'View data' => '查看表数据', - 'Optimize' => '优化表', - 'Repair' => '修复表', - 'Optimize all' => '优化全部表', - 'Repair all' => '修复全部表', - 'Table:%s' => '总计:%s个表', - 'Record:%s' => '记录:%s条', - 'Data:%s' => '占用:%s', - 'Index:%s' => '索引:%s', - 'SQL Result:' => '查询结果:', - 'SQL can not be empty' => 'SQL语句不能为空', - 'Max output:%s' => '最大返回%s条', - 'Total:%s' => '共有%s条记录! ', - 'Row:%s' => '记录:%s', - 'Executes one or multiple queries which are concatenated by a semicolon' => '请输入SQL语句,支持批量查询,多条SQL以分号(;)分格', - 'Query affected %s rows and took %s seconds' => '共影响%s条记录! 耗时:%s秒!', - 'Query returned an empty result' => '返回结果为空!', - 'Query took %s seconds' => '耗时%s秒!', - 'Optimize table %s done' => '优化表[%s]成功', - 'Repair table %s done' => '修复表[%s]成功', - 'Optimize table %s fail' => '优化表[%s]失败', - 'Repair table %s fail' => '修复表[%s]失败' -]; - diff --git a/application/admin/lang/zh-cn/general/profile.php b/application/admin/lang/zh-cn/general/profile.php index e851f9c0a87892d834263507e064e6f83ed91258..8f5ff030b961063e113ee6f874905a825005504a 100644 --- a/application/admin/lang/zh-cn/general/profile.php +++ b/application/admin/lang/zh-cn/general/profile.php @@ -4,6 +4,10 @@ return [ 'Url' => '链接', 'Userame' => '用户名', 'Createtime' => '操作时间', + 'Click to edit' => '点击编辑', 'Admin log' => '操作日志', 'Leave password blank if dont want to change' => '不修改密码请留空', + 'Please input correct email' => '请输入正确的Email地址', + 'Please input correct password' => '密码长度不正确', + 'Email already exists' => '邮箱已经存在', ]; diff --git a/application/admin/lang/zh-cn/index.php b/application/admin/lang/zh-cn/index.php index c4b76fd9d6d4ef6190f36b2269dde2b6d19c12f5..452c36dc91f654d1d763aad02aa5577ae16034ad 100644 --- a/application/admin/lang/zh-cn/index.php +++ b/application/admin/lang/zh-cn/index.php @@ -26,9 +26,33 @@ return [ 'Username or password is incorrect' => '用户名或密码不正确', 'Username is incorrect' => '用户名不正确', 'Password is incorrect' => '密码不正确', + 'Admin is forbidden' => '管理员已经被禁止登录', + 'Please try again after 1 day' => '请于1天后再尝试登录', 'Login successful' => '登录成功!', 'Logout successful' => '退出成功!', 'Verification code is incorrect' => '验证码不正确', 'Wipe cache completed' => '清除缓存成功', 'Wipe cache failed' => '清除缓存失败', + 'Wipe cache' => '清空缓存', + 'Wipe all cache' => '一键清除缓存', + 'Wipe content cache' => '清空内容缓存', + 'Wipe template cache' => '清除模板缓存', + 'Wipe addons cache' => '清除插件缓存', + 'Check for updates' => '检测更新', + 'Discover new version' => '发现新版本', + 'Go to download' => '去下载更新', + 'Currently is the latest version' => '当前已经是最新版本', + 'Ignore this version' => '忽略此次更新', + 'Do not remind again' => '不再提示', + 'Your current version' => '你的版本是', + 'New version' => '新版本', + 'Release notes' => '更新说明', + 'Latest news' => '最新消息', + 'View more' => '查看更多', + 'Links' => '相关链接', + 'Docs' => '官方文档', + 'Forum' => '交流社区', + 'QQ qun' => 'QQ交流群', + 'Captcha' => '验证码', + 'Security tips' => ' 安全提示:为了你的后台安全,请勿将后台管理入口设置为admin或admin.php', ]; diff --git a/application/admin/lang/zh-cn/page.php b/application/admin/lang/zh-cn/page.php deleted file mode 100644 index 852303d05d5bc08c342a2e97884c8974b37a9c4b..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/page.php +++ /dev/null @@ -1,17 +0,0 @@ - 'ID', - 'category_id' => '分类ID', - 'category' => '分类', - 'title' => '标题', - 'keywords' => '关键字', - 'flag' => '标志', - 'image' => '头像', - 'content' => '内容', - 'icon' => '图标', - 'views' => '点击', - 'comments' => '评论', - 'weigh' => '权重', - 'status' => '状态' -]; diff --git a/application/admin/lang/zh-cn/user/group.php b/application/admin/lang/zh-cn/user/group.php new file mode 100644 index 0000000000000000000000000000000000000000..2bd998bececd499f6a6b89e09e325da990435226 --- /dev/null +++ b/application/admin/lang/zh-cn/user/group.php @@ -0,0 +1,9 @@ + '组名', + 'Rules' => '权限节点', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/user/rule.php b/application/admin/lang/zh-cn/user/rule.php new file mode 100644 index 0000000000000000000000000000000000000000..197da5c4d3d244b4f16a560aaa3fcee4edc96034 --- /dev/null +++ b/application/admin/lang/zh-cn/user/rule.php @@ -0,0 +1,15 @@ + '父ID', + 'Name' => '规则', + 'Title' => '标题', + 'Remark' => '备注', + 'Ismenu' => '是否菜单', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Menu tips' => '规则任意,请不可重复,仅做层级显示,无需匹配控制器和方法', + 'Node tips' => '模块/控制器/方法名', + 'Weigh' => '权重', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/user/user.php b/application/admin/lang/zh-cn/user/user.php new file mode 100644 index 0000000000000000000000000000000000000000..13f096dfd5f17b5f3789320e1bc7460577b37d27 --- /dev/null +++ b/application/admin/lang/zh-cn/user/user.php @@ -0,0 +1,33 @@ + 'ID', + 'Group_id' => '组别ID', + 'Username' => '用户名', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Salt' => '密码盐', + 'Email' => '电子邮箱', + 'Mobile' => '手机号', + 'Avatar' => '头像', + 'Level' => '等级', + 'Gender' => '性别', + 'Male' => '男', + 'FeMale' => '女', + 'Birthday' => '生日', + 'Bio' => '格言', + 'Score' => '积分', + 'Successions' => '连续登录天数', + 'Maxsuccessions' => '最大连续登录天数', + 'Prevtime' => '上次登录时间', + 'Logintime' => '登录时间', + 'Loginip' => '登录IP', + 'Loginfailure' => '失败次数', + 'Joinip' => '加入IP', + 'Jointime' => '加入时间', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Token' => 'Token', + 'Status' => '状态', + 'Leave password blank if dont want to change' => '不修改密码请留空', +]; diff --git a/application/admin/lang/zh-cn/version.php b/application/admin/lang/zh-cn/version.php deleted file mode 100644 index ce7526936a9bcb2dc1a6fad5f7d5531d23a9eb20..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/version.php +++ /dev/null @@ -1,15 +0,0 @@ - 'ID', - 'oldversion' => '旧版本号', - 'newversion' => '新版本号', - 'packagesize' => '包大小', - 'content' => '升级内容', - 'downloadurl' => '下载地址', - 'enforce' => '强制更新', - 'createtime' => '创建时间', - 'updatetime' => '更新时间', - 'weigh' => '权重', - 'status' => '状态' -]; diff --git a/application/admin/lang/zh-cn/wechat/autoreply.php b/application/admin/lang/zh-cn/wechat/autoreply.php deleted file mode 100644 index 6ba7061c3f251d21bf20c416cf0d54c024da5161..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/wechat/autoreply.php +++ /dev/null @@ -1,8 +0,0 @@ - '文本', - 'Event key' => '响应标识', - 'Remark' => '备注', - 'Text already exists' => '文本已经存在', -]; diff --git a/application/admin/lang/zh-cn/wechat/config.php b/application/admin/lang/zh-cn/wechat/config.php deleted file mode 100644 index fbbd04a17057f882d698570347878db3dde394a7..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/wechat/config.php +++ /dev/null @@ -1,10 +0,0 @@ - '配置名称', - 'value' => '配置值', - 'Json key' => '键', - 'Json value' => '值', - 'createtime' => '创建时间', - 'updatetime' => '更新时间' -]; diff --git a/application/admin/lang/zh-cn/wechat/response.php b/application/admin/lang/zh-cn/wechat/response.php deleted file mode 100644 index 37bb3bd7c403440ceef43ad3af67f36c03dc9342..0000000000000000000000000000000000000000 --- a/application/admin/lang/zh-cn/wechat/response.php +++ /dev/null @@ -1,8 +0,0 @@ - '资源标题', - 'Event key' => '事件标识', - 'Text' => '文本', - 'App' => '应用', -]; diff --git a/application/admin/library/Auth.php b/application/admin/library/Auth.php index 145298fc4337a86e564ec30ebf1eccca9ae8d458..0616a6f9287ac6cec845a631827a1383bc6268b2 100644 --- a/application/admin/library/Auth.php +++ b/application/admin/library/Auth.php @@ -5,15 +5,18 @@ namespace app\admin\library; use app\admin\model\Admin; use fast\Random; use fast\Tree; +use think\Config; use think\Cookie; +use think\Hook; use think\Request; use think\Session; class Auth extends \fast\Auth { - + protected $_error = ''; protected $requestUri = ''; protected $breadcrumb = []; + protected $logined = false; //登录状态 public function __construct() { @@ -25,40 +28,56 @@ class Auth extends \fast\Auth return Session::get('admin.' . $name); } + /** + * 管理员登录 + * + * @param string $username 用户名 + * @param string $password 密码 + * @param int $keeptime 有效时长 + * @return boolean + */ public function login($username, $password, $keeptime = 0) { $admin = Admin::get(['username' => $username]); - if (!$admin) - { + if (!$admin) { + $this->setError('Username is incorrect'); + return false; + } + if ($admin['status'] == 'hidden') { + $this->setError('Admin is forbidden'); return false; } - if ($admin->password != md5(md5($password) . $admin->salt)) - { + if (Config::get('fastadmin.login_failure_retry') && $admin->loginfailure >= 10 && time() - $admin->updatetime < 86400) { + $this->setError('Please try again after 1 day'); + return false; + } + if ($admin->password != md5(md5($password) . $admin->salt)) { $admin->loginfailure++; $admin->save(); + $this->setError('Password is incorrect'); return false; } $admin->loginfailure = 0; $admin->logintime = time(); + $admin->loginip = request()->ip(); $admin->token = Random::uuid(); $admin->save(); - Session::set("admin", $admin); + Session::set("admin", $admin->toArray()); $this->keeplogin($keeptime); return true; } /** - * 注销登录 + * 退出登录 */ public function logout() { $admin = Admin::get(intval($this->id)); - if (!$admin) - { - return true; + if ($admin) { + $admin->token = ''; + $admin->save(); } - $admin->token = ''; - $admin->save(); + $this->logined = false; //重置登录状态 Session::delete("admin"); Cookie::delete("keeplogin"); return true; @@ -71,47 +90,46 @@ class Auth extends \fast\Auth public function autologin() { $keeplogin = Cookie::get('keeplogin'); - if (!$keeplogin) - { + if (!$keeplogin) { return false; } list($id, $keeptime, $expiretime, $key) = explode('|', $keeplogin); - if ($id && $keeptime && $expiretime && $key && $expiretime > time()) - { + if ($id && $keeptime && $expiretime && $key && $expiretime > time()) { $admin = Admin::get($id); - if (!$admin || !$admin->token) - { + if (!$admin || !$admin->token) { return false; } //token有变更 - if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token)) - { + if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token)) { + return false; + } + $ip = request()->ip(); + //IP有变动 + if ($admin->loginip != $ip) { return false; } - Session::set("admin", $admin); + Session::set("admin", $admin->toArray()); //刷新自动登录的时效 $this->keeplogin($keeptime); return true; - } - else - { + } else { return false; } } /** * 刷新保持登录的Cookie + * * @param int $keeptime - * @return boolean + * @return boolean */ protected function keeplogin($keeptime = 0) { - if ($keeptime) - { + if ($keeptime) { $expiretime = time() + $keeptime; $key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token); $data = [$this->id, $keeptime, $expiretime, $key]; - Cookie::set('keeplogin', implode('|', $data)); + Cookie::set('keeplogin', implode('|', $data), 86400 * 30); return true; } return false; @@ -119,31 +137,32 @@ class Auth extends \fast\Auth public function check($name, $uid = '', $relation = 'or', $mode = 'url') { - return parent::check($name, $this->id, $relation, $mode); + $uid = $uid ? $uid : $this->id; + return parent::check($name, $uid, $relation, $mode); } /** * 检测当前控制器和方法是否匹配传递的数组 * * @param array $arr 需要验证权限的数组 + * @return bool */ public function match($arr = []) { $request = Request::instance(); $arr = is_array($arr) ? $arr : explode(',', $arr); - if (!$arr) - { - return FALSE; + if (!$arr) { + return false; } + $arr = array_map('strtolower', $arr); // 是否存在 - if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) - { - return TRUE; + if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) { + return true; } // 没找到匹配 - return FALSE; + return false; } /** @@ -153,7 +172,32 @@ class Auth extends \fast\Auth */ public function isLogin() { - return Session::get('admin') ? true : false; + if ($this->logined) { + return true; + } + $admin = Session::get('admin'); + if (!$admin) { + return false; + } + //判断是否同一时间同一账号只能在一个地方登录 + if (Config::get('fastadmin.login_unique')) { + $my = Admin::get($admin['id']); + if (!$my || $my['token'] != $admin['token']) { + $this->logined = false; //重置登录状态 + Session::delete("admin"); + Cookie::delete("keeplogin"); + return false; + } + } + //判断管理员IP是否变动 + if (Config::get('fastadmin.loginip_check')) { + if (!isset($admin['loginip']) || $admin['loginip'] != request()->ip()) { + $this->logout(); + return false; + } + } + $this->logined = true; + return true; } /** @@ -201,7 +245,96 @@ class Auth extends \fast\Auth public function isSuperAdmin() { - return in_array('*', $this->getRuleIds()) ? TRUE : FALSE; + return in_array('*', $this->getRuleIds()) ? true : false; + } + + /** + * 获取管理员所属于的分组ID + * @param int $uid + * @return array + */ + public function getGroupIds($uid = null) + { + $groups = $this->getGroups($uid); + $groupIds = []; + foreach ($groups as $K => $v) { + $groupIds[] = (int)$v['group_id']; + } + return $groupIds; + } + + /** + * 取出当前管理员所拥有权限的分组 + * @param boolean $withself 是否包含当前所在的分组 + * @return array + */ + public function getChildrenGroupIds($withself = false) + { + //取出当前管理员所有的分组 + $groups = $this->getGroups(); + $groupIds = []; + foreach ($groups as $k => $v) { + $groupIds[] = $v['id']; + } + $originGroupIds = $groupIds; + foreach ($groups as $k => $v) { + if (in_array($v['pid'], $originGroupIds)) { + $groupIds = array_diff($groupIds, [$v['id']]); + unset($groups[$k]); + } + } + // 取出所有分组 + $groupList = \app\admin\model\AuthGroup::where(['status' => 'normal'])->select(); + $objList = []; + foreach ($groups as $k => $v) { + if ($v['rules'] === '*') { + $objList = $groupList; + break; + } + // 取出包含自己的所有子节点 + $childrenList = Tree::instance()->init($groupList)->getChildren($v['id'], true); + $obj = Tree::instance()->init($childrenList)->getTreeArray($v['pid']); + $objList = array_merge($objList, Tree::instance()->getTreeList($obj)); + } + $childrenGroupIds = []; + foreach ($objList as $k => $v) { + $childrenGroupIds[] = $v['id']; + } + if (!$withself) { + $childrenGroupIds = array_diff($childrenGroupIds, $groupIds); + } + return $childrenGroupIds; + } + + /** + * 取出当前管理员所拥有权限的管理员 + * @param boolean $withself 是否包含自身 + * @return array + */ + public function getChildrenAdminIds($withself = false) + { + $childrenAdminIds = []; + if (!$this->isSuperAdmin()) { + $groupIds = $this->getChildrenGroupIds(false); + $authGroupList = \app\admin\model\AuthGroupAccess:: + field('uid,group_id') + ->where('group_id', 'in', $groupIds) + ->select(); + foreach ($authGroupList as $k => $v) { + $childrenAdminIds[] = $v['uid']; + } + } else { + //超级管理员拥有所有人的权限 + $childrenAdminIds = Admin::column('id'); + } + if ($withself) { + if (!in_array($this->id, $childrenAdminIds)) { + $childrenAdminIds[] = $this->id; + } + } else { + $childrenAdminIds = array_diff($childrenAdminIds, [$this->id]); + } + return $childrenAdminIds; } /** @@ -211,90 +344,191 @@ class Auth extends \fast\Auth */ public function getBreadCrumb($path = '') { - if ($this->breadcrumb || !$path) + if ($this->breadcrumb || !$path) { return $this->breadcrumb; - $path_rule_id = 0; - foreach ($this->rules as $rule) - { - $path_rule_id = $rule['name'] == $path ? $rule['id'] : $path_rule_id; } - if ($path_rule_id) - { - $this->breadcrumb = Tree::instance()->init($this->rules)->getParents($path_rule_id, true); - foreach ($this->breadcrumb as $k => &$v) - { - $v['url'] = url($v['name']); + $titleArr = []; + $menuArr = []; + $urlArr = explode('/', $path); + foreach ($urlArr as $index => $item) { + $pathArr[implode('/', array_slice($urlArr, 0, $index + 1))] = $index; + } + if (!$this->rules && $this->id) { + $this->getRuleList(); + } + foreach ($this->rules as $rule) { + if (isset($pathArr[$rule['name']])) { + $rule['title'] = __($rule['title']); + $rule['url'] = url($rule['name']); + $titleArr[$pathArr[$rule['name']]] = $rule['title']; + $menuArr[$pathArr[$rule['name']]] = $rule; } + } + ksort($menuArr); + $this->breadcrumb = $menuArr; return $this->breadcrumb; } /** - * 获取左侧菜单栏 + * 获取左侧和顶部菜单栏 * - * @param array $params URL对应的badge数据 - * @return string + * @param array $params URL对应的badge数据 + * @param string $fixedPage 默认页 + * @return array */ public function getSidebar($params = [], $fixedPage = 'dashboard') { + // 边栏开始 + Hook::listen("admin_sidebar_begin", $params); $colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple']; $colorNums = count($colorArr); $badgeList = []; $module = request()->module(); // 生成菜单的badge - foreach ($params as $k => $v) - { - if (stripos($k, '/') === false) - { - $url = '/' . $module . '/' . $k; - } - else - { - $url = url($k); - } - - if (is_array($v)) - { + foreach ($params as $k => $v) { + $url = $k; + if (is_array($v)) { $nums = isset($v[0]) ? $v[0] : 0; $color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums]; $class = isset($v[2]) ? $v[2] : 'label'; - } - else - { + } else { $nums = $v; $color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums]; $class = 'label'; } //必须nums大于0才显示 - if ($nums) - { + if ($nums) { $badgeList[$url] = '' . $nums . ''; } } // 读取管理员当前拥有的权限节点 $userRule = $this->getRuleList(); - $select_id = 0; - $activeUrl = '/' . $module . '/' . $fixedPage; + $selected = $referer = []; + $refererUrl = Session::get('referer'); + $pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader'); // 必须将结果集转换为数组 - $ruleList = collection(model('AuthRule')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray(); - foreach ($ruleList as $k => &$v) - { - if (!in_array($v['name'], $userRule)) - { + $ruleList = collection(\app\admin\model\AuthRule::where('status', 'normal') + ->where('ismenu', 1) + ->order('weigh', 'desc') + ->cache("__menu__") + ->select())->toArray(); + $indexRuleList = \app\admin\model\AuthRule::where('status', 'normal') + ->where('ismenu', 0) + ->where('name', 'like', '%/index') + ->column('name,pid'); + $pidArr = array_filter(array_unique(array_map(function ($item) { + return $item['pid']; + }, $ruleList))); + foreach ($ruleList as $k => &$v) { + if (!in_array($v['name'], $userRule)) { unset($ruleList[$k]); continue; } - $select_id = $v['name'] == $activeUrl ? $v['id'] : $select_id; - $v['url'] = $v['name']; + $indexRuleName = $v['name'] . '/index'; + if (isset($indexRuleList[$indexRuleName]) && !in_array($indexRuleName, $userRule)) { + unset($ruleList[$k]); + continue; + } + $v['icon'] = $v['icon'] . ' fa-fw'; + $v['url'] = '/' . $module . '/' . $v['name']; $v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : ''; - $v['py'] = \fast\Pinyin::get($v['title'], true); - $v['pinyin'] = \fast\Pinyin::get($v['title']); + $v['py'] = $pinyin->abbr($v['title'], ''); + $v['pinyin'] = $pinyin->permalink($v['title'], ''); + $v['title'] = __($v['title']); + $selected = $v['name'] == $fixedPage ? $v : $selected; + $referer = url($v['url']) == $refererUrl ? $v : $referer; + } + $lastArr = array_diff($pidArr, array_filter(array_unique(array_map(function ($item) { + return $item['pid']; + }, $ruleList)))); + foreach ($ruleList as $index => $item) { + if (in_array($item['id'], $lastArr)) { + unset($ruleList[$index]); + } + } + if ($selected == $referer) { + $referer = []; + } + $selected && $selected['url'] = url($selected['url']); + $referer && $referer['url'] = url($referer['url']); + + $select_id = $selected ? $selected['id'] : 0; + $menu = $nav = ''; + if (Config::get('fastadmin.multiplenav')) { + $topList = []; + foreach ($ruleList as $index => $item) { + if (!$item['pid']) { + $topList[] = $item; + } + } + $selectParentIds = []; + $tree = Tree::instance(); + $tree->init($ruleList); + if ($select_id) { + $selectParentIds = $tree->getParentsIds($select_id, true); + } + foreach ($topList as $index => $item) { + $childList = Tree::instance()->getTreeMenu( + $item['id'], + '
    • @title @caret @badge @childlist
    • ', + $select_id, + '', + 'ul', + 'class="treeview-menu"' + ); + $current = in_array($item['id'], $selectParentIds); + $url = $childList ? 'javascript:;' : url($item['url']); + $addtabs = $childList || !$url ? "" : (stripos($url, "?") !== false ? "&" : "?") . "ref=addtabs"; + $childList = str_replace( + '" pid="' . $item['id'] . '"', + ' ' . ($current ? '' : 'hidden') . '" pid="' . $item['id'] . '"', + $childList + ); + $nav .= '
    • ' . $item['title'] . '
    • '; + $menu .= $childList; + } + } else { + // 构造菜单数据 + Tree::instance()->init($ruleList); + $menu = Tree::instance()->getTreeMenu( + 0, + '
    • @title @caret @badge @childlist
    • ', + $select_id, + '', + 'ul', + 'class="treeview-menu"' + ); + if ($selected) { + $nav .= ''; + } + if ($referer) { + $nav .= ''; + } } - // 构造菜单数据 - Tree::instance()->init($ruleList); - $menu = Tree::instance()->getTreeMenu(0, '
    • @title @caret @badge @childlist
    • ', $select_id, '', 'ul', 'class="treeview-menu"'); - return $menu; + + return [$menu, $nav, $selected, $referer]; } + /** + * 设置错误信息 + * + * @param string $error 错误信息 + * @return Auth + */ + public function setError($error) + { + $this->_error = $error; + return $this; + } + + /** + * 获取错误信息 + * @return string + */ + public function getError() + { + return $this->_error ? __($this->_error) : ''; + } } diff --git a/application/admin/library/traits/Backend.php b/application/admin/library/traits/Backend.php old mode 100644 new mode 100755 index 67bd793b2eb596bd041d783cff618ce4d2102a0a..f81f62fee840c33d300fd432d4af641b5e5e9094 --- a/application/admin/library/traits/Backend.php +++ b/application/admin/library/traits/Backend.php @@ -1,176 +1,482 @@ -request->filter(['strip_tags']); - if ($this->request->isAjax()) - { - //如果发送的来源是Selectpage,则转发到Selectpage - if ($this->request->request('pkey_name')) - { - return $this->selectpage(); - } - list($where, $sort, $order, $offset, $limit) = $this->buildparams(); - $total = $this->model - ->where($where) - ->order($sort, $order) - ->count(); - - $list = $this->model - ->where($where) - ->order($sort, $order) - ->limit($offset, $limit) - ->select(); - - $result = array("total" => $total, "rows" => $list); - - return json($result); - } - return $this->view->fetch(); - } - - /** - * 添加 - */ - public function add() - { - if ($this->request->isPost()) - { - $params = $this->request->post("row/a"); - if ($params) - { - foreach ($params as $k => &$v) - { - $v = is_array($v) ? implode(',', $v) : $v; - } - try - { - //是否采用模型验证 - if ($this->modelValidate) - { - $name = basename(str_replace('\\', '/', get_class($this->model))); - $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate; - $this->model->validate($validate); - } - $result = $this->model->save($params); - if ($result !== false) - { - $this->success(); - } - else - { - $this->error($this->model->getError()); - } - } - catch (\think\exception\PDOException $e) - { - $this->error($e->getMessage()); - } - } - $this->error(__('Parameter %s can not be empty', '')); - } - return $this->view->fetch(); - } - - /** - * 编辑 - */ - public function edit($ids = NULL) - { - $row = $this->model->get($ids); - if (!$row) - $this->error(__('No Results were found')); - if ($this->request->isPost()) - { - $params = $this->request->post("row/a"); - if ($params) - { - foreach ($params as $k => &$v) - { - $v = is_array($v) ? implode(',', $v) : $v; - } - try - { - //是否采用模型验证 - if ($this->modelValidate) - { - $name = basename(str_replace('\\', '/', get_class($this->model))); - $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : true) : $this->modelValidate; - $row->validate($validate); - } - $result = $row->save($params); - if ($result !== false) - { - $this->success(); - } - else - { - $this->error($row->getError()); - } - } - catch (think\exception\PDOException $e) - { - $this->error($e->getMessage()); - } - } - $this->error(__('Parameter %s can not be empty', '')); - } - $this->view->assign("row", $row); - return $this->view->fetch(); - } - - /** - * 删除 - */ - public function del($ids = "") - { - if ($ids) - { - $count = $this->model->destroy($ids); - if ($count) - { - $this->success(); - } - } - $this->error(__('Parameter %s can not be empty', 'ids')); - } - - /** - * 批量更新 - */ - public function multi($ids = "") - { - $ids = $ids ? $ids : $this->request->param("ids"); - if ($ids) - { - if ($this->request->has('params')) - { - parse_str($this->request->post("params"), $values); - $values = array_intersect_key($values, array_flip(is_array($this->multiFields) ? $this->multiFields : explode(',', $this->multiFields))); - if ($values) - { - $count = $this->model->where($this->model->getPk(), 'in', $ids)->update($values); - if ($count) - { - $this->success(); - } - } - else - { - $this->error(__('You have no permission')); - } - } - } - $this->error(__('Parameter %s can not be empty', 'ids')); - } - -} +excludeFields)) { + foreach ($this->excludeFields as $field) { + if (key_exists($field, $params)) { + unset($params[$field]); + } + } + } else { + if (key_exists($this->excludeFields, $params)) { + unset($params[$this->excludeFields]); + } + } + return $params; + } + + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 回收站 + */ + public function recyclebin() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->onlyTrashed() + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + $result = false; + Db::startTrans(); + try { + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this->model)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate; + $this->model->validateFailException(true)->validate($validate); + } + $result = $this->model->allowField(true)->save($params); + Db::commit(); + } catch (ValidateException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (PDOException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($result !== false) { + $this->success(); + } else { + $this->error(__('No rows were inserted')); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + if (!in_array($row[$this->dataLimitField], $adminIds)) { + $this->error(__('You have no permission')); + } + } + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + $result = false; + Db::startTrans(); + try { + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this->model)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate; + $row->validateFailException(true)->validate($validate); + } + $result = $row->allowField(true)->save($params); + Db::commit(); + } catch (ValidateException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (PDOException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($result !== false) { + $this->success(); + } else { + $this->error(__('No rows were updated')); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $list = $this->model->where($pk, 'in', $ids)->select(); + + $count = 0; + Db::startTrans(); + try { + foreach ($list as $k => $v) { + $count += $v->delete(); + } + Db::commit(); + } catch (PDOException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } else { + $this->error(__('No rows were deleted')); + } + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 真实删除 + */ + public function destroy($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $count = 0; + Db::startTrans(); + try { + $list = $this->model->onlyTrashed()->select(); + foreach ($list as $k => $v) { + $count += $v->delete(true); + } + Db::commit(); + } catch (PDOException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } else { + $this->error(__('No rows were deleted')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 还原 + */ + public function restore($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $count = 0; + Db::startTrans(); + try { + $list = $this->model->onlyTrashed()->select(); + foreach ($list as $index => $item) { + $count += $item->restore(); + } + Db::commit(); + } catch (PDOException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } + $this->error(__('No rows were updated')); + } + + /** + * 批量更新 + */ + public function multi($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + if ($this->request->has('params')) { + parse_str($this->request->post("params"), $values); + $values = $this->auth->isSuperAdmin() ? $values : array_intersect_key($values, array_flip(is_array($this->multiFields) ? $this->multiFields : explode(',', $this->multiFields))); + if ($values) { + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $count = 0; + Db::startTrans(); + try { + $list = $this->model->where($this->model->getPk(), 'in', $ids)->select(); + foreach ($list as $index => $item) { + $count += $item->allowField(true)->isUpdate(true)->save($values); + } + Db::commit(); + } catch (PDOException $e) { + Db::rollback(); + $this->error($e->getMessage()); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } else { + $this->error(__('No rows were updated')); + } + } else { + $this->error(__('You have no permission')); + } + } + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 导入 + */ + protected function import() + { + $file = $this->request->request('file'); + if (!$file) { + $this->error(__('Parameter %s can not be empty', 'file')); + } + $filePath = ROOT_PATH . DS . 'public' . DS . $file; + if (!is_file($filePath)) { + $this->error(__('No results were found')); + } + //实例化reader + $ext = pathinfo($filePath, PATHINFO_EXTENSION); + if (!in_array($ext, ['csv', 'xls', 'xlsx'])) { + $this->error(__('Unknown data format')); + } + if ($ext === 'csv') { + $file = fopen($filePath, 'r'); + $filePath = tempnam(sys_get_temp_dir(), 'import_csv'); + $fp = fopen($filePath, "w"); + $n = 0; + while ($line = fgets($file)) { + $line = rtrim($line, "\n\r\0"); + $encoding = mb_detect_encoding($line, ['utf-8', 'gbk', 'latin1', 'big5']); + if ($encoding != 'utf-8') { + $line = mb_convert_encoding($line, 'utf-8', $encoding); + } + if ($n == 0 || preg_match('/^".*"$/', $line)) { + fwrite($fp, $line . "\n"); + } else { + fwrite($fp, '"' . str_replace(['"', ','], ['""', '","'], $line) . "\"\n"); + } + $n++; + } + fclose($file) || fclose($fp); + + $reader = new Csv(); + } elseif ($ext === 'xls') { + $reader = new Xls(); + } else { + $reader = new Xlsx(); + } + + //导入文件首行类型,默认是注释,如果需要使用字段名称请使用name + $importHeadType = isset($this->importHeadType) ? $this->importHeadType : 'comment'; + + $table = $this->model->getQuery()->getTable(); + $database = \think\Config::get('database.database'); + $fieldArr = []; + $list = db()->query("SELECT COLUMN_NAME,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?", [$table, $database]); + foreach ($list as $k => $v) { + if ($importHeadType == 'comment') { + $fieldArr[$v['COLUMN_COMMENT']] = $v['COLUMN_NAME']; + } else { + $fieldArr[$v['COLUMN_NAME']] = $v['COLUMN_NAME']; + } + } + + //加载文件 + $insert = []; + try { + if (!$PHPExcel = $reader->load($filePath)) { + $this->error(__('Unknown data format')); + } + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allColumn = $currentSheet->getHighestDataColumn(); //取得最大的列号 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + $maxColumnNumber = Coordinate::columnIndexFromString($allColumn); + $fields = []; + for ($currentRow = 1; $currentRow <= 1; $currentRow++) { + for ($currentColumn = 1; $currentColumn <= $maxColumnNumber; $currentColumn++) { + $val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue(); + $fields[] = $val; + } + } + + for ($currentRow = 2; $currentRow <= $allRow; $currentRow++) { + $values = []; + for ($currentColumn = 1; $currentColumn <= $maxColumnNumber; $currentColumn++) { + $val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue(); + $values[] = is_null($val) ? '' : $val; + } + $row = []; + $temp = array_combine($fields, $values); + foreach ($temp as $k => $v) { + if (isset($fieldArr[$k]) && $k !== '') { + $row[$fieldArr[$k]] = $v; + } + } + if ($row) { + $insert[] = $row; + } + } + } catch (Exception $exception) { + $this->error($exception->getMessage()); + } + if (!$insert) { + $this->error(__('No rows were updated')); + } + + try { + //是否包含admin_id字段 + $has_admin_id = false; + foreach ($fieldArr as $name => $key) { + if ($key == 'admin_id') { + $has_admin_id = true; + break; + } + } + if ($has_admin_id) { + $auth = Auth::instance(); + foreach ($insert as &$val) { + if (!isset($val['admin_id']) || empty($val['admin_id'])) { + $val['admin_id'] = $auth->isLogin() ? $auth->id : 0; + } + } + } + $this->model->saveAll($insert); + } catch (PDOException $exception) { + $msg = $exception->getMessage(); + if (preg_match("/.+Integrity constraint violation: 1062 Duplicate entry '(.+)' for key '(.+)'/is", $msg, $matches)) { + $msg = "导入失败,包含【{$matches[1]}】的记录已存在"; + }; + $this->error($msg); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + + $this->success(); + } +} diff --git a/application/admin/model/Admin.php b/application/admin/model/Admin.php index b985e731b982f693adcaa847858fdbb3e22c4824..8c47f6f7447835740c41491caaf814143c0d0b0f 100644 --- a/application/admin/model/Admin.php +++ b/application/admin/model/Admin.php @@ -1,34 +1,34 @@ -encryptPassword($NewPassword); - $ret = $this->where(['id' => $uid])->update(['password' => $passwd]); - return $ret; - } - - // 密码加密 - protected function encryptPassword($password, $salt = '', $encrypt = 'md5') - { - return $encrypt($password . $salt); - } - -} +encryptPassword($NewPassword); + $ret = $this->where(['id' => $uid])->update(['password' => $passwd]); + return $ret; + } + + // 密码加密 + protected function encryptPassword($password, $salt = '', $encrypt = 'md5') + { + return $encrypt($password . $salt); + } + +} diff --git a/application/admin/model/AdminLog.php b/application/admin/model/AdminLog.php index b4f36687d79cf3960463fc0e0f26b906f81d7f84..faf7e5620ed958bc091b2266563dafdfa6717094 100644 --- a/application/admin/model/AdminLog.php +++ b/application/admin/model/AdminLog.php @@ -1,74 +1,114 @@ -id : 0; - $username = $admin ? $admin->username : __('Unknown'); - $content = self::$content; - if (!$content) - { - $content = request()->param(); - foreach ($content as $k => $v) - { - if (is_string($v) && strlen($v) > 200 || stripos($k, 'password') !== false) - { - unset($content[$k]); - } - } - } - $title = self::$title; - if (!$title) - { - $title = []; - $breadcrumb = \app\admin\library\Auth::instance()->getBreadcrumb(); - foreach ($breadcrumb as $k => $v) - { - $title[] = $v['title']; - } - $title = implode(' ', $title); - } - self::create([ - 'title' => $title, - 'content' => !is_scalar($content) ? json_encode($content) : $content, - 'url' => request()->url(), - 'admin_id' => $admin_id, - 'username' => $username, - 'useragent' => request()->server('HTTP_USER_AGENT'), - 'ip' => request()->ip() - ]); - } - - public function admin() - { - return $this->belongsTo('Admin', 'admin_id')->setEagerlyType(0); - } - -} +isLogin() ? $auth->id : 0; + $username = $auth->isLogin() ? $auth->username : __('Unknown'); + + $controllername = Loader::parseName(request()->controller()); + $actionname = strtolower(request()->action()); + $path = str_replace('.', '/', $controllername) . '/' . $actionname; + if (self::$ignoreRegex) { + foreach (self::$ignoreRegex as $index => $item) { + if (preg_match($item, $path)) { + return; + } + } + } + $content = $content ? $content : self::$content; + if (!$content) { + $content = request()->param('', null, 'trim,strip_tags,htmlspecialchars'); + $content = self::getPureContent($content); + } + $title = $title ? $title : self::$title; + if (!$title) { + $title = []; + $breadcrumb = Auth::instance()->getBreadcrumb($path); + foreach ($breadcrumb as $k => $v) { + $title[] = $v['title']; + } + $title = implode(' / ', $title); + } + self::create([ + 'title' => $title, + 'content' => !is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content, + 'url' => substr(request()->url(), 0, 1500), + 'admin_id' => $admin_id, + 'username' => $username, + 'useragent' => substr(request()->server('HTTP_USER_AGENT'), 0, 255), + 'ip' => request()->ip() + ]); + } + + /** + * 获取已屏蔽关键信息的数据 + * @param $content + * @return false|string + */ + protected static function getPureContent($content) + { + if (!is_array($content)) { + return $content; + } + foreach ($content as $index => &$item) { + if (preg_match("/(password|salt|token)/i", $index)) { + $item = "***"; + } else { + if (is_array($item)) { + $item = self::getPureContent($item); + } + } + } + return $content; + } + + public function admin() + { + return $this->belongsTo('Admin', 'admin_id')->setEagerlyType(0); + } +} diff --git a/application/admin/model/AuthGroup.php b/application/admin/model/AuthGroup.php index 214a6985feefacfcff88e502da5e84058cd0e40e..bfbaec3dcca2222e1be938a8c7c40054d53efd81 100644 --- a/application/admin/model/AuthGroup.php +++ b/application/admin/model/AuthGroup.php @@ -1,16 +1,21 @@ - __('String'), - 'text' => __('Text'), - 'number' => __('Number'), - 'datetime' => __('Datetime'), - 'select' => __('Select'), - 'selects' => __('Selects'), - 'image' => __('Image'), - 'images' => __('Images'), - 'file' => __('File'), - 'files' => __('Files'), - 'checkbox' => __('Checkbox'), - 'radio' => __('Radio'), - 'array' => __('Array'), - ]; - return $typeList; - } - - /** - * 读取分类分组列表 - * @return array - */ - public static function getGroupList() - { - $groupList = config('site.configgroup'); - return $groupList; - } - -} diff --git a/application/admin/model/User.php b/application/admin/model/User.php new file mode 100644 index 0000000000000000000000000000000000000000..822c81b1743d5623f582f1d6d7bb2a64b99045d1 --- /dev/null +++ b/application/admin/model/User.php @@ -0,0 +1,114 @@ +origin; + } + + protected static function init() + { + self::beforeUpdate(function ($row) { + $changed = $row->getChangedData(); + //如果有修改密码 + if (isset($changed['password'])) { + if ($changed['password']) { + $salt = \fast\Random::alnum(); + $row->password = \app\common\library\Auth::instance()->getEncryptPassword($changed['password'], $salt); + $row->salt = $salt; + } else { + unset($row->password); + } + } + }); + + + self::beforeUpdate(function ($row) { + $changedata = $row->getChangedData(); + if (isset($changedata['money'])) { + $origin = $row->getOriginData(); + MoneyLog::create(['user_id' => $row['id'], 'money' => $changedata['money'] - $origin['money'], 'before' => $origin['money'], 'after' => $changedata['money'], 'memo' => '管理员变更金额']); + } + if (isset($changedata['score'])) { + $origin = $row->getOriginData(); + ScoreLog::create(['user_id' => $row['id'], 'score' => $changedata['score'] - $origin['score'], 'before' => $origin['score'], 'after' => $changedata['score'], 'memo' => '管理员变更积分']); + } + }); + } + + public function getGenderList() + { + return ['1' => __('Male'), '0' => __('Female')]; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getPrevtimeTextAttr($value, $data) + { + $value = $value ? $value : $data['prevtime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getLogintimeTextAttr($value, $data) + { + $value = $value ? $value : $data['logintime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getJointimeTextAttr($value, $data) + { + $value = $value ? $value : $data['jointime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setPrevtimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setLogintimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setJointimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setBirthdayAttr($value) + { + return $value ? $value : null; + } + + public function group() + { + return $this->belongsTo('UserGroup', 'group_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + +} diff --git a/application/common/model/WechatConfig.php b/application/admin/model/UserGroup.php similarity index 39% rename from application/common/model/WechatConfig.php rename to application/admin/model/UserGroup.php index b02bb431c3b527fdbaeca604e9892ef58b5fd1d8..8ce26bc63a5607bf364f338d461465c5b1122e84 100644 --- a/application/common/model/WechatConfig.php +++ b/application/admin/model/UserGroup.php @@ -1,32 +1,34 @@ - $name]); - return $item ? $item->value : ''; - } - -} + __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/UserRule.php b/application/admin/model/UserRule.php new file mode 100644 index 0000000000000000000000000000000000000000..51391c1d1c8f1fdb0b18e908d5dbac01e3981827 --- /dev/null +++ b/application/admin/model/UserRule.php @@ -0,0 +1,62 @@ +getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public static function getTreeList($selected = []) + { + $ruleList = collection(self::where('status', 'normal')->order('weigh desc,id desc')->select())->toArray(); + $nodeList = []; + Tree::instance()->init($ruleList); + $ruleList = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'name'); + $hasChildrens = []; + foreach ($ruleList as $k => $v) + { + if ($v['haschild']) + $hasChildrens[] = $v['id']; + } + foreach ($ruleList as $k => $v) { + $state = array('selected' => in_array($v['id'], $selected) && !in_array($v['id'], $hasChildrens)); + $nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state); + } + return $nodeList; + } + +} diff --git a/application/admin/tags.php b/application/admin/tags.php index 57d55b8cfbed3483f50afab96b1726ad21dd4555..4e901f3a17e0f305a365a14fc5907cfdcb32165e 100644 --- a/application/admin/tags.php +++ b/application/admin/tags.php @@ -1,18 +1,18 @@ - -// +---------------------------------------------------------------------- -// 应用行为扩展定义文件 -return [ - // 应用结束 - 'app_end' => [ - 'app\\admin\\behavior\\AdminLog', - ], -]; + +// +---------------------------------------------------------------------- +// 应用行为扩展定义文件 +return [ + // 应用结束 + 'app_end' => [ + 'app\\admin\\behavior\\AdminLog', + ], +]; diff --git a/application/admin/validate/Admin.php b/application/admin/validate/Admin.php new file mode 100644 index 0000000000000000000000000000000000000000..5b4e4e8617f7b2edba1834b9a1b9b7aa8ea1cad5 --- /dev/null +++ b/application/admin/validate/Admin.php @@ -0,0 +1,55 @@ + 'require|regex:\w{3,12}|unique:admin', + 'nickname' => 'require', + 'password' => 'require|regex:\S{32}', + 'email' => 'require|email|unique:admin,email', + ]; + + /** + * 提示消息 + */ + protected $message = [ + ]; + + /** + * 字段描述 + */ + protected $field = [ + ]; + + /** + * 验证场景 + */ + protected $scene = [ + 'add' => ['username', 'email', 'nickname', 'password'], + 'edit' => ['username', 'email', 'nickname', 'password'], + ]; + + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->field = [ + 'username' => __('Username'), + 'nickname' => __('Nickname'), + 'password' => __('Password'), + 'email' => __('Email'), + ]; + $this->message = array_merge($this->message, [ + 'username.regex' => __('Please input correct username'), + 'password.regex' => __('Please input correct password') + ]); + parent::__construct($rules, $message, $field); + } + +} diff --git a/application/admin/validate/AuthRule.php b/application/admin/validate/AuthRule.php new file mode 100644 index 0000000000000000000000000000000000000000..a1cb9e775c7fe91d2f0160895d16a734c9fbdf1b --- /dev/null +++ b/application/admin/validate/AuthRule.php @@ -0,0 +1,52 @@ + '[a-z0-9_\/]+']; + + /** + * 验证规则 + */ + protected $rule = [ + 'name' => 'require|format|unique:AuthRule', + 'title' => 'require', + ]; + + /** + * 提示消息 + */ + protected $message = [ + 'name.format' => 'URL规则只能是小写字母、数字、下划线和/组成' + ]; + + /** + * 字段描述 + */ + protected $field = [ + ]; + + /** + * 验证场景 + */ + protected $scene = [ + ]; + + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->field = [ + 'name' => __('Name'), + 'title' => __('Title'), + ]; + $this->message['name.format'] = __('Name only supports letters, numbers, underscore and slash'); + parent::__construct($rules, $message, $field); + } + +} diff --git a/application/admin/validate/Category.php b/application/admin/validate/Category.php index b3591edb92b96be21e830ece74ef5330360ad513..8b67fdc36bcd6ab1868d98b3f8b1f117cd1d1572 100644 --- a/application/admin/validate/Category.php +++ b/application/admin/validate/Category.php @@ -1,27 +1,27 @@ - [], - 'edit' => [], - ]; - -} + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/User.php b/application/admin/validate/User.php new file mode 100644 index 0000000000000000000000000000000000000000..ab0622055382f8be139babe25489ee3604b32919 --- /dev/null +++ b/application/admin/validate/User.php @@ -0,0 +1,50 @@ + 'require|regex:\w{3,32}|unique:user', + 'nickname' => 'require|unique:user', + 'password' => 'regex:\S{6,32}', + 'email' => 'require|email|unique:user', + 'mobile' => 'unique:user' + ]; + + /** + * 字段描述 + */ + protected $field = [ + ]; + /** + * 提示消息 + */ + protected $message = [ + ]; + /** + * 验证场景 + */ + protected $scene = [ + 'add' => [], + 'edit' => ['username', 'email', 'nickname', 'password', 'email', 'mobile'], + ]; + + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->field = [ + 'username' => __('Username'), + 'nickname' => __('Nickname'), + 'password' => __('Password'), + 'email' => __('Email'), + 'mobile' => __('Mobile') + ]; + parent::__construct($rules, $message, $field); + } + +} diff --git a/application/admin/validate/UserGroup.php b/application/admin/validate/UserGroup.php new file mode 100644 index 0000000000000000000000000000000000000000..b7fa1ee37f9a6e7a43e5045e9be6b3b7a078be0e --- /dev/null +++ b/application/admin/validate/UserGroup.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/UserRule.php b/application/admin/validate/UserRule.php new file mode 100644 index 0000000000000000000000000000000000000000..2a2475bfaa21a90d44b921d64e0b30f4487f75cc --- /dev/null +++ b/application/admin/validate/UserRule.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/view/add.html b/application/admin/view/add.html deleted file mode 100644 index 746d0090a58da534d8b3975de4750939a7eb3bb9..0000000000000000000000000000000000000000 --- a/application/admin/view/add.html +++ /dev/null @@ -1,203 +0,0 @@ -
      - -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      - - - -
      -
      -
      - -
      - - - -
      -
      -
      - -
      - - {foreach name="genderdataList" item="vo"} - - {/foreach} - -
      -
      -
      - -
      - - {foreach name="hobbydataList" item="vo"} - - {/foreach} - -
      -
      -
      - -
      - -
      -
      -
      - -
      - -
      -
      -
      - -
      -
      - - - -
        -
        -
        -
        -
        - -
        -
        - - - -
          -
          -
          -
          -
          - -
          -
          - - - - -
          -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - -
          -
          -
          - -
          - - {foreach name="statusList" item="vo"} - - {/foreach} - -
          -
          -
          - -
          - - {foreach name="stateList" item="vo"} - - {/foreach} - -
          -
          - -
          diff --git a/extend/fast/ucenter/client/uc_client/data/cache/index.htm b/application/admin/view/addon/add.html similarity index 100% rename from extend/fast/ucenter/client/uc_client/data/cache/index.htm rename to application/admin/view/addon/add.html diff --git a/application/admin/view/addon/config.html b/application/admin/view/addon/config.html new file mode 100644 index 0000000000000000000000000000000000000000..ebec7b2a17e7aaf831cbdd3b4902150ec7c35eca --- /dev/null +++ b/application/admin/view/addon/config.html @@ -0,0 +1,108 @@ +
          + {if $addon.tips} +
          + {$addon.tips.title}
          + {$addon.tips.value} +
          + {/if} + + + + + + + + + {foreach $addon.config as $item} + + + + + {/foreach} + +
          {:__('Title')}{:__('Value')}
          {$item.title} +
          +
          + {switch $item.type} + {case string} + + {/case} + {case text} + + {/case} + {case array} +
          +
          + {:__('Array key')} + {:__('Array value')} +
          +
          {:__('Append')}
          + +
          + {/case} + {case date} + + {/case} + {case time} + + {/case} + {case datetime} + + {/case} + {case number} + + {/case} + {case checkbox} + {foreach name="item.content" item="vo"} + + {/foreach} + {/case} + {case radio} + {foreach name="item.content" item="vo"} + + {/foreach} + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
          + + + +
            +
            + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
            + + + +
            + {/case} + {case bool} + + + {/case} + {default /}{$item.value} + {/switch} +
            +
            +
            + +
            + +
            diff --git a/application/admin/view/addon/index.html b/application/admin/view/addon/index.html new file mode 100644 index 0000000000000000000000000000000000000000..770fdc2a481ba0c97b37cd4cc0f1b7efa5302177 --- /dev/null +++ b/application/admin/view/addon/index.html @@ -0,0 +1,314 @@ + +
            +
            + {:build_heading(null,FALSE)} + {if $Think.config.fastadmin.api_url} + + {/if} +
            + +
            +
            +
            +
            +
            + + {if $Think.config.fastadmin.api_url} + + + {:__('Userinfo')} + {/if} +
            + + +
            + +
            +
            + +
            +
            +
            + + + + + + + + + diff --git a/application/admin/view/auth/admin/add.html b/application/admin/view/auth/admin/add.html index de7399653dd54a415f68ad8c5c9f2dac8b3add14..6f984d288b8271548c127b7ecc6e896e9f8ec594 100644 --- a/application/admin/view/auth/admin/add.html +++ b/application/admin/view/auth/admin/add.html @@ -1,8 +1,9 @@
            + {:token()}
            - +
            - {:build_select('group[]', $groupdata, null, ['class'=>'form-control selectpicker', 'multiple'=>'', 'required'=>''])} + {:build_select('group[]', $groupdata, null, ['class'=>'form-control selectpicker', 'multiple'=>'', 'data-rule'=>'required'])}
            @@ -20,17 +21,17 @@
            - +
            - +
            - +
            {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
            diff --git a/application/admin/view/auth/admin/edit.html b/application/admin/view/auth/admin/edit.html index 0c15fa69fb7abd6a1d58f83437093069f6ac7e0e..64131d3ecc47036c041ea15c830fe107d0fe38af 100644 --- a/application/admin/view/auth/admin/edit.html +++ b/application/admin/view/auth/admin/edit.html @@ -1,36 +1,43 @@ + {:token()}
            - +
            - {:build_select('group[]', $groupdata, $groupids, ['class'=>'form-control selectpicker', 'multiple'=>''])} + {:build_select('group[]', $groupdata, $groupids, ['class'=>'form-control selectpicker', 'multiple'=>'', 'data-rule'=>'required'])}
            - +
            - +
            - +
            - +
            - + +
            + +
            +
            +
            +
            {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
            diff --git a/application/admin/view/auth/admin/index.html b/application/admin/view/auth/admin/index.html index a44c5627620f6de12a312dba59724b4f13e621cf..b4ed25659005f52bd521c252ee8893740d5c68c8 100644 --- a/application/admin/view/auth/admin/index.html +++ b/application/admin/view/auth/admin/index.html @@ -6,13 +6,13 @@
            - {:build_toolbar()} + {:build_toolbar('refresh,add,delete')}
            - - +
            - -
            diff --git a/application/admin/view/auth/adminlog/detail.html b/application/admin/view/auth/adminlog/detail.html index 83d51208fae821b217b90d8c02c0489f7142523a..49722a54d4c23fab7ff29594f1b47b857dd3036c 100644 --- a/application/admin/view/auth/adminlog/detail.html +++ b/application/admin/view/auth/adminlog/detail.html @@ -1,17 +1,22 @@ - + +
            - - - - + + + + - {volist name="row" id="vo" } - - - - - {/volist} + {volist name="row" id="vo" } + + + + + {/volist}
            标题内容
            {:__('Title')}{:__('Content')}
            {$key}{$vo}
            {:__($key)}{if $key=='createtime'}{$vo|datetime}{else/}{$vo|htmlentities}{/if}
            \ No newline at end of file +
            diff --git a/application/admin/view/auth/adminlog/index.html b/application/admin/view/auth/adminlog/index.html index 8d61a3a5c525720524586a65f87cc2355c143ddf..e0a711f65822d24260b9bac05f4b483821c9017c 100644 --- a/application/admin/view/auth/adminlog/index.html +++ b/application/admin/view/auth/adminlog/index.html @@ -8,11 +8,11 @@
            {:build_toolbar('refresh,delete')}
            - - +
            - -
            diff --git a/application/admin/view/auth/group/add.html b/application/admin/view/auth/group/add.html index 5e2aa8b28311c1b3c0ad2ea7be4b308e42e36b9f..0eb848b546226576f523b0fd7a163cbc21389408 100644 --- a/application/admin/view/auth/group/add.html +++ b/application/admin/view/auth/group/add.html @@ -1,19 +1,20 @@ - + + {:token()}
            - +
            - {:build_select('row[pid]', $groupdata, null, ['class'=>'form-control selectpicker', 'required'=>''])} + {:build_select('row[pid]', $groupdata, null, ['class'=>'form-control selectpicker', 'data-rule'=>'required'])}
            - +
            - +
            @@ -22,7 +23,7 @@
            - +
            {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
            diff --git a/application/admin/view/auth/group/edit.html b/application/admin/view/auth/group/edit.html index e7b433b2a5fa8d0113049ec9206370b607e8d3aa..c691bd16005b80365366c278722b5bff99d4d3e3 100644 --- a/application/admin/view/auth/group/edit.html +++ b/application/admin/view/auth/group/edit.html @@ -1,19 +1,20 @@ - + + {:token()}
            - +
            - {:build_select('row[pid]', $groupdata, $row['pid'], ['class'=>'form-control selectpicker', 'required'=>'', 'data-id'=>$row['id'], 'data-pid'=>$row['pid']])} + {:build_select('row[pid]', $groupdata, $row['pid'], ['class'=>'form-control selectpicker', 'data-rule'=>'required', 'data-id'=>$row['id'], 'data-pid'=>$row['pid']])}
            - +
            - +
            - +
            @@ -22,7 +23,7 @@
            - +
            {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
            diff --git a/application/admin/view/auth/group/index.html b/application/admin/view/auth/group/index.html index 8832095f60fafe7099769cdc66e8011eb047ebb4..9ad38482158f19401debcf0e0ebca9f43aab7ae5 100644 --- a/application/admin/view/auth/group/index.html +++ b/application/admin/view/auth/group/index.html @@ -6,10 +6,12 @@
            - {:build_toolbar()} + {:build_toolbar('refresh,add,delete')}
            - - +
            diff --git a/application/admin/view/auth/rule/add.html b/application/admin/view/auth/rule/add.html index f6539d027a602dc541c1666bb24ff8c76c8cf341..4a9d7373be023fc49eafb8157fac7a9f66f33101 100644 --- a/application/admin/view/auth/rule/add.html +++ b/application/admin/view/auth/rule/add.html @@ -1,28 +1,25 @@ -
            -

            {:__('Alert')}!

            - {:__('If not necessary, use the command line to build rule')} -
            + {:token()}
            - +
            {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])}
            - +
            - {:build_select('row[pid]', $ruledata, null, ['class'=>'form-control selectpicker', 'required'=>''])} + {:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])}
            - +
            - +
            @@ -31,8 +28,8 @@
            @@ -55,7 +52,7 @@
            - +
            {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
            diff --git a/application/admin/view/auth/rule/edit.html b/application/admin/view/auth/rule/edit.html index e4accf0887337c0553a15f726812076fb276d7ec..ba12a20cefb6786777714a582f08a4fcadd2d381 100644 --- a/application/admin/view/auth/rule/edit.html +++ b/application/admin/view/auth/rule/edit.html @@ -1,26 +1,27 @@ + {:token()}
            - +
            {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])}
            - +
            - {:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control selectpicker', 'required'=>''])} + {:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])}
            - +
            - +
            - +
            @@ -28,7 +29,7 @@
            @@ -41,17 +42,17 @@
            - +
            - +
            - +
            {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
            diff --git a/application/admin/view/auth/rule/index.html b/application/admin/view/auth/rule/index.html index 0d6331f44a4ac6276d85553136c42832c2bebd20..be73a56317ab3ef87eddb0a8e2683bd13dd32056 100644 --- a/application/admin/view/auth/rule/index.html +++ b/application/admin/view/auth/rule/index.html @@ -1,3 +1,6 @@ +
            {:build_heading()} @@ -6,11 +9,23 @@ diff --git a/application/admin/view/auth/rule/tpl.html b/application/admin/view/auth/rule/tpl.html index 4e34afb28b3d7a7cc7cb960aafbcde97d3fc5317..9413dde109efcecbe86280cdd1e5c39f6a4e316a 100644 --- a/application/admin/view/auth/rule/tpl.html +++ b/application/admin/view/auth/rule/tpl.html @@ -6,12 +6,13 @@ margin:5px 0 0 0; } #chooseicon ul li{ - width:30px;height:30px; - line-height:30px; - border:1px solid #ddd; + width:41px;height:42px; + line-height:42px; + border:1px solid #efefef; padding:1px; margin:1px; text-align: center; + font-size:18px; } #chooseicon ul li:hover{ border:1px solid #2c3e50; @@ -23,7 +24,7 @@
            -
            搜索图标
            +
            {:__('Search icon')}
            @@ -31,7 +32,7 @@
              <% for(var i=0; i -
            • +
            • <% } %> diff --git a/application/admin/view/category/add.html b/application/admin/view/category/add.html index 7ab24afab24b0c0f297566e3ee1624cdfe301434..5ca1ae074340284b78a8c5a27037ca20cea615a8 100644 --- a/application/admin/view/category/add.html +++ b/application/admin/view/category/add.html @@ -1,12 +1,16 @@
              + {:token()} +
              + {:__('Category warmtips')} +
              - - {foreach name="typeList" item="vo"} - + {/foreach} @@ -15,10 +19,10 @@
              - - {foreach name="parentList" item="vo"} - + {/foreach} @@ -39,10 +43,10 @@
              - + @@ -51,12 +55,15 @@
              -
              - - - -
                +
                + +
                + + +
                +
                +
                  @@ -78,7 +85,7 @@
                  - +
                  {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
                  diff --git a/application/admin/view/category/edit.html b/application/admin/view/category/edit.html index eb9403e152d669e096c2af12d1b59d1e9d3f64bf..7a554f36477e02250663673deb31332d7f7c9b29 100644 --- a/application/admin/view/category/edit.html +++ b/application/admin/view/category/edit.html @@ -1,12 +1,12 @@ - + {:token()}
                  - - {foreach name="typeList" item="vo"} - + {/foreach} @@ -15,10 +15,10 @@
                  - - {foreach name="parentList" item="vo"} - + {/foreach} @@ -27,22 +27,22 @@
                  - +
                  - +
                  - + @@ -51,24 +51,27 @@
                  -
                  - - - -
                    +
                    + +
                    + + +
                    +
                    +
                      - +
                      - +
                      @@ -78,7 +81,7 @@
                      - +
                      {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
                      diff --git a/application/admin/view/category/index.html b/application/admin/view/category/index.html index d3eefa4ffe993931e71fc62172907e92c38b5b9b..d69a291be4978fe586d44bfa0be218e457e758ed 100644 --- a/application/admin/view/category/index.html +++ b/application/admin/view/category/index.html @@ -1,13 +1,21 @@
                      - {:build_heading()} +
                      + {:build_heading(null,FALSE)} + +
                      - {:build_toolbar()} - - +
                      diff --git a/application/admin/view/common/control.html b/application/admin/view/common/control.html index 68d41ffdb24a4cc6f8d1b35b77fc87c2c0ad9f6b..77efe7c088ae1842a528b28f335060dc0ab9c1f7 100644 --- a/application/admin/view/common/control.html +++ b/application/admin/view/common/control.html @@ -5,6 +5,10 @@ .skin-list li a{ display: block; box-shadow: 0 0 3px rgba(0,0,0,0.4); } + .skin-list li a span{ + display: block; + float:left; + }
                      + + - -
                      Stats Tab Content
                      -
                      - -

                      General Settings

                      - - - -
                      - - -

                      - Other sets of options are available -

                      -
                      - - -
                      - - -

                      - Allow the user to show his name in blog posts -

                      -
                      - - - - +

                      {:__('Setting')}

                      - \ No newline at end of file + diff --git a/application/admin/view/common/header.html b/application/admin/view/common/header.html old mode 100644 new mode 100755 index da3e9dd861e61c0d2b895741408b95261550961a..2835a8074952e253792d4c6b59256ef892c4a6ca --- a/application/admin/view/common/header.html +++ b/application/admin/view/common/header.html @@ -1,130 +1,115 @@ + \ No newline at end of file + {/if} + diff --git a/application/admin/view/common/menu.html b/application/admin/view/common/menu.html index 5874fb10286abd7292b95e1c906d0c1d8163664b..3e0ff5c39bace196fd5cca4edfc6ab0a9a301094 100644 --- a/application/admin/view/common/menu.html +++ b/application/admin/view/common/menu.html @@ -1,17 +1,17 @@ - +
                      - - - -