mirror of https://gitee.com/karson/fastadmin.git
Pre Merge pull request !135 from sinfree/master
commit
b4aab35616
8
.bowerrc
8
.bowerrc
|
|
@ -1,3 +1,9 @@
|
|||
{
|
||||
"directory" : "public/assets/libs"
|
||||
"directory" : "public/assets/libs",
|
||||
"ignoredDependencies": [
|
||||
"file-saver",
|
||||
"html2canvas",
|
||||
"jspdf",
|
||||
"jspdf-autotable"
|
||||
]
|
||||
}
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
|
|||
42
.travis.yml
42
.travis.yml
|
|
@ -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
|
||||
380
LICENSE
380
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.
|
||||
50
README.md
50
README.md
|
|
@ -1,5 +1,4 @@
|
|||
FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。
|
||||
===============
|
||||
|
||||
|
||||
## **主要特性**
|
||||
|
|
@ -7,52 +6,61 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。
|
|||
* 基于`Auth`验证的权限管理系统
|
||||
* 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
|
||||
* 支持单管理员多角色
|
||||
* 支持目录和控制器结构一键生成权限节点
|
||||
* 支持管理子级数据或个人数据
|
||||
* 强大的一键生成功能
|
||||
* 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等
|
||||
* 一键压缩打包JS和CSS文件,一键CDN静态资源部署
|
||||
* 一键生成控制器菜单和规则
|
||||
* 一键生成API接口文档
|
||||
* 完善的前端功能组件开发
|
||||
* 基于`AdminLTE`二次开发
|
||||
* 基于`Bootstrap`开发,自适应手机、平板、PC
|
||||
* 基于`RequireJS`进行JS模块管理,按需加载
|
||||
* 基于`Less`进行样式开发
|
||||
* 基于`Bower`进行前端组件包管理
|
||||
* 数据库表一键生成`CRUD`,包括控制器、模型、视图、JS、语言包
|
||||
* 一键压缩打包JS和CSS文件
|
||||
* 强大的插件扩展功能,在线安装卸载升级插件
|
||||
* 通用的会员模块和API模块
|
||||
* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
|
||||
* 二级域名部署支持,同时域名支持绑定到插件
|
||||
* 多语言支持,服务端及客户端支持
|
||||
* 无缝整合又拍云上传功能
|
||||
* 强大的第三方模块支持([CMS](https://www.fastadmin.net/store/cms.html)、[博客](https://www.fastadmin.net/store/blog.html)、[知识付费问答](https://www.fastadmin.net/store/ask.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
|
||||
|
||||
提 示:演示站数据无法进行删除和修改,只能新增,完整体验请下载源码安装体验
|
||||
提 示:演示站数据无法进行修改,请下载源码安装体验全部功能
|
||||
|
||||
## **界面截图**
|
||||

|
||||

|
||||
|
||||
## **问题反馈**
|
||||
|
||||
在使用中有任何问题,请使用以下联系方式联系我们
|
||||
|
||||
交流社区: http://forum.fastadmin.net
|
||||
交流社区: https://forum.fastadmin.net
|
||||
|
||||
QQ群: [636393962](https://jq.qq.com/?_wv=1027&k=487PNBb)
|
||||
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群)
|
||||
|
||||
Email: (karsonzhang#163.com, 把#换成@)
|
||||
|
||||
weibo: [@karsonzhang](https://weibo.com/karsonzhang)
|
||||
|
||||
Github: https://github.com/karsonzhang/fastadmin
|
||||
|
||||
Git@OSC: https://git.oschina.net/karson/fastadmin
|
||||
Gitee: https://gitee.com/karson/fastadmin
|
||||
|
||||
## **特别鸣谢**
|
||||
|
||||
|
|
@ -60,19 +68,25 @@ Git@OSC: https://git.oschina.net/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
|
||||
|
||||
|
||||
## **版权信息**
|
||||
|
||||
FastAdmin遵循Apache2开源协议发布,并提供免费使用。
|
||||
|
||||
本项目包含的第三方源码和二进制文件之版权信息另行标注。
|
||||
|
||||
版权所有Copyright © 2017-2018 by FastAdmin (http://www.fastadmin.net)
|
||||
版权所有Copyright © 2017-2019 by FastAdmin (https://www.fastadmin.net)
|
||||
|
||||
All rights reserved。
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -2,17 +2,12 @@
|
|||
|
||||
namespace app\admin\behavior;
|
||||
|
||||
use think\Config;
|
||||
|
||||
class AdminLog
|
||||
{
|
||||
|
||||
public function run(&$params)
|
||||
{
|
||||
if (request()->isPost())
|
||||
{
|
||||
if (request()->isPost()) {
|
||||
\app\admin\model\AdminLog::record();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,322 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use think\addons\AddonException;
|
||||
use think\addons\Service;
|
||||
use think\Config;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\Db;
|
||||
use think\Exception;
|
||||
use think\exception\PDOException;
|
||||
|
||||
class Addon extends Command
|
||||
{
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->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)', '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/') !== false) {
|
||||
$name = explode('/', $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'])) {
|
||||
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, "\t") . ";\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;
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace addons\{%name%};
|
||||
|
||||
use app\common\library\Menu;
|
||||
use think\Addons;
|
||||
|
||||
/**
|
||||
* 插件
|
||||
*/
|
||||
class {%addonClassName%} extends Addons
|
||||
{
|
||||
|
||||
/**
|
||||
* 插件安装方法
|
||||
* @return bool
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
{%addonInstallMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件卸载方法
|
||||
* @return bool
|
||||
*/
|
||||
public function uninstall()
|
||||
{
|
||||
{%addonUninstallMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件启用方法
|
||||
* @return bool
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
{%addonEnableMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件禁用方法
|
||||
* @return bool
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
{%addonDisableMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现钩子方法
|
||||
* @return mixed
|
||||
*/
|
||||
public function testhook($param)
|
||||
{
|
||||
// 调用钩子时候的参数信息
|
||||
print_r($param);
|
||||
// 当前插件的配置信息,配置信息存在当前目录的config.php文件中,见下方
|
||||
print_r($this->getConfig());
|
||||
// 可以返回模板,模板文件默认读取的为插件目录中的文件。模板名不能为空!
|
||||
//return $this->fetch('view/info');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
//配置唯一标识
|
||||
'name' => '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' => ''
|
||||
],
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace addons\{%addon%}\controller;
|
||||
|
||||
use think\addons\Controller;
|
||||
|
||||
class Index extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->error("当前插件暂无前台页面");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
name = {%name%}
|
||||
title = 插件名称{%name%}
|
||||
intro = FastAdmin插件
|
||||
author = yourname
|
||||
website = https://www.fastadmin.net
|
||||
version = 1.0.0
|
||||
state = 1
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use app\admin\command\Api\library\Builder;
|
||||
use think\Config;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\Exception;
|
||||
|
||||
class Api extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$site = Config::get('site');
|
||||
$this
|
||||
->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 = [
|
||||
'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
|
||||
* @author JBYRNE http://jarretbyrne.com/2015/06/197/
|
||||
* @return string
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Info' => '基础信息',
|
||||
'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' => '响应输出',
|
||||
];
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\command\Api\library;
|
||||
|
||||
use think\Config;
|
||||
|
||||
/**
|
||||
* @website https://github.com/calinrada/php-apidoc
|
||||
* @author Calin Rada <rada.calin@gmail.com>
|
||||
* @author Karson <karsonzhang@163.com>
|
||||
*/
|
||||
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][$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);
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\command\Api\library;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class imported from https://github.com/eriknyk/Annotations
|
||||
* @author Erik Amaru Ortiz https://github.com/eriknyk
|
||||
*
|
||||
* @license http://opensource.org/licenses/bsd-license.php The BSD License
|
||||
* @author Calin Rada <rada.calin@gmail.com>
|
||||
*/
|
||||
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('/@(?<name>[A-Za-z_-]+)[\s\t]*\((?<args>(?:(?!\)).)*)\)\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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,582 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{$config.language}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="{$config.author}">
|
||||
<title>{$config.title}</title>
|
||||
|
||||
<!-- Bootstrap Core CSS -->
|
||||
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Plugin CSS -->
|
||||
<link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 70px; margin-bottom: 15px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: "Roboto", "SF Pro SC", "SF Pro Display", "SF Pro Icons", "PingFang SC", BlinkMacSystemFont, -apple-system, "Segoe UI", "Microsoft Yahei", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
h2 { font-size: 1.6em; }
|
||||
hr { margin-top: 10px; }
|
||||
.tab-pane { padding-top: 10px; }
|
||||
.mt0 { margin-top: 0px; }
|
||||
.footer { font-size: 12px; color: #666; }
|
||||
.label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; }
|
||||
.string { color: green; }
|
||||
.number { color: darkorange; }
|
||||
.boolean { color: blue; }
|
||||
.null { color: magenta; }
|
||||
.key { color: red; }
|
||||
.popover { max-width: 400px; max-height: 400px; overflow-y: auto;}
|
||||
.list-group.panel > .list-group-item {
|
||||
}
|
||||
.list-group-item:last-child {
|
||||
border-radius:0;
|
||||
}
|
||||
h4.panel-title a {
|
||||
font-weight:normal;
|
||||
font-size:14px;
|
||||
}
|
||||
h4.panel-title a .text-muted {
|
||||
font-size:12px;
|
||||
font-weight:normal;
|
||||
font-family: 'Verdana';
|
||||
}
|
||||
#sidebar {
|
||||
width: 220px;
|
||||
position: fixed;
|
||||
margin-left: -240px;
|
||||
overflow-y:auto;
|
||||
}
|
||||
#sidebar > .list-group {
|
||||
margin-bottom:0;
|
||||
}
|
||||
#sidebar > .list-group > a{
|
||||
text-indent:0;
|
||||
}
|
||||
#sidebar .child {
|
||||
border:1px solid #ddd;
|
||||
border-bottom:none;
|
||||
}
|
||||
#sidebar .child > a {
|
||||
border:0;
|
||||
}
|
||||
#sidebar .list-group a.current {
|
||||
background:#f5f5f5;
|
||||
}
|
||||
@media (max-width: 1620px){
|
||||
#sidebar {
|
||||
margin:0;
|
||||
}
|
||||
#accordion {
|
||||
padding-left:235px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px){
|
||||
#sidebar {
|
||||
display: none;
|
||||
}
|
||||
#accordion {
|
||||
padding-left:0px;
|
||||
}
|
||||
}
|
||||
.label-primary {
|
||||
background-color: #248aff;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Fixed navbar -->
|
||||
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="https://www.fastadmin.net" target="_blank">{$config.title}</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<form class="navbar-form navbar-right">
|
||||
<div class="form-group">
|
||||
Token:
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Tokentips}" placeholder="token" id="token" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
Apiurl:
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input id="apiUrl" type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Apiurltips}" placeholder="https://api.mydomain.com" value="{$config.apiurl}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-success btn-sm" data-toggle="tooltip" title="{$lang.Savetips}" id="save_data">
|
||||
<span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- menu -->
|
||||
<div id="sidebar">
|
||||
<div class="list-group panel">
|
||||
{foreach name="docslist" id="docs"}
|
||||
<a href="#{$key}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a>
|
||||
<div class="child collapse" id="{$key}">
|
||||
{foreach name="docs" id="api" }
|
||||
<a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title}</a>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-group" id="accordion">
|
||||
{foreach name="docslist" id="docs"}
|
||||
<h2>{$key}</h2>
|
||||
<hr>
|
||||
{foreach name="docs" id="api" }
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" id="heading-{$api.id}">
|
||||
<h4 class="panel-title">
|
||||
<span class="label {$api.method_label}">{$api.method|strtoupper}</span>
|
||||
<a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseOne{$api.id}" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" id="doctab{$api.id}">
|
||||
<li class="active"><a href="#info{$api.id}" data-toggle="tab">{$lang.Info}</a></li>
|
||||
<li><a href="#sandbox{$api.id}" data-toggle="tab">{$lang.Sandbox}</a></li>
|
||||
<li><a href="#sample{$api.id}" data-toggle="tab">{$lang.Sampleoutput}</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane active" id="info{$api.id}">
|
||||
<div class="well">
|
||||
{$api.summary}
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.Headers}</strong></div>
|
||||
<div class="panel-body">
|
||||
{if $api.headerslist}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{$lang.Name}</th>
|
||||
<th>{$lang.Type}</th>
|
||||
<th>{$lang.Required}</th>
|
||||
<th>{$lang.Description}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach name="api['headerslist']" id="header"}
|
||||
<tr>
|
||||
<td>{$header.name}</td>
|
||||
<td>{$header.type}</td>
|
||||
<td>{$header.required?'是':'否'}</td>
|
||||
<td>{$header.description}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{else /}
|
||||
无
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
|
||||
<div class="panel-body">
|
||||
{if $api.paramslist}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{$lang.Name}</th>
|
||||
<th>{$lang.Type}</th>
|
||||
<th>{$lang.Required}</th>
|
||||
<th>{$lang.Description}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach name="api['paramslist']" id="param"}
|
||||
<tr>
|
||||
<td>{$param.name}</td>
|
||||
<td>{$param.type}</td>
|
||||
<td>{:$param.required?'是':'否'}</td>
|
||||
<td>{$param.description}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{else /}
|
||||
无
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.Body}</strong></div>
|
||||
<div class="panel-body">
|
||||
{$api.body|default='无'}
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- #info -->
|
||||
|
||||
<div class="tab-pane" id="sandbox{$api.id}">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{if $api.headerslist}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.Headers}</strong></div>
|
||||
<div class="panel-body">
|
||||
<div class="headers">
|
||||
{foreach name="api['headerslist']" id="param"}
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="{$param.name}">{$param.name}</label>
|
||||
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}">
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
|
||||
<div class="panel-body">
|
||||
<form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}">
|
||||
{if $api.paramslist}
|
||||
{foreach name="api['paramslist']" id="param"}
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="{$param.name}">{$param.name}</label>
|
||||
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}">
|
||||
</div>
|
||||
{/foreach}
|
||||
{else /}
|
||||
<div class="form-group">
|
||||
无
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button>
|
||||
<button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.Response}</strong></div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="overflow-x:auto">
|
||||
<pre id="response_headers{$api.id}"></pre>
|
||||
<pre id="response{$api.id}"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div>
|
||||
<div class="panel-body">
|
||||
{if $api.returnparamslist}
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{$lang.Name}</th>
|
||||
<th>{$lang.Type}</th>
|
||||
<th>{$lang.Description}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach name="api['returnparamslist']" id="param"}
|
||||
<tr>
|
||||
<td>{$param.name}</td>
|
||||
<td>{$param.type}</td>
|
||||
<td>{$param.description}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{else /}
|
||||
无
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- #sandbox -->
|
||||
|
||||
<div class="tab-pane" id="sample{$api.id}">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<pre id="sample_response{$api.id}">{$api.return|default='无'}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- #sample -->
|
||||
|
||||
</div><!-- .tab-content -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
{/foreach}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row mt0 footer">
|
||||
<div class="col-md-6" align="left">
|
||||
Generated on {:date('Y-m-d H:i:s')}
|
||||
</div>
|
||||
<div class="col-md-6" align="right">
|
||||
<a href="https://www.fastadmin.net" target="_blank">FastAdmin</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
|
||||
|
||||
<!-- Bootstrap Core JavaScript -->
|
||||
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
function syntaxHighlight(json) {
|
||||
if (typeof json != 'string') {
|
||||
json = JSON.stringify(json, undefined, 2);
|
||||
}
|
||||
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
|
||||
var cls = 'number';
|
||||
if (/^"/.test(match)) {
|
||||
if (/:$/.test(match)) {
|
||||
cls = 'key';
|
||||
} else {
|
||||
cls = 'string';
|
||||
}
|
||||
} else if (/true|false/.test(match)) {
|
||||
cls = 'boolean';
|
||||
} else if (/null/.test(match)) {
|
||||
cls = 'null';
|
||||
}
|
||||
return '<span class="' + cls + '">' + match + '</span>';
|
||||
});
|
||||
}
|
||||
|
||||
function prepareStr(str) {
|
||||
try {
|
||||
return syntaxHighlight(JSON.stringify(JSON.parse(str.replace(/'/g, '"')), null, 2));
|
||||
} catch (e) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
var storage = (function () {
|
||||
var uid = new Date;
|
||||
var storage;
|
||||
var result;
|
||||
try {
|
||||
(storage = window.localStorage).setItem(uid, uid);
|
||||
result = storage.getItem(uid) == uid;
|
||||
storage.removeItem(uid);
|
||||
return result && storage;
|
||||
} catch (exception) {
|
||||
}
|
||||
}());
|
||||
|
||||
$.fn.serializeObject = function ()
|
||||
{
|
||||
var o = {};
|
||||
var a = this.serializeArray();
|
||||
$.each(a, function () {
|
||||
if (!this.value) {
|
||||
return;
|
||||
}
|
||||
if (o[this.name] !== undefined) {
|
||||
if (!o[this.name].push) {
|
||||
o[this.name] = [o[this.name]];
|
||||
}
|
||||
o[this.name].push(this.value || '');
|
||||
} else {
|
||||
o[this.name] = this.value || '';
|
||||
}
|
||||
});
|
||||
return o;
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
if (storage) {
|
||||
storage.getItem('token') && $('#token').val(storage.getItem('token'));
|
||||
storage.getItem('apiUrl') && $('#apiUrl').val(storage.getItem('apiUrl'));
|
||||
}
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip({
|
||||
placement: 'bottom'
|
||||
});
|
||||
|
||||
$(window).on("resize", function(){
|
||||
$("#sidebar").css("max-height", $(window).height()-80);
|
||||
});
|
||||
|
||||
$(window).trigger("resize");
|
||||
|
||||
$(document).on("click", "#sidebar .list-group > .list-group-item", function(){
|
||||
$("#sidebar .list-group > .list-group-item").removeClass("current");
|
||||
$(this).addClass("current");
|
||||
});
|
||||
$(document).on("click", "#sidebar .child a", function(){
|
||||
var heading = $("#heading-"+$(this).data("id"));
|
||||
if(!heading.next().hasClass("in")){
|
||||
$("a", heading).trigger("click");
|
||||
}
|
||||
$("html,body").animate({scrollTop:heading.offset().top-70});
|
||||
});
|
||||
|
||||
$('code[id^=response]').hide();
|
||||
|
||||
$.each($('pre[id^=sample_response],pre[id^=sample_post_body]'), function () {
|
||||
if ($(this).html() == 'NA') {
|
||||
return;
|
||||
}
|
||||
var str = prepareStr($(this).html());
|
||||
$(this).html(str);
|
||||
});
|
||||
|
||||
$("[data-toggle=popover]").popover({placement: 'right'});
|
||||
|
||||
$('[data-toggle=popover]').on('shown.bs.popover', function () {
|
||||
var $sample = $(this).parent().find(".popover-content"),
|
||||
str = $(this).data('content');
|
||||
if (typeof str == "undefined" || str === "") {
|
||||
return;
|
||||
}
|
||||
var str = prepareStr(str);
|
||||
$sample.html('<pre>' + str + '</pre>');
|
||||
});
|
||||
|
||||
$('body').on('click', '#save_data', function (e) {
|
||||
if (storage) {
|
||||
storage.setItem('token', $('#token').val());
|
||||
storage.setItem('apiUrl', $('#apiUrl').val());
|
||||
} else {
|
||||
alert('Your browser does not support local storage');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('click', '.send', function (e) {
|
||||
e.preventDefault();
|
||||
var form = $(this).closest('form');
|
||||
//added /g to get all the matched params instead of only first
|
||||
var matchedParamsInRoute = $(form).attr('action').match(/[^{]+(?=\})/g);
|
||||
var theId = $(this).attr('rel');
|
||||
//keep a copy of action attribute in order to modify the copy
|
||||
//instead of the initial attribute
|
||||
var url = $(form).attr('action');
|
||||
var method = $(form).prop('method').toLowerCase() || 'get';
|
||||
|
||||
var formData = new FormData();
|
||||
|
||||
$(form).find('input').each(function (i, input) {
|
||||
if ($(input).attr('type').toLowerCase() == 'file') {
|
||||
formData.append($(input).attr('name'), $(input)[0].files[0]);
|
||||
method = 'post';
|
||||
} else {
|
||||
formData.append($(input).attr('name'), $(input).val())
|
||||
}
|
||||
});
|
||||
|
||||
var index, key, value;
|
||||
|
||||
if (matchedParamsInRoute) {
|
||||
var params = {};
|
||||
formData.forEach(function(value, key){
|
||||
params[key] = value;
|
||||
});
|
||||
for (index = 0; index < matchedParamsInRoute.length; ++index) {
|
||||
try {
|
||||
key = matchedParamsInRoute[index];
|
||||
value = params[key];
|
||||
if (typeof value == "undefined")
|
||||
value = "";
|
||||
url = url.replace("\{" + key + "\}", value);
|
||||
formData.delete(key);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var headers = {};
|
||||
|
||||
var token = $('#token').val();
|
||||
if (token.length > 0) {
|
||||
headers['token'] = token;
|
||||
}
|
||||
|
||||
$("#sandbox" + theId + " .headers input[type=text]").each(function () {
|
||||
val = $(this).val();
|
||||
if (val.length > 0) {
|
||||
headers[$(this).prop('name')] = val;
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: $('#apiUrl').val() + url,
|
||||
data: method == 'get' ? $(form).serialize() : formData,
|
||||
type: method,
|
||||
dataType: 'json',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
headers: headers,
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
success: function (data, textStatus, xhr) {
|
||||
if (typeof data === 'object') {
|
||||
var str = JSON.stringify(data, null, 2);
|
||||
$('#response' + theId).html(syntaxHighlight(str));
|
||||
} else {
|
||||
$('#response' + theId).html(data || '');
|
||||
}
|
||||
$('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
|
||||
$('#response' + theId).show();
|
||||
},
|
||||
error: function (xhr, textStatus, error) {
|
||||
try {
|
||||
var str = JSON.stringify($.parseJSON(xhr.responseText), null, 2);
|
||||
} catch (e) {
|
||||
var str = xhr.responseText;
|
||||
}
|
||||
$('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
|
||||
$('#response' + theId).html(syntaxHighlight(str));
|
||||
$('#response' + theId).show();
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,37 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace {%controllerNamespace%};
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
use think\Controller;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* {%tableComment%}
|
||||
*
|
||||
* @icon {%iconName%}
|
||||
*/
|
||||
class {%controllerName%} extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* {%modelName%}模型对象
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('{%modelName%}');
|
||||
{%controllerAssignList%}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个方法
|
||||
* 因此在当前控制器中可不用编写增删改查的代码,如果需要自己控制这部分逻辑
|
||||
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||
*/
|
||||
|
||||
{%controllerIndex%}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace {%controllerNamespace%};
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* {%tableComment%}
|
||||
*
|
||||
* @icon {%iconName%}
|
||||
*/
|
||||
class {%controllerName%} extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* {%modelName%}模型对象
|
||||
* @var \{%modelNamespace%}\{%modelName%}
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = new \{%modelNamespace%}\{%modelName%};
|
||||
{%controllerAssignList%}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||
*/
|
||||
|
||||
{%controllerIndex%}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,22 +7,33 @@
|
|||
//当前是否为关联查询
|
||||
$this->relationSearch = {%relationSearch%};
|
||||
//设置过滤方法
|
||||
$this->request->filter(['strip_tags', 'htmlspecialchars']);
|
||||
$this->request->filter(['strip_tags']);
|
||||
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%}
|
||||
{%relationWithList%}
|
||||
->where($where)
|
||||
->order($sort, $order)
|
||||
->count();
|
||||
|
||||
$list = $this->model
|
||||
{%relationWith%}
|
||||
{%relationWithList%}
|
||||
->where($where)
|
||||
->order($sort, $order)
|
||||
->limit($offset, $limit)
|
||||
->select();
|
||||
|
||||
foreach ($list as $row) {
|
||||
{%visibleFieldList%}
|
||||
{%relationVisibleFieldList%}
|
||||
}
|
||||
$list = collection($list)->toArray();
|
||||
$result = array("total" => $total, "rows" => $list);
|
||||
|
||||
return json($result);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
|
||||
<div class="checkbox">
|
||||
{foreach name="{%fieldList%}" item="vo"}
|
||||
<label for="{%fieldName%}-{$key}"><input id="{%fieldName%}-{$key}" name="{%fieldName%}" type="checkbox" value="{$key}" {in name="key" value="{%selectedValue%}"}checked{/in} /> {$vo}</label>
|
||||
{/foreach}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
<dl class="fieldlist" data-name="{%fieldName%}">
|
||||
<dd>
|
||||
<ins>{:__('{%itemKey%}')}</ins>
|
||||
<ins>{:__('{%itemValue%}')}</ins>
|
||||
</dd>
|
||||
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
|
||||
<textarea name="{%fieldName%}" class="form-control hide" cols="30" rows="5">{%fieldValue%}</textarea>
|
||||
</dl>
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
<div class="panel-heading">
|
||||
{:build_heading(null,FALSE)}
|
||||
<ul class="nav nav-tabs" data-field="{%field%}">
|
||||
<li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
|
||||
{foreach name="{%fieldName%}List" item="vo"}
|
||||
<li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
|
||||
<div class="radio">
|
||||
{foreach name="{%fieldList%}" item="vo"}
|
||||
<label for="{%fieldName%}-{$key}"><input id="{%fieldName%}-{$key}" name="{%fieldName%}" type="radio" value="{$key}" {in name="key" value="{%selectedValue%}"}checked{/in} /> {$vo}</label>
|
||||
{/foreach}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('{%controllerUrl%}/recyclebin')?'':'hide'}" href="{%controllerUrl%}/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
<input {%attrStr%} name="{%fieldName%}" type="hidden" value="{%fieldValue%}">
|
||||
<a href="javascript:;" data-toggle="switcher" class="btn-switcher" data-input-id="c-{%field%}" data-yes="{%fieldYes%}" data-no="{%fieldNo%}" >
|
||||
<i class="fa fa-toggle-on text-success {%fieldSwitchClass%} fa-2x"></i>
|
||||
</a>
|
||||
|
|
@ -1,21 +1,31 @@
|
|||
<div class="panel panel-default panel-intro">
|
||||
{:build_heading()}
|
||||
{%headingHtml%}
|
||||
|
||||
<div class="panel-body">
|
||||
<div id="myTabContent" class="tab-content">
|
||||
<div class="tab-pane fade active in" id="one">
|
||||
<div class="widget-body no-padding">
|
||||
<div id="toolbar" class="toolbar">
|
||||
{:build_toolbar()}
|
||||
<div class="dropdown btn-group">
|
||||
<a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
|
||||
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('{%controllerUrl%}/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
|
||||
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('{%controllerUrl%}/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
|
||||
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('{%controllerUrl%}/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
|
||||
<a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('{%controllerUrl%}/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
|
||||
|
||||
<div class="dropdown btn-group {:$auth->check('{%controllerUrl%}/multi')?'':'hide'}">
|
||||
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
|
||||
<ul class="dropdown-menu text-left" role="menu">
|
||||
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
|
||||
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{%recyclebinHtml%}
|
||||
</div>
|
||||
<table id="table" class="table table-striped table-bordered table-hover" width="100%">
|
||||
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
|
||||
data-operate-edit="{:$auth->check('{%controllerUrl%}/edit')}"
|
||||
data-operate-del="{:$auth->check('{%controllerUrl%}/del')}"
|
||||
width="100%">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ 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',
|
||||
|
|
@ -30,7 +30,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
|
|||
|
||||
// 为表格绑定事件
|
||||
Table.api.bindevent(table);
|
||||
},
|
||||
},{%recyclebinJs%}
|
||||
add: function () {
|
||||
Controller.api.bindevent();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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]]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
public function {%relationMethod%}()
|
||||
{
|
||||
return $this->{%relationMode%}('{%relationModelName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}')->setEagerlyType(0);
|
||||
return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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] : '';
|
||||
}
|
||||
|
|
@ -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);
|
||||
},
|
||||
|
|
@ -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] : '';
|
||||
}
|
||||
|
|
@ -1,33 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace {%modelNamespace%};
|
||||
|
||||
use think\Model;
|
||||
|
||||
class {%modelName%} extends Model
|
||||
{
|
||||
// 表名
|
||||
protected ${%modelTableType%} = '{%modelTableName%}';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%};
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = {%createTime%};
|
||||
protected $updateTime = {%updateTime%};
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
{%appendAttrList%}
|
||||
];
|
||||
|
||||
{%modelInit%}
|
||||
|
||||
{%getEnumList%}
|
||||
|
||||
{%getAttrList%}
|
||||
|
||||
{%setAttrList%}
|
||||
|
||||
{%modelRelationMethod%}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace {%modelNamespace%};
|
||||
|
||||
use think\Model;
|
||||
{%sofeDeleteClassPath%}
|
||||
|
||||
class {%modelName%} extends Model
|
||||
{
|
||||
|
||||
{%softDelete%}
|
||||
|
||||
{%modelConnection%}
|
||||
|
||||
// 表名
|
||||
protected ${%modelTableType%} = '{%modelTableTypeName%}';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%};
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = {%createTime%};
|
||||
protected $updateTime = {%updateTime%};
|
||||
protected $deleteTime = {%deleteTime%};
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
{%appendAttrList%}
|
||||
];
|
||||
|
||||
{%modelInit%}
|
||||
|
||||
{%getEnumList%}
|
||||
|
||||
{%getAttrList%}
|
||||
|
||||
{%setAttrList%}
|
||||
|
||||
{%relationMethodList%}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<div class="panel panel-default panel-intro">
|
||||
{:build_heading()}
|
||||
|
||||
<div class="panel-body">
|
||||
<div id="myTabContent" class="tab-content">
|
||||
<div class="tab-pane fade active in" id="one">
|
||||
<div class="widget-body no-padding">
|
||||
<div id="toolbar" class="toolbar">
|
||||
{:build_toolbar('refresh')}
|
||||
<a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
|
||||
<a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
|
||||
<a class="btn btn-success btn-restoreall {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
|
||||
<a class="btn btn-danger btn-destroyall {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
|
||||
</div>
|
||||
<table id="table" class="table table-striped table-bordered table-hover"
|
||||
data-operate-restore="{:$auth->check('{%controllerUrl%}/restore')}"
|
||||
data-operate-destroy="{:$auth->check('{%controllerUrl%}/destroy')}"
|
||||
width="100%">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace {%modelNamespace%};
|
||||
|
||||
use think\Model;
|
||||
|
||||
class {%relationModelName%} extends Model
|
||||
{
|
||||
// 表名
|
||||
protected ${%relationModelTableType%} = '{%relationModelTableName%}';
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace {%modelNamespace%};
|
||||
|
||||
use think\Model;
|
||||
|
||||
class {%relationName%} extends Model
|
||||
{
|
||||
// 表名
|
||||
protected ${%relationTableType%} = '{%relationTableTypeName%}';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace {%validateNamespace%};
|
||||
|
||||
use think\Validate;
|
||||
|
||||
class {%validateName%} extends Validate
|
||||
{
|
||||
/**
|
||||
* 验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
];
|
||||
/**
|
||||
* 提示消息
|
||||
*/
|
||||
protected $message = [
|
||||
];
|
||||
/**
|
||||
* 验证场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'add' => [],
|
||||
'edit' => [],
|
||||
];
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace {%validateNamespace%};
|
||||
|
||||
use think\Validate;
|
||||
|
||||
class {%validateName%} extends Validate
|
||||
{
|
||||
/**
|
||||
* 验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
];
|
||||
/**
|
||||
* 提示消息
|
||||
*/
|
||||
protected $message = [
|
||||
];
|
||||
/**
|
||||
* 验证场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'add' => [],
|
||||
'edit' => [],
|
||||
];
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,45 +13,83 @@ use think\Exception;
|
|||
|
||||
class Install extends Command
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
//覆盖安装
|
||||
// 覆盖安装
|
||||
$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)
|
||||
{
|
||||
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');
|
||||
|
||||
$sql = str_replace("`fa_", "`{$prefix}", $sql);
|
||||
|
||||
// 先尝试能否自动创建数据库
|
||||
$config = Config::get('database');
|
||||
$pdo = new PDO("{$config['type']}:host={$config['hostname']}" . ($config['hostport'] ? ";port={$config['hostport']}" : ''), $config['username'], $config['password']);
|
||||
$pdo = new PDO("{$config['type']}:host={$hostname}" . ($hostport ? ";port={$hostport}" : ''), $username, $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;");
|
||||
$pdo->query("CREATE DATABASE IF NOT EXISTS `{$database}` CHARACTER SET utf8 COLLATE utf8_general_ci;");
|
||||
|
||||
// 连接install命令中指定的数据库
|
||||
$instance = Db::connect([
|
||||
'type' => "{$config['type']}",
|
||||
'hostname' => "{$hostname}",
|
||||
'hostport' => "{$hostport}",
|
||||
'database' => "{$database}",
|
||||
'username' => "{$username}",
|
||||
'password' => "{$password}",
|
||||
]);
|
||||
|
||||
// 查询一次SQL,判断连接是否正常
|
||||
Db::execute("SELECT 1");
|
||||
$instance->execute("SELECT 1");
|
||||
|
||||
// 调用原生PDO对象进行批量查询
|
||||
Db::getPdo()->exec($sql);
|
||||
$instance->getPdo()->exec($sql);
|
||||
|
||||
file_put_contents($installLockFile, 1);
|
||||
|
||||
$dbConfigFile = APP_PATH . 'database.php';
|
||||
$config = @file_get_contents($dbConfigFile);
|
||||
$callback = function ($matches) use ($hostname, $hostport, $username, $password, $database, $prefix) {
|
||||
$field = $matches[1];
|
||||
$replace = $$field;
|
||||
if ($matches[1] == 'hostport' && $hostport == 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);
|
||||
// 写入数据库配置
|
||||
file_put_contents($dbConfigFile, $config);
|
||||
|
||||
\think\Cache::rm('__menu__');
|
||||
|
||||
$output->info("Install Successed!");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
|
||||
|
|
@ -179,8 +206,7 @@ class Menu extends Command
|
|||
//反射机制调用类的注释和方法名
|
||||
$reflector = new ReflectionClass($className);
|
||||
|
||||
if (isset($tempClassFile))
|
||||
{
|
||||
if (isset($tempClassFile)) {
|
||||
//删除临时文件
|
||||
@unlink($tempClassFile);
|
||||
}
|
||||
|
|
@ -188,25 +214,33 @@ class Menu extends Command
|
|||
//只匹配公共的方法
|
||||
$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 +251,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');
|
||||
$rulemodel = $this->model->get(['name' => $name]);
|
||||
if (!$rulemodel)
|
||||
{
|
||||
$this->model
|
||||
->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal'])
|
||||
->isUpdate(false)
|
||||
->save();
|
||||
$pid = $this->model->id;
|
||||
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), "_"));
|
||||
}
|
||||
else
|
||||
{
|
||||
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) {
|
||||
$this->model
|
||||
->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal'])
|
||||
->isUpdate(false)
|
||||
->save();
|
||||
$pid = $this->model->id;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = "";
|
||||
$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!");
|
||||
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';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
({
|
||||
cssIn: "{%cssBasePath%}{%cssBaseName%}.css",
|
||||
out: "{%cssBasePath%}{%cssBaseName%}.min.css",
|
||||
optimizeCss: "default"
|
||||
optimizeCss: "default",
|
||||
optimize: "{%optimize%}"
|
||||
})
|
||||
|
|
@ -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%}", //来源文件,不包含后缀
|
||||
|
|
|
|||
|
|
@ -5,133 +5,192 @@ 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 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)
|
||||
/**
|
||||
* 生成下拉列表
|
||||
* @param string $name
|
||||
* @param mixed $options
|
||||
* @param mixed $selected
|
||||
* @param mixed $attr
|
||||
* @return string
|
||||
*/
|
||||
function build_select($name, $options, $selected = [], $attr = [])
|
||||
{
|
||||
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
|
||||
$options = is_array($options) ? $options : explode(',', $options);
|
||||
$selected = is_array($selected) ? $selected : explode(',', $selected);
|
||||
return Form::select($name, $options, $selected, $attr);
|
||||
}
|
||||
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)
|
||||
{
|
||||
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
|
||||
}
|
||||
return implode(' ', $html);
|
||||
}
|
||||
if (!function_exists('build_radios')) {
|
||||
|
||||
/**
|
||||
* 生成分类下拉列表框
|
||||
* @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)
|
||||
/**
|
||||
* 生成单选按钮组
|
||||
* @param string $name
|
||||
* @param array $list
|
||||
* @param mixed $selected
|
||||
* @return string
|
||||
*/
|
||||
function build_radios($name, $list = [], $selected = null)
|
||||
{
|
||||
$categorydata[$v['id']] = $v['name'];
|
||||
}
|
||||
$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 (!isset($btnAttr[$v]))
|
||||
{
|
||||
continue;
|
||||
$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}"]));
|
||||
}
|
||||
list($href, $class, $icon, $text) = $btnAttr[$v];
|
||||
$html[] = '<a href="' . $href . '" class="' . $class . '" ><i class="' . $icon . '"></i> ' . $text . '</a>';
|
||||
return '<div class="radio">' . implode(' ', $html) . '</div>';
|
||||
}
|
||||
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_checkboxs')) {
|
||||
|
||||
/**
|
||||
* 生成复选按钮组
|
||||
* @param string $name
|
||||
* @param array $list
|
||||
* @param mixed $selected
|
||||
* @return string
|
||||
*/
|
||||
function build_checkboxs($name, $list = [], $selected = null)
|
||||
{
|
||||
$path = request()->pathinfo();
|
||||
$path = $path[0] == '/' ? $path : '/' . $path;
|
||||
$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 '<div class="checkbox">' . implode(' ', $html) . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 = [])
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('build_toolbar')) {
|
||||
|
||||
/**
|
||||
* 生成表格操作按钮栏
|
||||
* @param array $btns 按钮组
|
||||
* @param array $attr 按钮属性值
|
||||
* @return string
|
||||
*/
|
||||
function build_toolbar($btns = null, $attr = [])
|
||||
{
|
||||
$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';
|
||||
}
|
||||
$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[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
|
||||
if ($v == 'import') {
|
||||
$template = str_replace('/', '_', $controller);
|
||||
$download = '';
|
||||
if (file_exists("./template/{$template}.xlsx")) {
|
||||
$download .= "<li><a href=\"/template/{$template}.xlsx\" target=\"_blank\">XLSX模版</a></li>";
|
||||
}
|
||||
if (file_exists("./template/{$template}.xls")) {
|
||||
$download .= "<li><a href=\"/template/{$template}.xls\" target=\"_blank\">XLS模版</a></li>";
|
||||
}
|
||||
if (file_exists("./template/{$template}.csv")) {
|
||||
$download .= empty($download) ? '' : "<li class=\"divider\"></li>";
|
||||
$download .= "<li><a href=\"/template/{$template}.csv\" target=\"_blank\">CSV模版</a></li>";
|
||||
}
|
||||
$download .= empty($download) ? '' : "\n ";
|
||||
if (!empty($download)) {
|
||||
$html[] = <<<EOT
|
||||
<div class="btn-group">
|
||||
<button type="button" href="{$href}" class="btn btn-info btn-import" title="{$title}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="{$icon}"></i> {$text}</button>
|
||||
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" title="下载批量导入模版">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">{$download}</ul>
|
||||
</div>
|
||||
EOT;
|
||||
} else {
|
||||
$html[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="' . $icon . '"></i> ' . $text . '</a>';
|
||||
}
|
||||
} else {
|
||||
$html[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '"><i class="' . $icon . '"></i> ' . $text . '</a>';
|
||||
}
|
||||
}
|
||||
return implode(' ', $html);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('build_heading')) {
|
||||
|
||||
/**
|
||||
* 生成页面Heading
|
||||
*
|
||||
* @param string $path 指定的path
|
||||
* @return string
|
||||
*/
|
||||
function build_heading($path = null, $container = true)
|
||||
{
|
||||
$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 = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
|
||||
if ($container) {
|
||||
$result = '<div class="panel-heading">' . $result . '</div>';
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
if (!$content)
|
||||
return '';
|
||||
return '<div class="panel-heading"><div class="panel-lead"><em>' . $title . '</em>' . $content . '</div></div>';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use fast\Http;
|
||||
use think\addons\AddonException;
|
||||
use think\addons\Service;
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* 插件管理
|
||||
*
|
||||
* @icon fa fa-cube
|
||||
* @remark 可在线安装、卸载、禁用、启用插件,同时支持添加本地插件。FastAdmin已上线插件商店 ,你可以发布你的免费或付费插件:<a href="https://www.fastadmin.net/store.html" target="_blank">https://www.fastadmin.net/store.html</a>
|
||||
*/
|
||||
class Addon extends Backend
|
||||
{
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
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]);
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置
|
||||
*/
|
||||
public function config($ids = null)
|
||||
{
|
||||
$name = $this->request->get("name");
|
||||
if (!$name) {
|
||||
$this->error(__('Parameter %s can not be empty', $ids ? 'id' : '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");
|
||||
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'));
|
||||
}
|
||||
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
|
||||
];
|
||||
Service::install($name, $force, $extend);
|
||||
$info = get_addon_info($name);
|
||||
$info['config'] = get_addon_config($name) ? 1 : 0;
|
||||
$info['state'] = 1;
|
||||
$this->success(__('Install successful'), null, ['addon' => $info]);
|
||||
} catch (AddonException $e) {
|
||||
$this->result($e->getData(), $e->getCode(), __($e->getMessage()));
|
||||
} catch (Exception $e) {
|
||||
$this->error(__($e->getMessage()), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载
|
||||
*/
|
||||
public function uninstall()
|
||||
{
|
||||
$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'));
|
||||
}
|
||||
try {
|
||||
Service::uninstall($name, $force);
|
||||
$this->success(__('Uninstall successful'));
|
||||
} catch (AddonException $e) {
|
||||
$this->result($e->getData(), $e->getCode(), __($e->getMessage()));
|
||||
} catch (Exception $e) {
|
||||
$this->error(__($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用启用
|
||||
*/
|
||||
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__');
|
||||
$this->success(__('Operate successful'));
|
||||
} catch (AddonException $e) {
|
||||
$this->result($e->getData(), $e->getCode(), __($e->getMessage()));
|
||||
} catch (Exception $e) {
|
||||
$this->error(__($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地上传
|
||||
*/
|
||||
public function local()
|
||||
{
|
||||
Config::set('default_return_type', 'json');
|
||||
|
||||
$file = $this->request->file('file');
|
||||
$addonTmpDir = RUNTIME_PATH . 'addons' . DS;
|
||||
if (!is_dir($addonTmpDir)) {
|
||||
@mkdir($addonTmpDir, 0755, true);
|
||||
}
|
||||
$info = $file->rule('uniqid')->validate(['size' => 10240000, 'ext' => 'zip'])->move($addonTmpDir);
|
||||
if ($info) {
|
||||
$tmpName = substr($info->getFilename(), 0, stripos($info->getFilename(), '.'));
|
||||
$tmpAddonDir = ADDON_PATH . $tmpName . DS;
|
||||
$tmpFile = $addonTmpDir . $info->getSaveName();
|
||||
try {
|
||||
Service::unzip($tmpName);
|
||||
unset($info);
|
||||
@unlink($tmpFile);
|
||||
$infoFile = $tmpAddonDir . 'info.ini';
|
||||
if (!is_file($infoFile)) {
|
||||
throw new Exception(__('Addon info file was not found'));
|
||||
}
|
||||
|
||||
$config = Config::parse($infoFile, '', $tmpName);
|
||||
$name = isset($config['name']) ? $config['name'] : '';
|
||||
if (!$name) {
|
||||
throw new Exception(__('Addon info file data incorrect'));
|
||||
}
|
||||
if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) {
|
||||
throw new Exception(__('Addon name incorrect'));
|
||||
}
|
||||
|
||||
$newAddonDir = ADDON_PATH . $name . DS;
|
||||
if (is_dir($newAddonDir)) {
|
||||
throw new Exception(__('Addon already exists'));
|
||||
}
|
||||
|
||||
//重命名插件文件夹
|
||||
rename($tmpAddonDir, $newAddonDir);
|
||||
try {
|
||||
//默认禁用该插件
|
||||
$info = get_addon_info($name);
|
||||
if ($info['state']) {
|
||||
$info['state'] = 0;
|
||||
set_addon_info($name, $info);
|
||||
}
|
||||
|
||||
//执行插件的安装方法
|
||||
$class = get_addon_class($name);
|
||||
if (class_exists($class)) {
|
||||
$addon = new $class();
|
||||
$addon->install();
|
||||
}
|
||||
|
||||
//导入SQL
|
||||
Service::importsql($name);
|
||||
|
||||
$info['config'] = get_addon_config($name) ? 1 : 0;
|
||||
$this->success(__('Offline installed tips'), null, ['addon' => $info]);
|
||||
} catch (Exception $e) {
|
||||
@rmdirs($newAddonDir);
|
||||
throw new Exception(__($e->getMessage()));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
unset($info);
|
||||
@unlink($tmpFile);
|
||||
@rmdirs($tmpAddonDir);
|
||||
$this->error(__($e->getMessage()));
|
||||
}
|
||||
} else {
|
||||
// 上传失败获取错误信息
|
||||
$this->error(__($file->getError()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新插件
|
||||
*/
|
||||
public function upgrade()
|
||||
{
|
||||
$name = $this->request->post("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'));
|
||||
}
|
||||
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
|
||||
];
|
||||
//调用更新的方法
|
||||
Service::upgrade($name, $extend);
|
||||
Cache::rm('__menu__');
|
||||
$this->success(__('Operate successful'));
|
||||
} catch (AddonException $e) {
|
||||
$this->result($e->getData(), $e->getCode(), __($e->getMessage()));
|
||||
} catch (Exception $e) {
|
||||
$this->error(__($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 已装插件
|
||||
*/
|
||||
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)) {
|
||||
$onlineaddons = [];
|
||||
$result = Http::sendRequest(config('fastadmin.api_url') . '/addon/index');
|
||||
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['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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,308 +1,286 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use fast\Random;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Db;
|
||||
use think\Lang;
|
||||
|
||||
/**
|
||||
* Ajax异步请求接口
|
||||
* @internal
|
||||
*/
|
||||
class Ajax extends Backend
|
||||
{
|
||||
|
||||
protected $noNeedLogin = ['lang'];
|
||||
protected $noNeedRight = ['*'];
|
||||
protected $layout = '';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
|
||||
//设置过滤方法
|
||||
$this->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;
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use fast\Random;
|
||||
use think\addons\Service;
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Db;
|
||||
use think\Lang;
|
||||
|
||||
/**
|
||||
* Ajax异步请求接口
|
||||
* @internal
|
||||
*/
|
||||
class Ajax extends Backend
|
||||
{
|
||||
|
||||
protected $noNeedLogin = ['lang'];
|
||||
protected $noNeedRight = ['*'];
|
||||
protected $layout = '';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
|
||||
//设置过滤方法
|
||||
$this->request->filter(['strip_tags', 'htmlspecialchars']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载语言包
|
||||
*/
|
||||
public function lang()
|
||||
{
|
||||
header('Content-Type: application/javascript');
|
||||
$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');
|
||||
$file = $this->request->file('file');
|
||||
if (empty($file)) {
|
||||
$this->error(__('No file upload or server upload limit exceeded'));
|
||||
}
|
||||
|
||||
//判断是否已经存在附件
|
||||
$sha1 = $file->hash();
|
||||
$extparam = $this->request->post();
|
||||
|
||||
$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 && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
|
||||
|
||||
$mimetypeArr = explode(',', strtolower($upload['mimetype']));
|
||||
$typeArr = explode('/', $fileInfo['type']);
|
||||
|
||||
//禁止上传PHP和HTML文件
|
||||
if (in_array($fileInfo['type'], ['text/x-php', 'text/html']) || in_array($suffix, ['php', 'html', 'htm'])) {
|
||||
$this->error(__('Uploaded file format is limited'));
|
||||
}
|
||||
//验证文件后缀
|
||||
if ($upload['mimetype'] !== '*' &&
|
||||
(
|
||||
!in_array($suffix, $mimetypeArr)
|
||||
|| (stripos($typeArr[0] . '/', $upload['mimetype']) !== false && (!in_array($fileInfo['type'], $mimetypeArr) && !in_array($typeArr[0] . '/*', $mimetypeArr)))
|
||||
)
|
||||
) {
|
||||
$this->error(__('Uploaded file format is limited'));
|
||||
}
|
||||
//验证是否为图片文件
|
||||
$imagewidth = $imageheight = 0;
|
||||
if (in_array($fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) {
|
||||
$imgInfo = getimagesize($fileInfo['tmp_name']);
|
||||
if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) {
|
||||
$this->error(__('Uploaded file is not a valid image'));
|
||||
}
|
||||
$imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth;
|
||||
$imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight;
|
||||
}
|
||||
$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) {
|
||||
$params = array(
|
||||
'admin_id' => (int)$this->auth->id,
|
||||
'user_id' => 0,
|
||||
'filesize' => $fileInfo['size'],
|
||||
'imagewidth' => $imagewidth,
|
||||
'imageheight' => $imageheight,
|
||||
'imagetype' => $suffix,
|
||||
'imageframes' => 0,
|
||||
'mimetype' => $fileInfo['type'],
|
||||
'url' => $uploadDir . $splInfo->getSaveName(),
|
||||
'uploadtime' => time(),
|
||||
'storage' => 'local',
|
||||
'sha1' => $sha1,
|
||||
'extparam' => json_encode($extparam),
|
||||
);
|
||||
$attachment = model("attachment");
|
||||
$attachment->data(array_filter($params));
|
||||
$attachment->save();
|
||||
\think\Hook::listen("upload_after", $attachment);
|
||||
$this->success(__('Upload successful'), null, [
|
||||
'url' => $uploadDir . $splInfo->getSaveName()
|
||||
]);
|
||||
} else {
|
||||
// 上传失败获取错误信息
|
||||
$this->error($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");
|
||||
//主键
|
||||
$pk = $this->request->post("pk");
|
||||
//排序的方式
|
||||
$orderway = $this->request->post("orderway", "", 'strtolower');
|
||||
$orderway = $orderway == 'asc' ? 'ASC' : 'DESC';
|
||||
$sour = $weighdata = [];
|
||||
$ids = explode(',', $ids);
|
||||
$prikey = $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;
|
||||
}
|
||||
}
|
||||
$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 !== '') {
|
||||
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->success('', null, $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'] : null;
|
||||
} else {
|
||||
$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->success('', null, $provincelist);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,69 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use app\common\model\Category as CategoryModel;
|
||||
use fast\Tree;
|
||||
|
||||
/**
|
||||
* 分类管理
|
||||
*
|
||||
* @icon fa fa-list
|
||||
* @remark 用于统一管理网站的所有分类,分类可进行无限级分类
|
||||
*/
|
||||
class Category extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $categorylist = [];
|
||||
protected $noNeedRight = ['selectpage'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use app\common\model\Category as CategoryModel;
|
||||
use fast\Tree;
|
||||
|
||||
/**
|
||||
* 分类管理
|
||||
*
|
||||
* @icon fa fa-list
|
||||
* @remark 用于统一管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加
|
||||
*/
|
||||
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 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);
|
||||
|
||||
if ($params['pid'] != $row['pid']) {
|
||||
$childrenIds = Tree::instance()->init(collection(\app\common\model\Category::select())->toArray())->getChildrenIds($row['id']);
|
||||
if (in_array($params['pid'], $childrenIds)) {
|
||||
$this->error(__('Can not change the parent to child'));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 控制台
|
||||
*
|
||||
* @icon fa fa-dashboard
|
||||
* @remark 用于展示当前系统中的统计数据、统计报表及重要实时数据
|
||||
*/
|
||||
class Dashboard extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$seventtime = \fast\Date::unixtime('day', -7);
|
||||
$paylist = $createlist = [];
|
||||
for ($i = 0; $i < 7; $i++)
|
||||
{
|
||||
$day = date("Y-m-d", $seventtime + ($i * 86400));
|
||||
$createlist[$day] = mt_rand(20, 200);
|
||||
$paylist[$day] = mt_rand(1, mt_rand(1, $createlist[$day]));
|
||||
}
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use think\Config;
|
||||
|
||||
/**
|
||||
* 控制台
|
||||
*
|
||||
* @icon fa fa-dashboard
|
||||
* @remark 用于展示当前系统中的统计数据、统计报表及重要实时数据
|
||||
*/
|
||||
class Dashboard extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$seventtime = \fast\Date::unixtime('day', -7);
|
||||
$paylist = $createlist = [];
|
||||
for ($i = 0; $i < 7; $i++)
|
||||
{
|
||||
$day = date("Y-m-d", $seventtime + ($i * 86400));
|
||||
$createlist[$day] = mt_rand(20, 200);
|
||||
$paylist[$day] = mt_rand(1, mt_rand(1, $createlist[$day]));
|
||||
}
|
||||
$hooks = config('addons.hooks');
|
||||
$uploadmode = isset($hooks['upload_config_init']) && $hooks['upload_config_init'] ? implode(',', $hooks['upload_config_init']) : 'local';
|
||||
$addonComposerCfg = ROOT_PATH . '/vendor/karsonzhang/fastadmin-addons/composer.json';
|
||||
Config::parse($addonComposerCfg, "json", "composer");
|
||||
$config = Config::get("composer");
|
||||
$addonVersion = isset($config['version']) ? $config['version'] : __('Unknown');
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,109 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 后台首页
|
||||
* @internal
|
||||
*/
|
||||
class Index extends Backend
|
||||
{
|
||||
|
||||
protected $noNeedLogin = ['login'];
|
||||
protected $noNeedRight = ['index', 'logout'];
|
||||
protected $layout = '';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台首页
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
$menulist = $this->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;
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\admin\model\AdminLog;
|
||||
use app\common\controller\Backend;
|
||||
use think\Config;
|
||||
use think\Hook;
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 后台首页
|
||||
* @internal
|
||||
*/
|
||||
class Index extends Backend
|
||||
{
|
||||
|
||||
protected $noNeedLogin = ['login'];
|
||||
protected $noNeedRight = ['index', 'logout'];
|
||||
protected $layout = '';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台首页
|
||||
*/
|
||||
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 = 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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 单页管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
* @remark 用于管理普通的单页面,通常用于关于我们、联系我们、商务合作等单一页面
|
||||
*/
|
||||
class Page extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $relationSearch = true;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 会员管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
* @internal
|
||||
*/
|
||||
class User extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* User模型对象
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('User');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
use think\Controller;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 版本管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class Version extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('Version');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
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;
|
||||
|
|
@ -15,34 +17,52 @@ use fast\Tree;
|
|||
class Admin extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\admin\model\Admin
|
||||
*/
|
||||
protected $model = null;
|
||||
//当前登录管理员所有子节点组别
|
||||
protected $childrenIds = [];
|
||||
protected $childrenGroupIds = [];
|
||||
protected $childrenAdminIds = [];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('Admin');
|
||||
|
||||
$groups = $this->auth->getGroups();
|
||||
$this->childrenAdminIds = $this->auth->getChildrenAdminIds(true);
|
||||
$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();
|
||||
|
||||
Tree::instance()->init($groupList);
|
||||
$groupdata = [];
|
||||
foreach ($objlist as $k => $v)
|
||||
if ($this->auth->isSuperAdmin())
|
||||
{
|
||||
$groupdata[$v['id']] = $v['name'];
|
||||
$result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0));
|
||||
foreach ($result as $k => $v)
|
||||
{
|
||||
$groupdata[$v['id']] = $v['name'];
|
||||
}
|
||||
}
|
||||
$this->childrenIds = array_keys($groupdata);
|
||||
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->view->assign('groupdata', $groupdata);
|
||||
$this->assignconfig("admin", ['id' => $this->auth->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -52,31 +72,39 @@ class Admin extends Backend
|
|||
{
|
||||
if ($this->request->isAjax())
|
||||
{
|
||||
$groupData = model('AuthGroup')->where('status', 'normal')->column('id,name');
|
||||
|
||||
$childrenAdminIds = [];
|
||||
$authGroupList = model('AuthGroupAccess')
|
||||
//如果发送的来源是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')
|
||||
->where('group_id', 'in', $this->childrenIds)
|
||||
->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']];
|
||||
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)
|
||||
->where('id', 'in', $this->childrenAdminIds)
|
||||
->order($sort, $order)
|
||||
->count();
|
||||
|
||||
$list = $this->model
|
||||
->where($where)
|
||||
->where('id', 'in', $childrenAdminIds)
|
||||
->where('id', 'in', $this->childrenAdminIds)
|
||||
->field(['password', 'salt', 'token'], true)
|
||||
->order($sort, $order)
|
||||
->limit($offset, $limit)
|
||||
|
|
@ -87,6 +115,7 @@ class Admin extends Backend
|
|||
$v['groups'] = implode(',', array_keys($groups));
|
||||
$v['groups_text'] = implode(',', array_values($groups));
|
||||
}
|
||||
unset($v);
|
||||
$result = array("total" => $total, "rows" => $list);
|
||||
|
||||
return json($result);
|
||||
|
|
@ -101,29 +130,30 @@ class Admin extends Backend
|
|||
{
|
||||
if ($this->request->isPost())
|
||||
{
|
||||
$this->code = -1;
|
||||
$params = $this->request->post("row/a");
|
||||
if ($params)
|
||||
{
|
||||
$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];
|
||||
$dataset[] = ['uid' => $this->model->id, 'group_id' => $value];
|
||||
}
|
||||
model('AuthGroupAccess')->saveAll($dataset);
|
||||
$this->code = 1;
|
||||
$this->success();
|
||||
}
|
||||
|
||||
return;
|
||||
$this->error();
|
||||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
|
@ -138,7 +168,6 @@ class Admin extends Backend
|
|||
$this->error(__('No Results were found'));
|
||||
if ($this->request->isPost())
|
||||
{
|
||||
$this->code = -1;
|
||||
$params = $this->request->post("row/a");
|
||||
if ($params)
|
||||
{
|
||||
|
|
@ -151,7 +180,17 @@ class Admin extends Backend
|
|||
{
|
||||
unset($params['password'], $params['salt']);
|
||||
}
|
||||
$row->save($params);
|
||||
//这里需要针对username和email做唯一验证
|
||||
$adminValidate = \think\Loader::validate('Admin');
|
||||
$adminValidate->rule([
|
||||
'username' => 'require|max:50|unique:admin,username,' . $row->id,
|
||||
'email' => 'require|email|unique:admin,email,' . $row->id
|
||||
]);
|
||||
$result = $row->validate('Admin.edit')->save($params);
|
||||
if ($result === false)
|
||||
{
|
||||
$this->error($row->getError());
|
||||
}
|
||||
|
||||
// 先移除所有权限
|
||||
model('AuthGroupAccess')->where('uid', $row->id)->delete();
|
||||
|
|
@ -159,7 +198,7 @@ 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)
|
||||
|
|
@ -167,10 +206,9 @@ class Admin extends Backend
|
|||
$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 = [];
|
||||
|
|
@ -188,11 +226,10 @@ class Admin extends Backend
|
|||
*/
|
||||
public function del($ids = "")
|
||||
{
|
||||
$this->code = -1;
|
||||
if ($ids)
|
||||
{
|
||||
// 避免越权删除管理员
|
||||
$childrenGroupIds = $this->childrenIds;
|
||||
$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();
|
||||
|
|
@ -208,12 +245,11 @@ class Admin extends Backend
|
|||
{
|
||||
$this->model->destroy($deleteIds);
|
||||
model('AuthGroupAccess')->where('uid', 'in', $deleteIds)->delete();
|
||||
$this->code = 1;
|
||||
$this->success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -223,7 +259,17 @@ 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace app\admin\controller\auth;
|
||||
|
||||
use app\admin\model\AuthGroup;
|
||||
use app\common\controller\Backend;
|
||||
use fast\Tree;
|
||||
|
||||
/**
|
||||
* 管理员日志
|
||||
|
|
@ -14,34 +14,25 @@ use fast\Tree;
|
|||
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();
|
||||
$this->childrenAdminIds = $this->auth->getChildrenAdminIds(true);
|
||||
$this->childrenGroupIds = $this->auth->getChildrenGroupIds($this->auth->isSuperAdmin() ? true : false);
|
||||
|
||||
// 取出所有分组
|
||||
$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);
|
||||
$groupName = AuthGroup::where('id', 'in', $this->childrenGroupIds)
|
||||
->column('id,name');
|
||||
|
||||
$this->view->assign('groupdata', $groupName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -51,20 +42,16 @@ class Adminlog extends Backend
|
|||
{
|
||||
if ($this->request->isAjax())
|
||||
{
|
||||
$childrenAdminIds = model('AuthGroupAccess')
|
||||
->field('uid')
|
||||
->where('group_id', 'in', $this->childrenIds)
|
||||
->column('uid');
|
||||
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||
$total = $this->model
|
||||
->where($where)
|
||||
->where('admin_id', 'in', $childrenAdminIds)
|
||||
->where('admin_id', 'in', $this->childrenAdminIds)
|
||||
->order($sort, $order)
|
||||
->count();
|
||||
|
||||
$list = $this->model
|
||||
->where($where)
|
||||
->where('admin_id', 'in', $childrenAdminIds)
|
||||
->where('admin_id', 'in', $this->childrenAdminIds)
|
||||
->order($sort, $order)
|
||||
->limit($offset, $limit)
|
||||
->select();
|
||||
|
|
@ -74,7 +61,7 @@ class Adminlog extends Backend
|
|||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
|
|
@ -93,7 +80,7 @@ class Adminlog extends Backend
|
|||
*/
|
||||
public function add()
|
||||
{
|
||||
$this->code = -1;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -102,7 +89,7 @@ class Adminlog extends Backend
|
|||
*/
|
||||
public function edit($ids = NULL)
|
||||
{
|
||||
$this->code = -1;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -110,10 +97,9 @@ class Adminlog extends Backend
|
|||
*/
|
||||
public function del($ids = "")
|
||||
{
|
||||
$this->code = -1;
|
||||
if ($ids)
|
||||
{
|
||||
$childrenGroupIds = $this->childrenIds;
|
||||
$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();
|
||||
|
|
@ -127,12 +113,11 @@ class Adminlog extends Backend
|
|||
if ($deleteIds)
|
||||
{
|
||||
$this->model->destroy($deleteIds);
|
||||
$this->code = 1;
|
||||
$this->success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,7 +127,12 @@ class Adminlog extends Backend
|
|||
public function multi($ids = "")
|
||||
{
|
||||
// 管理员禁止批量操作
|
||||
$this->code = -1;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
public function selectpage()
|
||||
{
|
||||
return parent::selectpage();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace app\admin\controller\auth;
|
||||
|
||||
use app\admin\model\AuthGroup;
|
||||
use app\common\controller\Backend;
|
||||
use fast\Tree;
|
||||
|
||||
|
|
@ -14,9 +15,12 @@ use fast\Tree;
|
|||
class Group extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\admin\model\AuthGroup
|
||||
*/
|
||||
protected $model = null;
|
||||
//当前登录管理员所有子节点组别
|
||||
protected $childrenIds = [];
|
||||
//当前登录管理员所有子组别
|
||||
protected $childrenGroupIds = [];
|
||||
//当前组别列表数据
|
||||
protected $groupdata = [];
|
||||
//无需要权限判断的方法
|
||||
|
|
@ -27,27 +31,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();
|
||||
|
||||
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'])));
|
||||
}
|
||||
}
|
||||
$groupName = [];
|
||||
foreach ($result as $k => $v) {
|
||||
$groupName[$v['id']] = $v['name'];
|
||||
}
|
||||
|
||||
$groupdata = [];
|
||||
foreach ($objlist as $k => $v)
|
||||
{
|
||||
$groupdata[$v['id']] = $v['name'];
|
||||
}
|
||||
$this->groupdata = $groupdata;
|
||||
$this->childrenIds = array_keys($groupdata);
|
||||
$this->view->assign('groupdata', $groupdata);
|
||||
$this->groupdata = $groupName;
|
||||
$this->assignconfig("admin", ['id' => $this->auth->id, 'group_ids' => $this->auth->getGroupIds()]);
|
||||
|
||||
$this->view->assign('groupdata', $this->groupdata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,14 +61,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 +88,15 @@ class Group extends Backend
|
|||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost())
|
||||
{
|
||||
$this->code = -1;
|
||||
if ($this->request->isPost()) {
|
||||
$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 can not be its own child'));
|
||||
}
|
||||
$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 +108,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 +120,23 @@ class Group 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()) {
|
||||
$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 can not be its own child'));
|
||||
}
|
||||
$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 +148,11 @@ class Group extends Backend
|
|||
// 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限
|
||||
$rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules);
|
||||
$params['rules'] = implode(',', $rules);
|
||||
if ($params)
|
||||
{
|
||||
if ($params) {
|
||||
$row->save($params);
|
||||
$this->code = 1;
|
||||
$this->success();
|
||||
}
|
||||
|
||||
$this->error();
|
||||
return;
|
||||
}
|
||||
$this->view->assign("row", $row);
|
||||
|
|
@ -168,12 +164,10 @@ class Group extends Backend
|
|||
*/
|
||||
public function del($ids = "")
|
||||
{
|
||||
$this->code = -1;
|
||||
if ($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 +176,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 +208,12 @@ class Group extends Backend
|
|||
public function multi($ids = "")
|
||||
{
|
||||
// 组别禁止批量操作
|
||||
$this->code = -1;
|
||||
return;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取角色权限树
|
||||
*
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function roletree()
|
||||
|
|
@ -236,75 +223,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'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -15,6 +16,9 @@ use think\Cache;
|
|||
class Rule extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\admin\model\AuthRule
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $rulelist = [];
|
||||
protected $multiFields = 'ismenu,status';
|
||||
|
|
@ -24,15 +28,22 @@ class Rule extends Backend
|
|||
parent::_initialize();
|
||||
$this->model = model('AuthRule');
|
||||
// 必须将结果集转换为数组
|
||||
Tree::instance()->init(collection($this->model->order('weigh', 'desc')->select())->toArray());
|
||||
$ruleList = collection($this->model->order('weigh', 'desc')->order('id', 'asc')->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'])
|
||||
foreach ($this->rulelist as $k => &$v) {
|
||||
if (!$v['ismenu']) {
|
||||
continue;
|
||||
}
|
||||
$ruledata[$v['id']] = $v['title'];
|
||||
}
|
||||
unset($v);
|
||||
$this->view->assign('ruledata', $ruledata);
|
||||
}
|
||||
|
||||
|
|
@ -41,8 +52,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 +68,20 @@ class Rule extends Backend
|
|||
*/
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost())
|
||||
{
|
||||
$this->code = -1;
|
||||
if ($this->request->isPost()) {
|
||||
$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 +89,37 @@ 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()) {
|
||||
$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 +130,18 @@ class Rule extends Backend
|
|||
*/
|
||||
public function del($ids = "")
|
||||
{
|
||||
$this->code = -1;
|
||||
if ($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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 表格完整示例
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark 在使用Bootstrap-table中的常用方式,更多使用方式可查看:http://bootstrap-table.wenzhixin.net.cn/zh-cn/
|
||||
*/
|
||||
class Bootstraptable extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $noNeedRight = ['change', 'detail'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 彩色角标
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark 在JS端控制角标的显示与隐藏,请注意左侧菜单栏角标的数值变化
|
||||
*/
|
||||
class Colorbadge extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('AdminLog');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 控制器间跳转
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark FastAdmin支持在控制器间跳转,点击后将切换到另外一个TAB中,无需刷新当前页面
|
||||
*/
|
||||
class Controllerjump extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('AdminLog');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 多级联动
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark FastAdmin使用了jQuery-cxselect实现多级联动,更多文档请参考https://github.com/karsonzhang/cxSelect
|
||||
*/
|
||||
class Cxselect extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 多表格示例
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark 当一个页面上存在多个Bootstrap-table时该如何控制按钮和表格
|
||||
*/
|
||||
class Multitable extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->loadlang('general/attachment');
|
||||
$this->loadlang('general/crontab');
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 多模型关联
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark 当使用到关联模型时需要重载index方法
|
||||
*/
|
||||
class Relationmodel extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\example;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 表格模板示例
|
||||
*
|
||||
* @icon fa fa-table
|
||||
* @remark 可以通过使用表格模板将表格中的行渲染成一样的展现方式,基于此功能可以任意定制自己想要的展示列表
|
||||
*/
|
||||
class Tabletemplate extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,79 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 附件管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
* @remark 主要用于管理上传到又拍云的数据或上传至本服务的上传数据
|
||||
*/
|
||||
class Attachment extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 附件管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
* @remark 主要用于管理上传到又拍云的数据或上传至本服务的上传数据
|
||||
*/
|
||||
class Attachment extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\common\model\Attachment
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('Attachment');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//设置过滤方法
|
||||
$this->request->filter(['strip_tags']);
|
||||
if ($this->request->isAjax()) {
|
||||
$mimetypeQuery = [];
|
||||
$filter = $this->request->request('filter');
|
||||
$filterArr = (array)json_decode($filter, TRUE);
|
||||
if (isset($filterArr['mimetype']) && stripos($filterArr['mimetype'], ',') !== false) {
|
||||
$this->request->get(['filter' => json_encode(array_merge($filterArr, ['mimetype' => '']))]);
|
||||
$mimetypeQuery = function ($query) use ($filterArr) {
|
||||
$mimetypeArr = explode(',', $filterArr['mimetype']);
|
||||
foreach ($mimetypeArr as $index => $item) {
|
||||
$query->whereOr('mimetype', 'like', '%' . $item . '%');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||
$total = $this->model
|
||||
->where($mimetypeQuery)
|
||||
->where($where)
|
||||
->order($sort, $order)
|
||||
->count();
|
||||
|
||||
$list = $this->model
|
||||
->where($mimetypeQuery)
|
||||
->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->error();
|
||||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除附件
|
||||
* @param array $ids
|
||||
*/
|
||||
public function del($ids = "")
|
||||
{
|
||||
if ($ids) {
|
||||
\think\Hook::add('upload_delete', function ($params) {
|
||||
$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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,263 +1,218 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use app\common\library\Email;
|
||||
use app\common\model\Config as ConfigModel;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* 系统配置
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class Config extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $noNeedRight = ['check'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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', '<?php' . "\n\nreturn " . var_export($config, true) . ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$params = $this->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('<div style="min-height:550px; padding: 100px 55px 200px;">' . __('This is a test mail content') . '</div>')
|
||||
->send();
|
||||
if ($result)
|
||||
{
|
||||
$this->code = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->code = -1;
|
||||
$this->msg = $email->getError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use app\common\library\Email;
|
||||
use app\common\model\Config as ConfigModel;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* 系统配置
|
||||
*
|
||||
* @icon fa fa-cogs
|
||||
* @remark 可以在此增改系统的变量和分组,也可以自定义分组和变量,如果需要删除请从数据库中删除
|
||||
*/
|
||||
class Config extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\common\model\Config
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $noNeedRight = ['check'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
$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('groupList', ConfigModel::getGroupList());
|
||||
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 (in_array($params['type'], ['select', 'selects', 'checkbox', 'radio', 'array'])) {
|
||||
$params['content'] = json_encode(ConfigModel::decode($params['content']), JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
$params['content'] = '';
|
||||
}
|
||||
$result = $this->model->create($params);
|
||||
if ($result !== false) {
|
||||
try {
|
||||
$this->refreshFile();
|
||||
} catch (Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
$this->success();
|
||||
} else {
|
||||
$this->error($this->model->getError());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->error(__('Parameter %s can not be empty', ''));
|
||||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param null $ids
|
||||
*/
|
||||
public function edit($ids = NULL)
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$row = $this->request->post("row/a");
|
||||
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();
|
||||
}
|
||||
}
|
||||
$this->model->allowField(true)->saveAll($configList);
|
||||
try {
|
||||
$this->refreshFile();
|
||||
} catch (Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
$this->success();
|
||||
}
|
||||
$this->error(__('Parameter %s can not be empty', ''));
|
||||
}
|
||||
}
|
||||
|
||||
public function del($ids = "")
|
||||
{
|
||||
$name = $this->request->request('name');
|
||||
$config = ConfigModel::getByName($name);
|
||||
if ($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(APP_PATH . 'extra' . DS . 'site.php', '<?php' . "\n\nreturn " . var_export($config, true) . ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测配置项是否存在
|
||||
* @internal
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$params = $this->request->post("row/a");
|
||||
if ($params) {
|
||||
|
||||
$config = $this->model->get($params);
|
||||
if (!$config) {
|
||||
return $this->success();
|
||||
} else {
|
||||
return $this->error(__('Name already exist'));
|
||||
}
|
||||
} else {
|
||||
return $this->error(__('Invalid parameters'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送测试邮件
|
||||
* @internal
|
||||
*/
|
||||
public function emailtest()
|
||||
{
|
||||
$row = $this->request->post('row/a');
|
||||
\think\Config::set('site', array_merge(\think\Config::get('site'), $row));
|
||||
$receiver = $this->request->request("receiver");
|
||||
$email = new Email;
|
||||
$result = $email
|
||||
->to($receiver)
|
||||
->subject(__("This is a test mail"))
|
||||
->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . __('This is a test mail content') . '</div>')
|
||||
->send();
|
||||
if ($result) {
|
||||
$this->success();
|
||||
} else {
|
||||
$this->error($email->getError());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use Cron\CronExpression;
|
||||
|
||||
/**
|
||||
* 定时任务
|
||||
*
|
||||
* @icon fa fa-tasks
|
||||
* @remark 类似于Linux的Crontab定时任务,可以按照设定的时间进行任务的执行,目前仅支持三种任务:请求URL、执行SQL、执行Shell
|
||||
*/
|
||||
class Crontab extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $noNeedRight = ['check_schedule', 'get_schedule_future'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use think\Db;
|
||||
use think\Debug;
|
||||
|
||||
/**
|
||||
* 数据库管理
|
||||
*
|
||||
* @icon fa fa-database
|
||||
* @remark 可在线进行一些简单的数据库表优化或修复,查看表结构和数据。也可以进行SQL语句的操作
|
||||
*/
|
||||
class Database extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
function index()
|
||||
{
|
||||
$tables_data_length = $tables_index_length = $tables_free_length = $tables_data_count = 0;
|
||||
$tables = $list = [];
|
||||
$list = Db::query("SHOW TABLES");
|
||||
foreach ($list as $key => $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 '<style type="text/css">
|
||||
xmp,body{margin:0;padding:0;line-height:18px;font-size:12px;font-family:"Helvetica Neue", Helvetica, Microsoft Yahei, Hiragino Sans GB, WenQuanYi Micro Hei, sans-serif;}
|
||||
hr{height:1px;margin:5px 1px;background:#e3e3e3;border:none;}
|
||||
</style>';
|
||||
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 "<br />";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function viewinfo($name)
|
||||
{
|
||||
$row = Db::query("SHOW CREATE TABLE `{$name}`");
|
||||
$row = array_values($row[0]);
|
||||
$info = $row[1];
|
||||
echo "<xmp>{$info};</xmp>";
|
||||
}
|
||||
|
||||
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 "<br />";
|
||||
}
|
||||
}
|
||||
|
||||
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 "<br />";
|
||||
}
|
||||
}
|
||||
|
||||
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:<span style='color:green;'>{$val}</span> ";
|
||||
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) . "<br />";
|
||||
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 .= "<hr/>";
|
||||
$r .= "<font color='red'>" . __('Row:%s', $j) . "</font><br />";
|
||||
foreach ($n as $k => $v)
|
||||
{
|
||||
$r .= "<font color='blue'>{$k}:</font>{$v}<br/>\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) . "<br />";
|
||||
}
|
||||
}
|
||||
echo $r;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,80 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use think\Session;
|
||||
use app\admin\model\AdminLog;
|
||||
use app\common\controller\Backend;
|
||||
use fast\Random;
|
||||
|
||||
/**
|
||||
* 个人配置
|
||||
*
|
||||
* @icon fa fa-user
|
||||
*/
|
||||
class Profile extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->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;
|
||||
}
|
||||
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\admin\controller\general;
|
||||
|
||||
use app\admin\model\Admin;
|
||||
use app\common\controller\Backend;
|
||||
use fast\Random;
|
||||
use think\Session;
|
||||
|
||||
/**
|
||||
* 个人配置
|
||||
*
|
||||
* @icon fa fa-user
|
||||
*/
|
||||
class Profile extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//设置过滤方法
|
||||
$this->request->filter(['strip_tags']);
|
||||
if ($this->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())
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$admin = Admin::get($this->auth->id);
|
||||
$admin->save($params);
|
||||
//因为个人资料面板读取的Session显示,修改自己资料后同时更新Session
|
||||
Session::set("admin", $admin->toArray());
|
||||
$this->success();
|
||||
}
|
||||
$this->error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\user;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 会员组管理
|
||||
*
|
||||
* @icon fa fa-users
|
||||
*/
|
||||
class Group extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\admin\model\UserGroup
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('UserGroup');
|
||||
$this->view->assign("statusList", $this->model->getStatusList());
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$nodeList = \app\admin\model\UserRule::getTreeList();
|
||||
$this->assign("nodeList", $nodeList);
|
||||
return parent::add();
|
||||
}
|
||||
|
||||
public function edit($ids = NULL)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\user;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use fast\Tree;
|
||||
|
||||
/**
|
||||
* 会员规则管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class Rule extends Backend
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @var \app\admin\model\UserRule
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $rulelist = [];
|
||||
protected $multiFields = 'ismenu,status';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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 del($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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\user;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* 会员管理
|
||||
*
|
||||
* @icon fa fa-user
|
||||
*/
|
||||
class User extends Backend
|
||||
{
|
||||
|
||||
protected $relationSearch = true;
|
||||
|
||||
|
||||
/**
|
||||
* @var \app\admin\model\User
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = model('User');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//设置过滤方法
|
||||
$this->request->filter(['strip_tags']);
|
||||
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
|
||||
->with('group')
|
||||
->where($where)
|
||||
->order($sort, $order)
|
||||
->count();
|
||||
$list = $this->model
|
||||
->with('group')
|
||||
->where($where)
|
||||
->order($sort, $order)
|
||||
->limit($offset, $limit)
|
||||
->select();
|
||||
foreach ($list as $k => $v) {
|
||||
$v->hidden(['password', 'salt']);
|
||||
}
|
||||
$result = array("total" => $total, "rows" => $list);
|
||||
|
||||
return json($result);
|
||||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
public function edit($ids = NULL)
|
||||
{
|
||||
$row = $this->model->get($ids);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\wechat;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use app\common\model\WechatResponse;
|
||||
|
||||
/**
|
||||
* 微信自动回复管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class Autoreply extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $noNeedRight = ['check_text_unique'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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')]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\wechat;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use think\Controller;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 微信配置管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class Config extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\wechat;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use app\common\model\WechatResponse;
|
||||
use EasyWeChat\Foundation\Application;
|
||||
use think\Config;
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* 菜单管理
|
||||
*
|
||||
* @icon fa fa-list-alt
|
||||
*/
|
||||
class Menu extends Backend
|
||||
{
|
||||
|
||||
protected $wechatcfg = NULL;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace app\admin\controller\wechat;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use fast\service\Wechat;
|
||||
|
||||
/**
|
||||
* 资源管理
|
||||
*
|
||||
* @icon fa fa-list-alt
|
||||
*/
|
||||
class Response extends Backend
|
||||
{
|
||||
|
||||
protected $model = null;
|
||||
protected $searchFields = 'id,title';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -11,10 +11,17 @@ return [
|
|||
'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' => '否',
|
||||
|
|
@ -40,6 +58,8 @@ return [
|
|||
'Choose' => '选择',
|
||||
'Search' => '搜索',
|
||||
'Refresh' => '刷新',
|
||||
'Install' => '安装',
|
||||
'Uninstall' => '卸载',
|
||||
'First' => '首页',
|
||||
'Previous' => '上一页',
|
||||
'Next' => '下一页',
|
||||
|
|
@ -53,20 +73,16 @@ return [
|
|||
'Index' => '首页',
|
||||
'Hot' => '热门',
|
||||
'Recommend' => '推荐',
|
||||
'Dashboard' => '控制台',
|
||||
'Upload' => '上传',
|
||||
'Uploading' => '上传中',
|
||||
'Code' => '编号',
|
||||
'Message' => '内容',
|
||||
'Line' => '行号',
|
||||
'File' => '文件',
|
||||
'Menu' => '菜单',
|
||||
'Name' => '名称',
|
||||
'Weigh' => '权重',
|
||||
'Type' => '类型',
|
||||
'Title' => '标题',
|
||||
'Content' => '内容',
|
||||
'Status' => '状态',
|
||||
'Operate' => '操作',
|
||||
'Append' => '追加',
|
||||
'Select' => '选择',
|
||||
'Memo' => '备注',
|
||||
|
|
@ -80,17 +96,13 @@ return [
|
|||
'Create time' => '创建时间',
|
||||
'Update time' => '更新时间',
|
||||
'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 +110,72 @@ 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?' => '确定清空?',
|
||||
'You have no permission' => '你没有权限访问',
|
||||
'Please enter your username' => '请输入你的用户名',
|
||||
'Please enter your password' => '请输入你的密码',
|
||||
'Please login first' => '请登录后操作',
|
||||
'You can upload up to %d file%s' => '你最多还可以上传%d个文件',
|
||||
'You can choose up to %d file%s' => '你最多还可以选择%d个文件',
|
||||
'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中',
|
||||
'This page will be re-directed in %s seconds' => '页面将在 %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' => '可在线安装、卸载、禁用、启用插件,同时支持添加本地插件。FastAdmin已上线插件商店 ,你可以发布你的免费或付费插件:<a href="https://www.fastadmin.net/store.html" target="_blank">https://www.fastadmin.net/store.html</a>',
|
||||
'Admin tips' => '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成',
|
||||
'Admin log tips' => '管理员可以查看自己所拥有的权限的管理员日志',
|
||||
'Group tips' => '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员',
|
||||
'Rule tips' => '规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过命令行进行生成规则节点',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Id' => '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' => '此处登录账号为<a href="https://www.fastadmin.net" target="_blank">FastAdmin官网账号</a>',
|
||||
'Logined tips' => '你好!%s<br />当前你已经登录,将同步保存你的购买记录',
|
||||
'Pay tips' => '扫码支付后如果仍然无法立即下载,请不要重复支付,请加<a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank">QQ群:636393962</a>向管理员反馈',
|
||||
'Pay click tips' => '请点击这里在新窗口中进行支付!',
|
||||
'Pay new window tips' => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!',
|
||||
'Uninstall tips' => '确认卸载<b>[%s]</b>?<p class="text-danger">卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!</p>如有重要数据请备份后再操作!',
|
||||
'Upgrade tips' => '确认升级<b>[%s]</b>?<p class="text-danger">如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!<br>升级后可能出现部分冗余数据记录,请根据需要移除即可!!!</p>如有重要数据请备份后再操作!',
|
||||
'Offline installed tips' => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
|
||||
'Online installed tips' => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
|
||||
'Not login tips' => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!',
|
||||
'Not installed tips' => '请安装后再访问插件前台页面!',
|
||||
'Not enabled tips' => '插件已经禁用,请启用后再访问插件前台页面!',
|
||||
'New version tips' => '发现新版本:%s 点击查看更新日志',
|
||||
'Store now available tips' => 'FastAdmin插件市场暂不可用,是否切换到本地插件?',
|
||||
'Switch to the local' => '切换到本地插件',
|
||||
'try to reload' => '重新尝试加载',
|
||||
'Please disable addon first' => '请先禁用插件再进行升级',
|
||||
'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' => '我购买的插件',
|
||||
'My posts' => '我发布的插件',
|
||||
'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' => '登录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' => '上传的插件已经存在',
|
||||
'Unable to open the zip file' => '无法打开ZIP文件',
|
||||
'Unable to extract the file' => '无法解压ZIP文件',
|
||||
];
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制',
|
||||
'Uploaded file format is limited' => '上传文件格式受限制',
|
||||
'Uploaded file is not a valid image' => '上传文件不是有效的图片文件',
|
||||
'Upload successful' => '上传成功',
|
||||
];
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Group' => '所属组别',
|
||||
'Login time' => '最后登录',
|
||||
'Group' => '所属组别',
|
||||
'Loginfailure' => '登录失败次数',
|
||||
'Login time' => '最后登录',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Toggle all' => '显示全部',
|
||||
'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规则只能是小写字母、数字、下划线和/组成',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Id' => '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' => '温馨提示:栏目类型请前往<b>常规管理</b>-><b>系统配置</b>-><b>字典配置</b>中进行管理',
|
||||
'Can not change the parent to child' => '父组别不能是它的子组别',
|
||||
'Status' => '状态'
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Custom' => '自定义',
|
||||
'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' => '查看更多',
|
||||
'Security tips' => '<i class="fa fa-warning"></i> 安全提示:你正在使用默认的后台登录入口,为了你的网站安全,建议你修改后台登录入口,<a href="https://forum.fastadmin.net/thread/7640" target="_blank">点击查看修改方法</a>',
|
||||
];
|
||||
|
|
@ -1,17 +1,22 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Url' => '物理路径',
|
||||
'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' => '预览',
|
||||
'Filesize' => '文件大小',
|
||||
'Mimetype' => 'Mime类型',
|
||||
'Extparam' => '透传数据',
|
||||
'Createtime' => '创建日期',
|
||||
'Uploadtime' => '上传时间',
|
||||
'Storage' => '存储引擎',
|
||||
'Upload to third' => '上传到第三方',
|
||||
'Upload to local' => '上传到本地',
|
||||
'Upload from editor' => '从编辑器上传'
|
||||
];
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@ return [
|
|||
'Basic' => '基础配置',
|
||||
'Email' => '邮件配置',
|
||||
'Attachment' => '附件配置',
|
||||
'Dictionary' => '字典配置',
|
||||
'User' => '会员配置',
|
||||
'Example' => '示例分组',
|
||||
'Extend' => '扩展属性',
|
||||
'String' => '字符',
|
||||
'Text' => '文本',
|
||||
'Editor' => '编辑器',
|
||||
'Number' => '数字',
|
||||
'Date' => '日期',
|
||||
'Time' => '时间',
|
||||
|
|
@ -25,6 +27,7 @@ return [
|
|||
'Files' => '文件(多)',
|
||||
'Select' => '列表',
|
||||
'Selects' => '列表(多选)',
|
||||
'Switch' => '开关',
|
||||
'Checkbox' => '复选',
|
||||
'Radio' => '单选',
|
||||
'Array' => '数组',
|
||||
|
|
@ -32,8 +35,26 @@ return [
|
|||
'Array value' => '键值',
|
||||
'Content' => '数据列表',
|
||||
'Rule' => '校验规则',
|
||||
'Site name' => '站点名称',
|
||||
'Beian' => '备案号',
|
||||
'Cdn url' => 'CDN地址',
|
||||
'Version' => '版本号',
|
||||
'Timezone' => '时区',
|
||||
'Forbidden ip' => '禁止IP',
|
||||
'Languages' => '语言',
|
||||
'Fixed page' => '后台固定页',
|
||||
'Category type' => '分类类型',
|
||||
'Config group' => '配置分组',
|
||||
'Mail type' => '邮件发送方式',
|
||||
'Mail smtp host' => 'SMTP服务器',
|
||||
'Mail smtp port' => 'SMTP端口',
|
||||
'Mail smtp user' => 'SMTP用户名',
|
||||
'Mail smtp password' => 'SMTP密码',
|
||||
'Mail vertify type' => 'SMTP验证方式',
|
||||
'Mail from' => '发件人邮箱',
|
||||
'Name already exist' => '变量名称已经存在',
|
||||
'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' => '请输入测试接收者邮箱',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Title' => '任务标题',
|
||||
'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 次的执行时间',
|
||||
];
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'SQL Result' => '查询结果',
|
||||
'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]失败'
|
||||
];
|
||||
|
||||
|
|
@ -4,6 +4,7 @@ return [
|
|||
'Url' => '链接',
|
||||
'Userame' => '用户名',
|
||||
'Createtime' => '操作时间',
|
||||
'Click to edit' => '点击编辑',
|
||||
'Admin log' => '操作日志',
|
||||
'Leave password blank if dont want to change' => '不修改密码请留空',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -26,9 +26,32 @@ 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' => '验证码',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'id' => 'ID',
|
||||
'category_id' => '分类ID',
|
||||
'category' => '分类',
|
||||
'title' => '标题',
|
||||
'keywords' => '关键字',
|
||||
'flag' => '标志',
|
||||
'image' => '头像',
|
||||
'content' => '内容',
|
||||
'icon' => '图标',
|
||||
'views' => '点击',
|
||||
'comments' => '评论',
|
||||
'weigh' => '权重',
|
||||
'status' => '状态'
|
||||
];
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Name' => '组名',
|
||||
'Rules' => '权限节点',
|
||||
'Createtime' => '添加时间',
|
||||
'Updatetime' => '更新时间',
|
||||
'Status' => '状态'
|
||||
];
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Pid' => '父ID',
|
||||
'Name' => '规则',
|
||||
'Title' => '标题',
|
||||
'Remark' => '备注',
|
||||
'Ismenu' => '是否菜单',
|
||||
'Createtime' => '创建时间',
|
||||
'Updatetime' => '更新时间',
|
||||
'Menu tips' => '规则任意,请不可重复,仅做层级显示,无需匹配控制器和方法',
|
||||
'Node tips' => '模块/控制器/方法名',
|
||||
'Weigh' => '权重',
|
||||
'Status' => '状态'
|
||||
];
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Id' => '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' => '不修改密码请留空',
|
||||
];
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'id' => 'ID',
|
||||
'oldversion' => '旧版本号',
|
||||
'newversion' => '新版本号',
|
||||
'packagesize' => '包大小',
|
||||
'content' => '升级内容',
|
||||
'downloadurl' => '下载地址',
|
||||
'enforce' => '强制更新',
|
||||
'createtime' => '创建时间',
|
||||
'updatetime' => '更新时间',
|
||||
'weigh' => '权重',
|
||||
'status' => '状态'
|
||||
];
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Text' => '文本',
|
||||
'Event key' => '响应标识',
|
||||
'Remark' => '备注',
|
||||
'Text already exists' => '文本已经存在',
|
||||
];
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'name' => '配置名称',
|
||||
'value' => '配置值',
|
||||
'Json key' => '键',
|
||||
'Json value' => '值',
|
||||
'createtime' => '创建时间',
|
||||
'updatetime' => '更新时间'
|
||||
];
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'Resource title' => '资源标题',
|
||||
'Event key' => '事件标识',
|
||||
'Text' => '文本',
|
||||
'App' => '应用',
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue