正在显示
48 个修改的文件
包含
4214 行增加
和
0 行删除
.bowerrc
0 → 100644
.env.sample
0 → 100644
.gitignore
0 → 100644
LICENSE
0 → 100644
1 | +Apache License | ||
2 | +Version 2.0, January 2004 | ||
3 | +http://www.apache.org/licenses/ | ||
4 | + | ||
5 | +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
6 | + | ||
7 | +1. Definitions. | ||
8 | + | ||
9 | +"License" shall mean the terms and conditions for use, reproduction, and | ||
10 | +distribution as defined by Sections 1 through 9 of this document. | ||
11 | + | ||
12 | +"Licensor" shall mean the copyright owner or entity authorized by the copyright | ||
13 | +owner that is granting the License. | ||
14 | + | ||
15 | +"Legal Entity" shall mean the union of the acting entity and all other entities | ||
16 | +that control, are controlled by, or are under common control with that entity. | ||
17 | +For the purposes of this definition, "control" means (i) the power, direct or | ||
18 | +indirect, to cause the direction or management of such entity, whether by | ||
19 | +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
20 | +outstanding shares, or (iii) beneficial ownership of such entity. | ||
21 | + | ||
22 | +"You" (or "Your") shall mean an individual or Legal Entity exercising | ||
23 | +permissions granted by this License. | ||
24 | + | ||
25 | +"Source" form shall mean the preferred form for making modifications, including | ||
26 | +but not limited to software source code, documentation source, and configuration | ||
27 | +files. | ||
28 | + | ||
29 | +"Object" form shall mean any form resulting from mechanical transformation or | ||
30 | +translation of a Source form, including but not limited to compiled object code, | ||
31 | +generated documentation, and conversions to other media types. | ||
32 | + | ||
33 | +"Work" shall mean the work of authorship, whether in Source or Object form, made | ||
34 | +available under the License, as indicated by a copyright notice that is included | ||
35 | +in or attached to the work (an example is provided in the Appendix below). | ||
36 | + | ||
37 | +"Derivative Works" shall mean any work, whether in Source or Object form, that | ||
38 | +is based on (or derived from) the Work and for which the editorial revisions, | ||
39 | +annotations, elaborations, or other modifications represent, as a whole, an | ||
40 | +original work of authorship. For the purposes of this License, Derivative Works | ||
41 | +shall not include works that remain separable from, or merely link (or bind by | ||
42 | +name) to the interfaces of, the Work and Derivative Works thereof. | ||
43 | + | ||
44 | +"Contribution" shall mean any work of authorship, including the original version | ||
45 | +of the Work and any modifications or additions to that Work or Derivative Works | ||
46 | +thereof, that is intentionally submitted to Licensor for inclusion in the Work | ||
47 | +by the copyright owner or by an individual or Legal Entity authorized to submit | ||
48 | +on behalf of the copyright owner. For the purposes of this definition, | ||
49 | +"submitted" means any form of electronic, verbal, or written communication sent | ||
50 | +to the Licensor or its representatives, including but not limited to | ||
51 | +communication on electronic mailing lists, source code control systems, and | ||
52 | +issue tracking systems that are managed by, or on behalf of, the Licensor for | ||
53 | +the purpose of discussing and improving the Work, but excluding communication | ||
54 | +that is conspicuously marked or otherwise designated in writing by the copyright | ||
55 | +owner as "Not a Contribution." | ||
56 | + | ||
57 | +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf | ||
58 | +of whom a Contribution has been received by Licensor and subsequently | ||
59 | +incorporated within the Work. | ||
60 | + | ||
61 | +2. Grant of Copyright License. | ||
62 | + | ||
63 | +Subject to the terms and conditions of this License, each Contributor hereby | ||
64 | +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||
65 | +irrevocable copyright license to reproduce, prepare Derivative Works of, | ||
66 | +publicly display, publicly perform, sublicense, and distribute the Work and such | ||
67 | +Derivative Works in Source or Object form. | ||
68 | + | ||
69 | +3. Grant of Patent License. | ||
70 | + | ||
71 | +Subject to the terms and conditions of this License, each Contributor hereby | ||
72 | +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||
73 | +irrevocable (except as stated in this section) patent license to make, have | ||
74 | +made, use, offer to sell, sell, import, and otherwise transfer the Work, where | ||
75 | +such license applies only to those patent claims licensable by such Contributor | ||
76 | +that are necessarily infringed by their Contribution(s) alone or by combination | ||
77 | +of their Contribution(s) with the Work to which such Contribution(s) was | ||
78 | +submitted. If You institute patent litigation against any entity (including a | ||
79 | +cross-claim or counterclaim in a lawsuit) alleging that the Work or a | ||
80 | +Contribution incorporated within the Work constitutes direct or contributory | ||
81 | +patent infringement, then any patent licenses granted to You under this License | ||
82 | +for that Work shall terminate as of the date such litigation is filed. | ||
83 | + | ||
84 | +4. Redistribution. | ||
85 | + | ||
86 | +You may reproduce and distribute copies of the Work or Derivative Works thereof | ||
87 | +in any medium, with or without modifications, and in Source or Object form, | ||
88 | +provided that You meet the following conditions: | ||
89 | + | ||
90 | +You must give any other recipients of the Work or Derivative Works a copy of | ||
91 | +this License; and | ||
92 | +You must cause any modified files to carry prominent notices stating that You | ||
93 | +changed the files; and | ||
94 | +You must retain, in the Source form of any Derivative Works that You distribute, | ||
95 | +all copyright, patent, trademark, and attribution notices from the Source form | ||
96 | +of the Work, excluding those notices that do not pertain to any part of the | ||
97 | +Derivative Works; and | ||
98 | +If the Work includes a "NOTICE" text file as part of its distribution, then any | ||
99 | +Derivative Works that You distribute must include a readable copy of the | ||
100 | +attribution notices contained within such NOTICE file, excluding those notices | ||
101 | +that do not pertain to any part of the Derivative Works, in at least one of the | ||
102 | +following places: within a NOTICE text file distributed as part of the | ||
103 | +Derivative Works; within the Source form or documentation, if provided along | ||
104 | +with the Derivative Works; or, within a display generated by the Derivative | ||
105 | +Works, if and wherever such third-party notices normally appear. The contents of | ||
106 | +the NOTICE file are for informational purposes only and do not modify the | ||
107 | +License. You may add Your own attribution notices within Derivative Works that | ||
108 | +You distribute, alongside or as an addendum to the NOTICE text from the Work, | ||
109 | +provided that such additional attribution notices cannot be construed as | ||
110 | +modifying the License. | ||
111 | +You may add Your own copyright statement to Your modifications and may provide | ||
112 | +additional or different license terms and conditions for use, reproduction, or | ||
113 | +distribution of Your modifications, or for any such Derivative Works as a whole, | ||
114 | +provided Your use, reproduction, and distribution of the Work otherwise complies | ||
115 | +with the conditions stated in this License. | ||
116 | + | ||
117 | +5. Submission of Contributions. | ||
118 | + | ||
119 | +Unless You explicitly state otherwise, any Contribution intentionally submitted | ||
120 | +for inclusion in the Work by You to the Licensor shall be under the terms and | ||
121 | +conditions of this License, without any additional terms or conditions. | ||
122 | +Notwithstanding the above, nothing herein shall supersede or modify the terms of | ||
123 | +any separate license agreement you may have executed with Licensor regarding | ||
124 | +such Contributions. | ||
125 | + | ||
126 | +6. Trademarks. | ||
127 | + | ||
128 | +This License does not grant permission to use the trade names, trademarks, | ||
129 | +service marks, or product names of the Licensor, except as required for | ||
130 | +reasonable and customary use in describing the origin of the Work and | ||
131 | +reproducing the content of the NOTICE file. | ||
132 | + | ||
133 | +7. Disclaimer of Warranty. | ||
134 | + | ||
135 | +Unless required by applicable law or agreed to in writing, Licensor provides the | ||
136 | +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, | ||
137 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, | ||
138 | +including, without limitation, any warranties or conditions of TITLE, | ||
139 | +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are | ||
140 | +solely responsible for determining the appropriateness of using or | ||
141 | +redistributing the Work and assume any risks associated with Your exercise of | ||
142 | +permissions under this License. | ||
143 | + | ||
144 | +8. Limitation of Liability. | ||
145 | + | ||
146 | +In no event and under no legal theory, whether in tort (including negligence), | ||
147 | +contract, or otherwise, unless required by applicable law (such as deliberate | ||
148 | +and grossly negligent acts) or agreed to in writing, shall any Contributor be | ||
149 | +liable to You for damages, including any direct, indirect, special, incidental, | ||
150 | +or consequential damages of any character arising as a result of this License or | ||
151 | +out of the use or inability to use the Work (including but not limited to | ||
152 | +damages for loss of goodwill, work stoppage, computer failure or malfunction, or | ||
153 | +any and all other commercial damages or losses), even if such Contributor has | ||
154 | +been advised of the possibility of such damages. | ||
155 | + | ||
156 | +9. Accepting Warranty or Additional Liability. | ||
157 | + | ||
158 | +While redistributing the Work or Derivative Works thereof, You may choose to | ||
159 | +offer, and charge a fee for, acceptance of support, warranty, indemnity, or | ||
160 | +other liability obligations and/or rights consistent with this License. However, | ||
161 | +in accepting such obligations, You may act only on Your own behalf and on Your | ||
162 | +sole responsibility, not on behalf of any other Contributor, and only if You | ||
163 | +agree to indemnify, defend, and hold each Contributor harmless for any liability | ||
164 | +incurred by, or claims asserted against, such Contributor by reason of your | ||
165 | +accepting any such warranty or additional liability. | ||
166 | + | ||
167 | +END OF TERMS AND CONDITIONS | ||
168 | + | ||
169 | +APPENDIX: How to apply the Apache License to your work | ||
170 | + | ||
171 | +To apply the Apache License to your work, attach the following boilerplate | ||
172 | +notice, with the fields enclosed by brackets "{}" replaced with your own | ||
173 | +identifying information. (Don't include the brackets!) The text should be | ||
174 | +enclosed in the appropriate comment syntax for the file format. We also | ||
175 | +recommend that a file or class name and description of purpose be included on | ||
176 | +the same "printed page" as the copyright notice for easier identification within | ||
177 | +third-party archives. | ||
178 | + | ||
179 | + Copyright 2017 Karson | ||
180 | + | ||
181 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
182 | + you may not use this file except in compliance with the License. | ||
183 | + You may obtain a copy of the License at | ||
184 | + | ||
185 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
186 | + | ||
187 | + Unless required by applicable law or agreed to in writing, software | ||
188 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
189 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
190 | + See the License for the specific language governing permissions and | ||
191 | + limitations under the License. |
README.md
0 → 100644
1 | +FastAdmin是一款基于ThinkPHP+Bootstrap的极速后台开发框架。 | ||
2 | + | ||
3 | + | ||
4 | +## 主要特性 | ||
5 | + | ||
6 | +* 基于`Auth`验证的权限管理系统 | ||
7 | + * 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置 | ||
8 | + * 支持单管理员多角色 | ||
9 | + * 支持管理子级数据或个人数据 | ||
10 | +* 强大的一键生成功能 | ||
11 | + * 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等 | ||
12 | + * 一键压缩打包JS和CSS文件,一键CDN静态资源部署 | ||
13 | + * 一键生成控制器菜单和规则 | ||
14 | + * 一键生成API接口文档 | ||
15 | +* 完善的前端功能组件开发 | ||
16 | + * 基于`AdminLTE`二次开发 | ||
17 | + * 基于`Bootstrap`开发,自适应手机、平板、PC | ||
18 | + * 基于`RequireJS`进行JS模块管理,按需加载 | ||
19 | + * 基于`Less`进行样式开发 | ||
20 | +* 强大的插件扩展功能,在线安装卸载升级插件 | ||
21 | +* 通用的会员模块和API模块 | ||
22 | +* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证 | ||
23 | +* 二级域名部署支持,同时域名支持绑定到应用插件 | ||
24 | +* 多语言支持,服务端及客户端支持 | ||
25 | +* 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩 | ||
26 | +* 支持表格固定列、固定表头、跨页选择、Excel导出、模板渲染等功能 | ||
27 | +* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)、[博客](https://www.fastadmin.net/store/blog.html)、[知识付费问答](https://www.fastadmin.net/store/ask.html)、[在线投票系统](https://www.fastadmin.net/store/vote.html)、[B2C商城](https://www.fastadmin.net/store/shopro.html)、[B2B2C商城](https://www.fastadmin.net/store/wanlshop.html)) | ||
28 | +* 支持CMS、博客、知识付费问答无缝整合[Xunsearch全文搜索](https://www.fastadmin.net/store/xunsearch.html) | ||
29 | +* 第三方小程序支持([CMS小程序](https://www.fastadmin.net/store/cms.html)、[预订小程序](https://www.fastadmin.net/store/ball.html)、[问答小程序](https://www.fastadmin.net/store/ask.html)、[点餐小程序](https://www.fastadmin.net/store/unidrink.html)、[B2C小程序](https://www.fastadmin.net/store/shopro.html)、[B2B2C小程序](https://www.fastadmin.net/store/wanlshop.html)、[博客小程序](https://www.fastadmin.net/store/blog.html)) | ||
30 | +* 整合第三方短信接口(阿里云、腾讯云短信) | ||
31 | +* 无缝整合第三方云存储(七牛云、阿里云OSS、又拍云)功能,支持云储存分片上传 | ||
32 | +* 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器) | ||
33 | +* 第三方登录(QQ、微信、微博)整合 | ||
34 | +* 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付 | ||
35 | +* 丰富的插件应用市场 | ||
36 | + | ||
37 | +## 安装使用 | ||
38 | + | ||
39 | +https://doc.fastadmin.net | ||
40 | + | ||
41 | +## 在线演示 | ||
42 | + | ||
43 | +https://demo.fastadmin.net | ||
44 | + | ||
45 | +用户名:admin | ||
46 | + | ||
47 | +密 码:123456 | ||
48 | + | ||
49 | +提 示:演示站数据无法进行修改,请下载源码安装体验全部功能 | ||
50 | + | ||
51 | +## 界面截图 | ||
52 | +![控制台](https://images.gitee.com/uploads/images/2020/0929/202947_8db2d281_10933.gif "控制台") | ||
53 | + | ||
54 | +## 问题反馈 | ||
55 | + | ||
56 | +在使用中有任何问题,请使用以下联系方式联系我们 | ||
57 | + | ||
58 | +交流社区: https://ask.fastadmin.net | ||
59 | + | ||
60 | +QQ 1 群(满)、QQ 2 群(满)、QQ 3 群(满)、QQ 4 群(满)、QQ 5 群(满)、QQ 6 群(满)、[QQ 7 群](https://www.fastadmin.net/goto/qun)。 | ||
61 | + | ||
62 | +Github: https://github.com/karsonzhang/fastadmin | ||
63 | + | ||
64 | +Gitee: https://gitee.com/karson/fastadmin | ||
65 | + | ||
66 | +## 特别鸣谢 | ||
67 | + | ||
68 | +感谢以下的项目,排名不分先后 | ||
69 | + | ||
70 | +ThinkPHP:http://www.thinkphp.cn | ||
71 | + | ||
72 | +AdminLTE:https://adminlte.io | ||
73 | + | ||
74 | +Bootstrap:http://getbootstrap.com | ||
75 | + | ||
76 | +jQuery:http://jquery.com | ||
77 | + | ||
78 | +Bootstrap-table:https://github.com/wenzhixin/bootstrap-table | ||
79 | + | ||
80 | +Nice-validator: https://validator.niceue.com | ||
81 | + | ||
82 | +SelectPage: https://github.com/TerryZ/SelectPage | ||
83 | + | ||
84 | +Layer: https://layuion.com/layer/ | ||
85 | + | ||
86 | +DropzoneJS: https://www.dropzonejs.com | ||
87 | + | ||
88 | + | ||
89 | +## 版权信息 | ||
90 | + | ||
91 | +FastAdmin遵循Apache2开源协议发布,并提供免费使用。 | ||
92 | + | ||
93 | +本项目包含的第三方源码和二进制文件之版权信息另行标注。 | ||
94 | + | ||
95 | +版权所有Copyright © 2017-2022 by FastAdmin (https://www.fastadmin.net) | ||
96 | + | ||
97 | +All rights reserved。 |
addons/.gitkeep
0 → 100644
1 | + |
addons/.htaccess
0 → 100644
1 | +deny from all |
addons/command/.addonrc
0 → 100644
1 | +{"files":["application\\admin\\controller\\Command.php","application\\admin\\lang\\zh-cn\\command.php","application\\admin\\model\\Command.php","application\\admin\\validate\\Command.php","application\\admin\\view\\command\\add.html","application\\admin\\view\\command\\detail.html","application\\admin\\view\\command\\index.html","public\\assets\\js\\backend\\command.js"],"license":"regular","licenseto":"10789","licensekey":"ldZibow0fkR8sBOz 93K3lIjzifgBCUnAQxVItA==","domains":["campus.cn"],"licensecodes":[],"validations":["c51a74160ee0aa6a19729b33d4a9fcdc"],"menus":["command","command\/index","command\/add","command\/detail","command\/command","command\/execute","command\/del","command\/multi"]} |
addons/command/Command.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\command; | ||
4 | + | ||
5 | +use app\common\library\Menu; | ||
6 | +use think\Addons; | ||
7 | + | ||
8 | +/** | ||
9 | + * 在线命令插件 | ||
10 | + */ | ||
11 | +class Command extends Addons | ||
12 | +{ | ||
13 | + | ||
14 | + /** | ||
15 | + * 插件安装方法 | ||
16 | + * @return bool | ||
17 | + */ | ||
18 | + public function install() | ||
19 | + { | ||
20 | + $menu = [ | ||
21 | + [ | ||
22 | + 'name' => 'command', | ||
23 | + 'title' => '在线命令管理', | ||
24 | + 'icon' => 'fa fa-terminal', | ||
25 | + 'sublist' => [ | ||
26 | + ['name' => 'command/index', 'title' => '查看'], | ||
27 | + ['name' => 'command/add', 'title' => '添加'], | ||
28 | + ['name' => 'command/detail', 'title' => '详情'], | ||
29 | + ['name' => 'command/command', 'title' => '生成并执行命令'], | ||
30 | + ['name' => 'command/execute', 'title' => '再次执行命令'], | ||
31 | + ['name' => 'command/del', 'title' => '删除'], | ||
32 | + ['name' => 'command/multi', 'title' => '批量更新'], | ||
33 | + ] | ||
34 | + ] | ||
35 | + ]; | ||
36 | + Menu::create($menu); | ||
37 | + return true; | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * 插件卸载方法 | ||
42 | + * @return bool | ||
43 | + */ | ||
44 | + public function uninstall() | ||
45 | + { | ||
46 | + Menu::delete('command'); | ||
47 | + return true; | ||
48 | + } | ||
49 | + | ||
50 | + /** | ||
51 | + * 插件启用方法 | ||
52 | + * @return bool | ||
53 | + */ | ||
54 | + public function enable() | ||
55 | + { | ||
56 | + Menu::enable('command'); | ||
57 | + return true; | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * 插件禁用方法 | ||
62 | + * @return bool | ||
63 | + */ | ||
64 | + public function disable() | ||
65 | + { | ||
66 | + Menu::disable('command'); | ||
67 | + return true; | ||
68 | + } | ||
69 | + | ||
70 | +} |
addons/command/config.php
0 → 100644
addons/command/controller/Index.php
0 → 100644
addons/command/info.ini
0 → 100644
addons/command/install.sql
0 → 100644
1 | +CREATE TABLE IF NOT EXISTS `__PREFIX__command` ( | ||
2 | + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', | ||
3 | + `type` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '类型', | ||
4 | + `params` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '参数', | ||
5 | + `command` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '命令', | ||
6 | + `content` text COMMENT '返回结果', | ||
7 | + `executetime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '执行时间', | ||
8 | + `createtime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '创建时间', | ||
9 | + `updatetime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '更新时间', | ||
10 | + `status` enum('successed','failured') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'failured' COMMENT '状态', | ||
11 | + PRIMARY KEY (`id`) USING BTREE | ||
12 | +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线命令表'; |
addons/command/library/Output.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\command\library; | ||
4 | + | ||
5 | +/** | ||
6 | + * Class Output | ||
7 | + */ | ||
8 | +class Output extends \think\console\Output | ||
9 | +{ | ||
10 | + | ||
11 | + protected $message = []; | ||
12 | + | ||
13 | + public function __construct($driver = 'console') | ||
14 | + { | ||
15 | + parent::__construct($driver); | ||
16 | + } | ||
17 | + | ||
18 | + protected function block($style, $message) | ||
19 | + { | ||
20 | + $this->message[] = $message; | ||
21 | + } | ||
22 | + | ||
23 | + public function getMessage() | ||
24 | + { | ||
25 | + return $this->message; | ||
26 | + } | ||
27 | + | ||
28 | +} |
addons/third/.addonrc
0 → 100644
1 | +{"files":["application\\admin\\controller\\Third.php","application\\admin\\lang\\zh-cn\\third.php","application\\admin\\model\\Third.php","application\\admin\\validate\\Third.php","application\\admin\\view\\third\\index.html","application\\index\\controller\\Third.php","application\\index\\view\\third\\prepare.html","public\\assets\\js\\backend\\third.js"],"license":"regular","licenseto":"10789","licensekey":"SZzlcuMI09VyfFRb uyBhMrlq5+kj9X0+fU4DIw==","domains":["campus.cn"],"licensecodes":[],"validations":["c51a74160ee0aa6a19729b33d4a9fcdc"],"menus":["third","third\/index","third\/del"]} |
addons/third/Third.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third; | ||
4 | + | ||
5 | +use app\common\library\Auth; | ||
6 | +use app\common\library\Menu; | ||
7 | +use think\Addons; | ||
8 | +use think\Request; | ||
9 | +use think\Session; | ||
10 | + | ||
11 | +/** | ||
12 | + * 第三方登录 | ||
13 | + */ | ||
14 | +class Third extends Addons | ||
15 | +{ | ||
16 | + | ||
17 | + protected static $html = ['register' => '', 'profile' => '']; | ||
18 | + | ||
19 | + /** | ||
20 | + * 插件安装方法 | ||
21 | + * @return bool | ||
22 | + */ | ||
23 | + public function install() | ||
24 | + { | ||
25 | + $menu = [ | ||
26 | + [ | ||
27 | + 'name' => 'third', | ||
28 | + 'title' => '第三方登录管理', | ||
29 | + 'icon' => 'fa fa-users', | ||
30 | + 'sublist' => [ | ||
31 | + [ | ||
32 | + "name" => "third/index", | ||
33 | + "title" => "查看" | ||
34 | + ], | ||
35 | + [ | ||
36 | + "name" => "third/del", | ||
37 | + "title" => "删除" | ||
38 | + ] | ||
39 | + ] | ||
40 | + ] | ||
41 | + ]; | ||
42 | + Menu::create($menu); | ||
43 | + return true; | ||
44 | + } | ||
45 | + | ||
46 | + /** | ||
47 | + * 插件卸载方法 | ||
48 | + * @return bool | ||
49 | + */ | ||
50 | + public function uninstall() | ||
51 | + { | ||
52 | + Menu::delete("third"); | ||
53 | + return true; | ||
54 | + } | ||
55 | + | ||
56 | + /** | ||
57 | + * 插件启用方法 | ||
58 | + * @return bool | ||
59 | + */ | ||
60 | + public function enable() | ||
61 | + { | ||
62 | + Menu::enable("third"); | ||
63 | + return true; | ||
64 | + } | ||
65 | + | ||
66 | + /** | ||
67 | + * 插件禁用方法 | ||
68 | + * @return bool | ||
69 | + */ | ||
70 | + public function disable() | ||
71 | + { | ||
72 | + Menu::disable("third"); | ||
73 | + return true; | ||
74 | + } | ||
75 | + | ||
76 | + /** | ||
77 | + * 删除第三方登录表的关联数据 | ||
78 | + */ | ||
79 | + public function userDeleteSuccessed(\app\common\model\User $user) | ||
80 | + { | ||
81 | + \addons\third\model\Third::where('user_id', $user->id)->delete(); | ||
82 | + } | ||
83 | + | ||
84 | + /** | ||
85 | + * 移除第三方登录信息 | ||
86 | + */ | ||
87 | + public function userLogoutSuccessed(\app\common\model\User $user) | ||
88 | + { | ||
89 | + Session::delete(["wechat-userinfo", "qq-userinfo", "weibo-userinfo"]); | ||
90 | + } | ||
91 | + | ||
92 | + /** | ||
93 | + * 模块开始 | ||
94 | + */ | ||
95 | + public function moduleInit() | ||
96 | + { | ||
97 | + $config = $this->getConfig(); | ||
98 | + if (!$config['status']) { | ||
99 | + return; | ||
100 | + } | ||
101 | + $request = Request::instance(); | ||
102 | + | ||
103 | + $module = strtolower($request->module()); | ||
104 | + $controller = strtolower($request->controller()); | ||
105 | + $action = strtolower($request->action()); | ||
106 | + if ($module !== 'index' || $controller !== 'user' || !in_array($action, ['login', 'register'])) { | ||
107 | + return; | ||
108 | + } | ||
109 | + $url = $request->get('url', $request->server('HTTP_REFERER', '', 'trim'), 'trim'); | ||
110 | + $data = [ | ||
111 | + 'status' => isset($config['status']) ? explode(',', $config['status']) : [], | ||
112 | + 'url' => $url | ||
113 | + ]; | ||
114 | + self::$html['register'] = $this->view->fetch('view/hook/user_register_end', $data); | ||
115 | + } | ||
116 | + | ||
117 | + /** | ||
118 | + * 方法开始 | ||
119 | + */ | ||
120 | + public function actionBegin() | ||
121 | + { | ||
122 | + $config = $this->getConfig(); | ||
123 | + if (!$config['status']) { | ||
124 | + return; | ||
125 | + } | ||
126 | + $request = Request::instance(); | ||
127 | + | ||
128 | + $module = strtolower($request->module()); | ||
129 | + $controller = strtolower($request->controller()); | ||
130 | + $action = strtolower($request->action()); | ||
131 | + if ($module !== 'index' || $controller !== 'user' || !in_array($action, ['profile'])) { | ||
132 | + return; | ||
133 | + } | ||
134 | + $platform = \addons\third\model\Third::where('user_id', Auth::instance()->id)->column('platform'); | ||
135 | + $data = [ | ||
136 | + 'status' => isset($config['status']) ? explode(',', $config['status']) : [], | ||
137 | + 'platform' => $platform | ||
138 | + ]; | ||
139 | + self::$html['profile'] = $this->view->fetch('view/hook/user_profile_end', $data); | ||
140 | + } | ||
141 | + | ||
142 | + /** | ||
143 | + * 配置 | ||
144 | + * @param $params | ||
145 | + */ | ||
146 | + public function configInit(&$params) | ||
147 | + { | ||
148 | + // 兼容旧版本FastAdmin | ||
149 | + $config = $this->getConfig(); | ||
150 | + $module = strtolower(request()->module()); | ||
151 | + $controller = strtolower(request()->controller()); | ||
152 | + $action = strtolower(request()->action()); | ||
153 | + $loginhtml = version_compare(config('fastadmin.version'), '1.3.0', '<') > 0 && $module === 'index' && $controller === 'user' && in_array($action, ['login', 'register']) ? self::$html['register'] : ''; | ||
154 | + $params['third'] = ['status' => explode(',', $config['status']), 'loginhtml' => $loginhtml]; | ||
155 | + } | ||
156 | + | ||
157 | + /** | ||
158 | + * HTML替换 | ||
159 | + */ | ||
160 | + public function viewFilter(& $content) | ||
161 | + { | ||
162 | + $config = $this->getConfig(); | ||
163 | + if (!$config['status']) { | ||
164 | + return; | ||
165 | + } | ||
166 | + $request = Request::instance(); | ||
167 | + | ||
168 | + $module = strtolower($request->module()); | ||
169 | + $controller = strtolower($request->controller()); | ||
170 | + $action = strtolower($request->action()); | ||
171 | + if ($module !== 'index' || $controller !== 'user') { | ||
172 | + return; | ||
173 | + } | ||
174 | + if (in_array($action, ['login', 'register'])) { | ||
175 | + $html = self::$html['register'] ?? ''; | ||
176 | + $content = str_replace(['<!--@IndexRegisterFormEnd-->', '<!--@IndexLoginFormEnd-->'], $html, $content); | ||
177 | + } elseif ($action === 'profile') { | ||
178 | + $html = self::$html['profile'] ?? ''; | ||
179 | + $content = str_replace("<div class=\"form-group normal-footer\">", "{$html}<div class=\"form-group normal-footer\">", $content); | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | +} |
addons/third/bootstrap.js
0 → 100644
addons/third/config.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + [ | ||
5 | + 'name' => 'qq', | ||
6 | + 'title' => 'QQ', | ||
7 | + 'type' => 'array', | ||
8 | + 'content' => [ | ||
9 | + 'app_id' => '', | ||
10 | + 'app_secret' => '', | ||
11 | + 'scope' => 'get_user_info', | ||
12 | + ], | ||
13 | + 'value' => [ | ||
14 | + 'app_id' => '100000000', | ||
15 | + 'app_secret' => '', | ||
16 | + 'scope' => 'get_user_info', | ||
17 | + ], | ||
18 | + 'rule' => 'required', | ||
19 | + 'msg' => '', | ||
20 | + 'tip' => '', | ||
21 | + 'ok' => '', | ||
22 | + 'extend' => '', | ||
23 | + ], | ||
24 | + [ | ||
25 | + 'name' => 'wechat', | ||
26 | + 'title' => '微信公众号', | ||
27 | + 'type' => 'array', | ||
28 | + 'content' => [ | ||
29 | + 'app_id' => '', | ||
30 | + 'app_secret' => '', | ||
31 | + 'callback' => '', | ||
32 | + 'scope' => 'snsapi_base', | ||
33 | + ], | ||
34 | + 'value' => [ | ||
35 | + 'app_id' => '100000000', | ||
36 | + 'app_secret' => '123456', | ||
37 | + 'scope' => 'snsapi_userinfo', | ||
38 | + ], | ||
39 | + 'rule' => 'required', | ||
40 | + 'msg' => '', | ||
41 | + 'tip' => '', | ||
42 | + 'ok' => '', | ||
43 | + 'extend' => '', | ||
44 | + ], | ||
45 | + [ | ||
46 | + 'name' => 'weibo', | ||
47 | + 'title' => '微博', | ||
48 | + 'type' => 'array', | ||
49 | + 'content' => [ | ||
50 | + 'app_id' => '', | ||
51 | + 'app_secret' => '', | ||
52 | + 'scope' => 'get_user_info', | ||
53 | + ], | ||
54 | + 'value' => [ | ||
55 | + 'app_id' => '100000000', | ||
56 | + 'app_secret' => '123456', | ||
57 | + 'scope' => 'get_user_info', | ||
58 | + ], | ||
59 | + 'rule' => 'required', | ||
60 | + 'msg' => '', | ||
61 | + 'tip' => '', | ||
62 | + 'ok' => '', | ||
63 | + 'extend' => '', | ||
64 | + ], | ||
65 | + [ | ||
66 | + 'name' => 'bindaccount', | ||
67 | + 'title' => '账号绑定', | ||
68 | + 'type' => 'radio', | ||
69 | + 'content' => [ | ||
70 | + 1 => '开启', | ||
71 | + 0 => '关闭', | ||
72 | + ], | ||
73 | + 'value' => '1', | ||
74 | + 'rule' => 'required', | ||
75 | + 'msg' => '', | ||
76 | + 'tip' => '', | ||
77 | + 'ok' => 'PC端是否开启账号绑定,关闭则自动创建账号', | ||
78 | + 'extend' => '', | ||
79 | + ], | ||
80 | + [ | ||
81 | + 'name' => 'status', | ||
82 | + 'title' => 'PC端第三方登录开关', | ||
83 | + 'type' => 'checkbox', | ||
84 | + 'content' => [ | ||
85 | + 'qq' => 'QQ', | ||
86 | + 'wechat' => '微信', | ||
87 | + 'weibo' => '微博', | ||
88 | + ], | ||
89 | + 'value' => 'qq,wechat,weibo', | ||
90 | + 'rule' => '', | ||
91 | + 'msg' => '', | ||
92 | + 'tip' => '', | ||
93 | + 'ok' => 'PC端第三方登录开关', | ||
94 | + 'extend' => '', | ||
95 | + ], | ||
96 | + [ | ||
97 | + 'name' => 'rewrite', | ||
98 | + 'title' => '伪静态', | ||
99 | + 'type' => 'array', | ||
100 | + 'content' => [], | ||
101 | + 'value' => [ | ||
102 | + 'index/index' => '/third$', | ||
103 | + 'index/connect' => '/third/connect/[:platform]', | ||
104 | + 'index/callback' => '/third/callback/[:platform]', | ||
105 | + 'index/bind' => '/third/bind/[:platform]', | ||
106 | + 'index/unbind' => '/third/unbind/[:platform]', | ||
107 | + ], | ||
108 | + 'rule' => 'required', | ||
109 | + 'msg' => '', | ||
110 | + 'tip' => '', | ||
111 | + 'ok' => '', | ||
112 | + 'extend' => '', | ||
113 | + ], | ||
114 | +]; |
addons/third/controller/Api.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\controller; | ||
4 | + | ||
5 | +use addons\third\library\Application; | ||
6 | +use app\common\controller\Api as commonApi; | ||
7 | +use addons\third\library\Service; | ||
8 | +use addons\third\model\Third; | ||
9 | +use app\common\library\Sms; | ||
10 | +use fast\Random; | ||
11 | +use think\Lang; | ||
12 | +use think\Config; | ||
13 | +use think\Session; | ||
14 | +use think\Validate; | ||
15 | + | ||
16 | +/** | ||
17 | + * 第三方登录插件 | ||
18 | + */ | ||
19 | +class Api extends commonApi | ||
20 | +{ | ||
21 | + protected $noNeedLogin = ['getAuthUrl', 'callback', 'account']; // 无需登录即可访问的方法,同时也无需鉴权了 | ||
22 | + protected $noNeedRight = ['*']; // 无需鉴权即可访问的方法 | ||
23 | + | ||
24 | + protected $app = null; | ||
25 | + protected $options = []; | ||
26 | + protected $config = null; | ||
27 | + | ||
28 | + public function _initialize() | ||
29 | + { | ||
30 | + //跨域检测 | ||
31 | + check_cors_request(); | ||
32 | + //设置session_id | ||
33 | + Config::set('session.id', $this->request->server("HTTP_SID")); | ||
34 | + | ||
35 | + parent::_initialize(); | ||
36 | + $this->config = get_addon_config('third'); | ||
37 | + $this->app = new Application($this->config); | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * H5获取授权链接 | ||
42 | + * @return void | ||
43 | + */ | ||
44 | + public function getAuthUrl() | ||
45 | + { | ||
46 | + $url = $this->request->param('url', '', 'trim'); | ||
47 | + $platform = $this->request->param('platform'); | ||
48 | + if (!$url || !$platform || !isset($this->config[$platform])) { | ||
49 | + $this->error('参数错误'); | ||
50 | + } | ||
51 | + $this->config[$platform]['callback'] = $url; | ||
52 | + $this->app = new Application($this->config); // | ||
53 | + if (!$this->app->{$platform}) { | ||
54 | + $this->error(__('Invalid parameters')); | ||
55 | + } | ||
56 | + $this->success('', $this->app->{$platform}->getAuthorizeUrl()); | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * 公众号:wechat 授权回调的请求【非第三方,自己的前端请求】 | ||
61 | + * @return void | ||
62 | + */ | ||
63 | + public function callback() | ||
64 | + { | ||
65 | + | ||
66 | + $platform = $this->request->param('platform'); | ||
67 | + if (!$this->app->{$platform}) { | ||
68 | + $this->error(__('Invalid parameters')); | ||
69 | + } | ||
70 | + $userinfo = $this->app->{$platform}->getUserInfo($this->request->param()); | ||
71 | + if (!$userinfo) { | ||
72 | + $this->error(__('操作失败')); | ||
73 | + } | ||
74 | + $userinfo['apptype'] = 'mp'; | ||
75 | + $userinfo['platform'] = $platform; | ||
76 | + | ||
77 | + $third = [ | ||
78 | + 'avatar' => $userinfo['userinfo']['avatar'], | ||
79 | + 'nickname' => $userinfo['userinfo']['nickname'] | ||
80 | + ]; | ||
81 | + | ||
82 | + $user = null; | ||
83 | + if ($this->auth->isLogin() || Service::isBindThird($userinfo['platform'], $userinfo['openid'], $userinfo['apptype'], $userinfo['unionid'])) { | ||
84 | + Service::connect($userinfo['platform'], $userinfo); | ||
85 | + $user = $this->auth->getUserinfo(); | ||
86 | + } else { | ||
87 | + $user = false; | ||
88 | + Session::set('third-userinfo', $userinfo); | ||
89 | + } | ||
90 | + $this->success("授权成功!", ['user' => $user, 'third' => $third]); | ||
91 | + } | ||
92 | + | ||
93 | + /** | ||
94 | + * 登录或创建账号 | ||
95 | + */ | ||
96 | + public function account() | ||
97 | + { | ||
98 | + | ||
99 | + if ($this->request->isPost()) { | ||
100 | + $params = Session::get('third-userinfo'); | ||
101 | + $mobile = $this->request->post('mobile', ''); | ||
102 | + $code = $this->request->post('code', $this->request->post('captcha')); | ||
103 | + $token = $this->request->post('__token__'); | ||
104 | + $rule = [ | ||
105 | + 'mobile' => 'require|regex:/^1\d{10}$/', | ||
106 | + '__token__' => 'require|token', | ||
107 | + ]; | ||
108 | + $msg = [ | ||
109 | + 'mobile' => 'Mobile is incorrect', | ||
110 | + ]; | ||
111 | + $data = [ | ||
112 | + 'mobile' => $mobile, | ||
113 | + '__token__' => $token, | ||
114 | + ]; | ||
115 | + $ret = Sms::check($mobile, $code, 'bind'); | ||
116 | + if (!$ret) { | ||
117 | + $this->error(__('验证码错误')); | ||
118 | + } | ||
119 | + $validate = new Validate($rule, $msg); | ||
120 | + $result = $validate->check($data); | ||
121 | + if (!$result) { | ||
122 | + $this->error(__($validate->getError()), ['__token__' => $this->request->token()]); | ||
123 | + } | ||
124 | + | ||
125 | + $userinfo = \app\common\model\User::where('mobile', $mobile)->find(); | ||
126 | + if ($userinfo) { | ||
127 | + $result = $this->auth->direct($userinfo->id); | ||
128 | + } else { | ||
129 | + $result = $this->auth->register($mobile, Random::alnum(), '', $mobile, isset($params['userinfo']) ? $params['userinfo'] : []); | ||
130 | + } | ||
131 | + | ||
132 | + if ($result) { | ||
133 | + Service::connect($params['platform'], $params); | ||
134 | + $this->success(__('绑定账号成功'), ['userinfo' => $this->auth->getUserinfo()]); | ||
135 | + } else { | ||
136 | + $this->error($this->auth->getError(), ['__token__' => $this->request->token()]); | ||
137 | + } | ||
138 | + } | ||
139 | + } | ||
140 | +} |
addons/third/controller/Index.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\controller; | ||
4 | + | ||
5 | +use addons\third\library\Application; | ||
6 | +use addons\third\library\Service; | ||
7 | +use addons\third\model\Third; | ||
8 | +use think\addons\Controller; | ||
9 | +use think\Config; | ||
10 | +use think\Cookie; | ||
11 | +use think\Hook; | ||
12 | +use think\Lang; | ||
13 | +use think\Session; | ||
14 | + | ||
15 | +/** | ||
16 | + * 第三方登录插件 | ||
17 | + */ | ||
18 | +class Index extends Controller | ||
19 | +{ | ||
20 | + protected $app = null; | ||
21 | + protected $options = []; | ||
22 | + | ||
23 | + public function _initialize() | ||
24 | + { | ||
25 | + parent::_initialize(); | ||
26 | + $config = get_addon_config('third'); | ||
27 | + $this->app = new Application($config); | ||
28 | + } | ||
29 | + | ||
30 | + /** | ||
31 | + * 插件首页 | ||
32 | + */ | ||
33 | + public function index() | ||
34 | + { | ||
35 | + if (!\app\admin\library\Auth::instance()->id) { | ||
36 | + $this->error('当前插件暂无前台页面'); | ||
37 | + } | ||
38 | + $platformList = []; | ||
39 | + if ($this->auth->id) { | ||
40 | + $platformList = Third::where('user_id', $this->auth->id)->column('platform'); | ||
41 | + } | ||
42 | + $this->view->assign('platformList', $platformList); | ||
43 | + return $this->view->fetch(); | ||
44 | + } | ||
45 | + | ||
46 | + /** | ||
47 | + * 发起授权 | ||
48 | + */ | ||
49 | + public function connect() | ||
50 | + { | ||
51 | + $platform = $this->request->param('platform'); | ||
52 | + $config = get_addon_config('third'); | ||
53 | + if (!$config['status']) { | ||
54 | + $this->error("第三方登录已关闭"); | ||
55 | + } | ||
56 | + $status = explode(',', $config['status']); | ||
57 | + if (!in_array($platform, $status)) { | ||
58 | + $this->error("该登录方式已关闭"); | ||
59 | + } | ||
60 | + | ||
61 | + $url = $this->request->request('url', $this->request->server('HTTP_REFERER', '/', 'trim'), 'trim'); | ||
62 | + if (!$this->app->{$platform}) { | ||
63 | + $this->error('参数错误'); | ||
64 | + } | ||
65 | + if ($url) { | ||
66 | + Session::set("redirecturl", $url); | ||
67 | + } | ||
68 | + // 跳转到登录授权页面 | ||
69 | + $this->redirect($this->app->{$platform}->getAuthorizeUrl()); | ||
70 | + return; | ||
71 | + } | ||
72 | + | ||
73 | + /** | ||
74 | + * 通知回调 | ||
75 | + */ | ||
76 | + public function callback() | ||
77 | + { | ||
78 | + $auth = $this->auth; | ||
79 | + | ||
80 | + //监听注册登录注销的事件 | ||
81 | + Hook::add('user_login_successed', function ($user) use ($auth) { | ||
82 | + $expire = input('post.keeplogin') ? 30 * 86400 : 0; | ||
83 | + Cookie::set('uid', $user->id, $expire); | ||
84 | + Cookie::set('token', $auth->getToken(), $expire); | ||
85 | + }); | ||
86 | + Hook::add('user_register_successed', function ($user) use ($auth) { | ||
87 | + Cookie::set('uid', $user->id); | ||
88 | + Cookie::set('token', $auth->getToken()); | ||
89 | + }); | ||
90 | + Hook::add('user_logout_successed', function ($user) use ($auth) { | ||
91 | + Cookie::delete('uid'); | ||
92 | + Cookie::delete('token'); | ||
93 | + }); | ||
94 | + $platform = $this->request->param('platform'); | ||
95 | + | ||
96 | + // 成功后返回之前页面,但忽略登录/注册页面 | ||
97 | + $url = Session::has("redirecturl") ? Session::pull("redirecturl") : url('index/user/index'); | ||
98 | + $url = preg_match("/\/user\/(register|login|resetpwd)/i", $url) ? url('index/user/index') : $url; | ||
99 | + | ||
100 | + // 授权成功后的回调 | ||
101 | + $userinfo = $this->app->{$platform}->getUserInfo(); | ||
102 | + if (!$userinfo) { | ||
103 | + $this->error(__('操作失败'), $url); | ||
104 | + } | ||
105 | + | ||
106 | + Session::set("{$platform}-userinfo", $userinfo); | ||
107 | + //判断是否启用账号绑定 | ||
108 | + $third = Third::get(['platform' => $platform, 'openid' => $userinfo['openid']]); | ||
109 | + if (!$third) { | ||
110 | + $config = get_addon_config('third'); | ||
111 | + //要求绑定账号或会员当前是登录状态 | ||
112 | + if ($config['bindaccount'] || $this->auth->id) { | ||
113 | + $this->redirect(url('index/third/prepare') . "?" . http_build_query(['platform' => $platform, 'url' => $url])); | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | + //直接登录 | ||
118 | + $loginret = Service::connect($platform, $userinfo); | ||
119 | + if ($loginret) { | ||
120 | + $this->redirect($url); | ||
121 | + } else { | ||
122 | + $this->error("登录失败,请返回重试", $url); | ||
123 | + } | ||
124 | + } | ||
125 | + | ||
126 | + /** | ||
127 | + * 绑定账号 | ||
128 | + */ | ||
129 | + public function bind() | ||
130 | + { | ||
131 | + $platform = $this->request->request('platform', $this->request->param('platform', '')); | ||
132 | + $url = $this->request->get('url', $this->request->server('HTTP_REFERER', '', 'trim'), 'trim'); | ||
133 | + $redirecturl = url("index/third/bind") . "?" . http_build_query(['platform' => $platform, 'url' => $url]); | ||
134 | + $this->redirect($redirecturl); | ||
135 | + return; | ||
136 | + } | ||
137 | + | ||
138 | + /** | ||
139 | + * 解绑账号 | ||
140 | + */ | ||
141 | + public function unbind() | ||
142 | + { | ||
143 | + $platform = $this->request->request('platform', $this->request->param('platform', '')); | ||
144 | + $url = $this->request->get('url', $this->request->server('HTTP_REFERER', '', 'trim'), 'trim'); | ||
145 | + $redirecturl = url("index/third/unbind") . "?" . http_build_query(['platform' => $platform, 'url' => $url]); | ||
146 | + $this->redirect($redirecturl); | ||
147 | + return; | ||
148 | + } | ||
149 | + | ||
150 | +} |
addons/third/info.ini
0 → 100644
addons/third/install.sql
0 → 100644
1 | + | ||
2 | +CREATE TABLE IF NOT EXISTS `__PREFIX__third` ( | ||
3 | + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', | ||
4 | + `user_id` int(10) unsigned DEFAULT '0' COMMENT '会员ID', | ||
5 | + `platform` varchar(30) DEFAULT '' COMMENT '第三方应用', | ||
6 | + `apptype` varchar(50) DEFAULT '' COMMENT '应用类型', | ||
7 | + `unionid` varchar(100) DEFAULT '' COMMENT '第三方UNIONID', | ||
8 | + `openid` varchar(100) DEFAULT '' COMMENT '第三方OPENID', | ||
9 | + `openname` varchar(100) DEFAULT '' COMMENT '第三方会员昵称', | ||
10 | + `access_token` varchar(255) NULL DEFAULT '' COMMENT 'AccessToken', | ||
11 | + `refresh_token` varchar(255) DEFAULT 'RefreshToken', | ||
12 | + `expires_in` int(10) unsigned DEFAULT '0' COMMENT '有效期', | ||
13 | + `createtime` bigint(16) unsigned DEFAULT NULL COMMENT '创建时间', | ||
14 | + `updatetime` bigint(16) unsigned DEFAULT NULL COMMENT '更新时间', | ||
15 | + `logintime` bigint(16) unsigned DEFAULT NULL COMMENT '登录时间', | ||
16 | + `expiretime` bigint(16) unsigned DEFAULT NULL COMMENT '过期时间', | ||
17 | + PRIMARY KEY (`id`), | ||
18 | + UNIQUE KEY `platform` (`platform`,`openid`), | ||
19 | + KEY `user_id` (`user_id`,`platform`), | ||
20 | + KEY `unionid` (`platform`,`unionid`) | ||
21 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='第三方登录表'; | ||
22 | + | ||
23 | +ALTER TABLE `__PREFIX__third` ADD COLUMN `apptype` varchar(50) NULL DEFAULT '' COMMENT '应用类型' AFTER `platform`; | ||
24 | + | ||
25 | +ALTER TABLE `__PREFIX__third` ADD COLUMN `unionid` varchar(100) NULL DEFAULT '' COMMENT '第三方UnionID' AFTER `apptype`; | ||
26 | +ALTER TABLE `__PREFIX__third` ADD INDEX `unionid`(`platform`, `unionid`); | ||
27 | + | ||
28 | +ALTER TABLE `__PREFIX__third` CHARACTER SET = utf8mb4, COLLATE = utf8mb4_general_ci; | ||
29 | +ALTER TABLE `__PREFIX__third` MODIFY COLUMN `openname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '第三方会员昵称' AFTER `unionid`; | ||
30 | + |
addons/third/library/Application.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\library; | ||
4 | + | ||
5 | +class Application | ||
6 | +{ | ||
7 | + | ||
8 | + /** | ||
9 | + * 配置信息 | ||
10 | + * @var array | ||
11 | + */ | ||
12 | + private $config = []; | ||
13 | + | ||
14 | + /** | ||
15 | + * 服务提供者 | ||
16 | + * @var array | ||
17 | + */ | ||
18 | + private $providers = [ | ||
19 | + 'qq' => 'Qq', | ||
20 | + 'weibo' => 'Weibo', | ||
21 | + 'wechat' => 'Wechat', | ||
22 | + ]; | ||
23 | + | ||
24 | + /** | ||
25 | + * 服务对象信息 | ||
26 | + * @var array | ||
27 | + */ | ||
28 | + protected $services = []; | ||
29 | + | ||
30 | + public function __construct($options = []) | ||
31 | + { | ||
32 | + $options = array_intersect_key($options, $this->providers); | ||
33 | + $options = array_merge($this->config, is_array($options) ? $options : []); | ||
34 | + foreach ($options as $key => &$option) { | ||
35 | + $option['app_id'] = isset($option['app_id']) ? $option['app_id'] : ''; | ||
36 | + $option['app_secret'] = isset($option['app_secret']) ? $option['app_secret'] : ''; | ||
37 | + // 如果未定义回调地址则自动生成 | ||
38 | + $option['callback'] = isset($option['callback']) && $option['callback'] ? $option['callback'] : addon_url('third/index/callback', [':platform' => $key], false, true); | ||
39 | + } | ||
40 | + $this->config = $options; | ||
41 | + //注册服务器提供者 | ||
42 | + $this->registerProviders(); | ||
43 | + } | ||
44 | + | ||
45 | + /** | ||
46 | + * 注册服务提供者 | ||
47 | + */ | ||
48 | + private function registerProviders() | ||
49 | + { | ||
50 | + foreach ($this->providers as $k => $v) { | ||
51 | + $this->services[$k] = function () use ($k, $v) { | ||
52 | + $options = $this->config[$k]; | ||
53 | + $objname = __NAMESPACE__ . "\\{$v}"; | ||
54 | + return new $objname($options); | ||
55 | + }; | ||
56 | + } | ||
57 | + } | ||
58 | + | ||
59 | + public function __set($key, $value) | ||
60 | + { | ||
61 | + $this->services[$key] = $value; | ||
62 | + } | ||
63 | + | ||
64 | + public function __get($key) | ||
65 | + { | ||
66 | + return isset($this->services[$key]) ? $this->services[$key]($this) : null; | ||
67 | + } | ||
68 | +} |
addons/third/library/Qq.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\library; | ||
4 | + | ||
5 | +use fast\Http; | ||
6 | +use think\Config; | ||
7 | +use think\Session; | ||
8 | + | ||
9 | +/** | ||
10 | |||
11 | + */ | ||
12 | +class Qq | ||
13 | +{ | ||
14 | + const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize"; | ||
15 | + const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token"; | ||
16 | + const GET_USERINFO_URL = "https://graph.qq.com/user/get_user_info"; | ||
17 | + const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me"; | ||
18 | + | ||
19 | + /** | ||
20 | + * 配置信息 | ||
21 | + * @var array | ||
22 | + */ | ||
23 | + private $config = []; | ||
24 | + | ||
25 | + public function __construct($options = []) | ||
26 | + { | ||
27 | + if ($config = Config::get('third.qq')) { | ||
28 | + $this->config = array_merge($this->config, $config); | ||
29 | + } | ||
30 | + $this->config = array_merge($this->config, is_array($options) ? $options : []); | ||
31 | + } | ||
32 | + | ||
33 | + /** | ||
34 | + * 登陆 | ||
35 | + */ | ||
36 | + public function login() | ||
37 | + { | ||
38 | + header("Location:" . $this->getAuthorizeUrl()); | ||
39 | + } | ||
40 | + | ||
41 | + /** | ||
42 | + * 获取authorize_url | ||
43 | + */ | ||
44 | + public function getAuthorizeUrl() | ||
45 | + { | ||
46 | + $state = md5(uniqid(rand(), true)); | ||
47 | + Session::set('state', $state); | ||
48 | + $queryarr = array( | ||
49 | + "response_type" => "code", | ||
50 | + "client_id" => $this->config['app_id'], | ||
51 | + "redirect_uri" => $this->config['callback'], | ||
52 | + "scope" => $this->config['scope'], | ||
53 | + "state" => $state, | ||
54 | + ); | ||
55 | + request()->isMobile() && $queryarr['display'] = 'mobile'; | ||
56 | + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr); | ||
57 | + return $url; | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * 获取用户信息 | ||
62 | + * @param array $params | ||
63 | + * @return array | ||
64 | + */ | ||
65 | + public function getUserInfo($params = []) | ||
66 | + { | ||
67 | + $params = $params ? $params : $_GET; | ||
68 | + if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) { | ||
69 | + //获取access_token | ||
70 | + $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params; | ||
71 | + $access_token = isset($data['access_token']) ? $data['access_token'] : ''; | ||
72 | + $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : ''; | ||
73 | + $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0; | ||
74 | + if ($access_token) { | ||
75 | + $openid = $this->getOpenId($access_token); | ||
76 | + //获取用户信息 | ||
77 | + $queryarr = [ | ||
78 | + "access_token" => $access_token, | ||
79 | + "oauth_consumer_key" => $this->config['app_id'], | ||
80 | + "openid" => $openid, | ||
81 | + ]; | ||
82 | + $ret = Http::get(self::GET_USERINFO_URL, $queryarr); | ||
83 | + $userinfo = (array)json_decode($ret, true); | ||
84 | + if (!$userinfo || !isset($userinfo['ret']) || $userinfo['ret'] !== 0) { | ||
85 | + return []; | ||
86 | + } | ||
87 | + $userinfo = $userinfo ? $userinfo : []; | ||
88 | + $userinfo['avatar'] = isset($userinfo['figureurl_qq_2']) ? $userinfo['figureurl_qq_2'] : ''; | ||
89 | + $data = [ | ||
90 | + 'access_token' => $access_token, | ||
91 | + 'refresh_token' => $refresh_token, | ||
92 | + 'expires_in' => $expires_in, | ||
93 | + 'openid' => $openid, | ||
94 | + 'userinfo' => $userinfo | ||
95 | + ]; | ||
96 | + return $data; | ||
97 | + } | ||
98 | + } | ||
99 | + return []; | ||
100 | + } | ||
101 | + | ||
102 | + /** | ||
103 | + * 获取access_token | ||
104 | + * @param string $code | ||
105 | + * @return array | ||
106 | + */ | ||
107 | + public function getAccessToken($code = '') | ||
108 | + { | ||
109 | + if (!$code) { | ||
110 | + return []; | ||
111 | + } | ||
112 | + $queryarr = array( | ||
113 | + "grant_type" => "authorization_code", | ||
114 | + "client_id" => $this->config['app_id'], | ||
115 | + "client_secret" => $this->config['app_secret'], | ||
116 | + "redirect_uri" => $this->config['callback'], | ||
117 | + "code" => $code, | ||
118 | + ); | ||
119 | + $ret = Http::get(self::GET_ACCESS_TOKEN_URL, $queryarr); | ||
120 | + $params = []; | ||
121 | + parse_str($ret, $params); | ||
122 | + return $params ? $params : []; | ||
123 | + } | ||
124 | + | ||
125 | + /** | ||
126 | + * 获取open_id | ||
127 | + * @param string $access_token | ||
128 | + * @return string | ||
129 | + */ | ||
130 | + private function getOpenId($access_token = '') | ||
131 | + { | ||
132 | + $response = Http::get(self::GET_OPENID_URL, ['access_token' => $access_token]); | ||
133 | + if (strpos($response, "callback") !== false) { | ||
134 | + $lpos = strpos($response, "("); | ||
135 | + $rpos = strrpos($response, ")"); | ||
136 | + $response = substr($response, $lpos + 1, $rpos - $lpos - 1); | ||
137 | + } | ||
138 | + $user = (array)json_decode($response, true); | ||
139 | + return isset($user['openid']) ? $user['openid'] : ''; | ||
140 | + } | ||
141 | +} |
addons/third/library/Service.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\library; | ||
4 | + | ||
5 | +use addons\third\model\Third; | ||
6 | +use app\common\model\User; | ||
7 | +use fast\Random; | ||
8 | +use think\Db; | ||
9 | +use think\Exception; | ||
10 | + | ||
11 | +/** | ||
12 | + * 第三方登录服务类 | ||
13 | + * | ||
14 | + */ | ||
15 | +class Service | ||
16 | +{ | ||
17 | + | ||
18 | + /** | ||
19 | + * 第三方登录 | ||
20 | + * @param string $platform 平台 | ||
21 | + * @param array $params 参数 | ||
22 | + * @param array $extend 会员扩展信息 | ||
23 | + * @param int $keeptime 有效时长 | ||
24 | + * @return boolean | ||
25 | + */ | ||
26 | + public static function connect($platform, $params = [], $extend = [], $keeptime = 0) | ||
27 | + { | ||
28 | + | ||
29 | + $time = time(); | ||
30 | + $nickname = $params['nickname'] ?? ($params['userinfo']['nickname'] ?? ''); | ||
31 | + $avatar = $params['avatar'] ?? ($params['userinfo']['avatar'] ?? ''); | ||
32 | + $values = [ | ||
33 | + 'platform' => $platform, | ||
34 | + 'openid' => $params['openid'], | ||
35 | + 'openname' => $nickname, | ||
36 | + 'access_token' => $params['access_token'], | ||
37 | + 'refresh_token' => $params['refresh_token'], | ||
38 | + 'expires_in' => $params['expires_in'], | ||
39 | + 'logintime' => $time, | ||
40 | + 'expiretime' => $time + $params['expires_in'], | ||
41 | + ]; | ||
42 | + $values = array_merge($values, $params); | ||
43 | + | ||
44 | + $auth = \app\common\library\Auth::instance(); | ||
45 | + | ||
46 | + $auth->keeptime($keeptime); | ||
47 | + //是否有自己的 | ||
48 | + $third = Third::get(['platform' => $platform, 'openid' => $params['openid']], 'user'); | ||
49 | + if ($third) { | ||
50 | + if (!$third->user) { | ||
51 | + $third->delete(); | ||
52 | + } else { | ||
53 | + $third->allowField(true)->save($values); | ||
54 | + // 写入登录Cookies和Token | ||
55 | + return $auth->direct($third->user_id); | ||
56 | + } | ||
57 | + } | ||
58 | + | ||
59 | + //存在unionid就需要判断是否需要生成新记录 | ||
60 | + if (isset($params['unionid']) && !empty($params['unionid'])) { | ||
61 | + $third = Third::get(['platform' => $platform, 'unionid' => $params['unionid']], 'user'); | ||
62 | + if ($third) { | ||
63 | + if (!$third->user) { | ||
64 | + $third->delete(); | ||
65 | + } else { | ||
66 | + // 保存第三方信息 | ||
67 | + $values['user_id'] = $third->user_id; | ||
68 | + $third = Third::create($values, true); | ||
69 | + // 写入登录Cookies和Token | ||
70 | + return $auth->direct($third->user_id); | ||
71 | + } | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + if ($auth->id) { | ||
76 | + if (!$third) { | ||
77 | + $values['user_id'] = $auth->id; | ||
78 | + Third::create($values, true); | ||
79 | + } | ||
80 | + $user = $auth->getUser(); | ||
81 | + } else { | ||
82 | + // 先随机一个用户名,随后再变更为u+数字id | ||
83 | + $username = Random::alnum(20); | ||
84 | + $password = Random::alnum(6); | ||
85 | + $domain = request()->host(); | ||
86 | + | ||
87 | + Db::startTrans(); | ||
88 | + try { | ||
89 | + // 默认注册一个会员 | ||
90 | + $result = $auth->register($username, $password, $username . '@' . $domain, '', $extend); | ||
91 | + if (!$result) { | ||
92 | + throw new Exception($auth->getError()); | ||
93 | + } | ||
94 | + $user = $auth->getUser(); | ||
95 | + $fields = ['username' => 'u' . $user->id, 'email' => 'u' . $user->id . '@' . $domain]; | ||
96 | + if ($nickname) { | ||
97 | + $fields['nickname'] = $nickname; | ||
98 | + } | ||
99 | + if ($avatar) { | ||
100 | + $fields['avatar'] = function_exists("xss_clean") ? xss_clean(strip_tags($avatar)) : strip_tags($avatar); | ||
101 | + } | ||
102 | + | ||
103 | + // 更新会员资料 | ||
104 | + $user = User::get($user->id); | ||
105 | + $user->save($fields); | ||
106 | + | ||
107 | + // 保存第三方信息 | ||
108 | + $values['user_id'] = $user->id; | ||
109 | + Third::create($values, true); | ||
110 | + Db::commit(); | ||
111 | + } catch (\Exception $e) { | ||
112 | + Db::rollback(); | ||
113 | + $auth->logout(); | ||
114 | + return false; | ||
115 | + } | ||
116 | + } | ||
117 | + // 写入登录Cookies和Token | ||
118 | + return $auth->direct($user->id); | ||
119 | + } | ||
120 | + | ||
121 | + | ||
122 | + public static function isBindThird($platform, $openid, $apptype = '', $unionid = '') | ||
123 | + { | ||
124 | + $conddtions = [ | ||
125 | + 'platform' => $platform, | ||
126 | + 'openid' => $openid | ||
127 | + ]; | ||
128 | + if ($apptype) { | ||
129 | + $conddtions['apptype'] = $apptype; | ||
130 | + } | ||
131 | + $third = Third::get($conddtions, 'user'); | ||
132 | + //第三方存在 | ||
133 | + if ($third) { | ||
134 | + //用户失效 | ||
135 | + if (!$third->user) { | ||
136 | + $third->delete(); | ||
137 | + return false; | ||
138 | + } | ||
139 | + return true; | ||
140 | + } | ||
141 | + if ($unionid) { | ||
142 | + $third = Third::get(['platform' => $platform, 'unionid' => $unionid], 'user'); | ||
143 | + if ($third) { | ||
144 | + // | ||
145 | + if (!$third->user) { | ||
146 | + $third->delete(); | ||
147 | + return false; | ||
148 | + } | ||
149 | + return true; | ||
150 | + } | ||
151 | + } | ||
152 | + | ||
153 | + return false; | ||
154 | + } | ||
155 | +} |
addons/third/library/Wechat.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\library; | ||
4 | + | ||
5 | +use fast\Http; | ||
6 | +use think\Config; | ||
7 | +use think\Session; | ||
8 | + | ||
9 | +/** | ||
10 | + * 微信 | ||
11 | + */ | ||
12 | +class Wechat | ||
13 | +{ | ||
14 | + const GET_AUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize"; | ||
15 | + const GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"; | ||
16 | + const GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo"; | ||
17 | + | ||
18 | + /** | ||
19 | + * 配置信息 | ||
20 | + * @var array | ||
21 | + */ | ||
22 | + private $config = []; | ||
23 | + | ||
24 | + public function __construct($options = []) | ||
25 | + { | ||
26 | + if ($config = Config::get('third.wechat')) { | ||
27 | + $this->config = array_merge($this->config, $config); | ||
28 | + } | ||
29 | + $this->config = array_merge($this->config, is_array($options) ? $options : []); | ||
30 | + } | ||
31 | + | ||
32 | + /** | ||
33 | + * 登陆 | ||
34 | + */ | ||
35 | + public function login() | ||
36 | + { | ||
37 | + header("Location:" . $this->getAuthorizeUrl()); | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * 获取authorize_url | ||
42 | + */ | ||
43 | + public function getAuthorizeUrl() | ||
44 | + { | ||
45 | + $state = md5(uniqid(rand(), true)); | ||
46 | + Session::set('state', $state); | ||
47 | + $queryarr = array( | ||
48 | + "appid" => $this->config['app_id'], | ||
49 | + "redirect_uri" => $this->config['callback'], | ||
50 | + "response_type" => "code", | ||
51 | + "scope" => $this->config['scope'], | ||
52 | + "state" => $state, | ||
53 | + ); | ||
54 | + request()->isMobile() && $queryarr['display'] = 'mobile'; | ||
55 | + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr) . '#wechat_redirect'; | ||
56 | + return $url; | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * 获取用户信息 | ||
61 | + * @param array $params | ||
62 | + * @return array | ||
63 | + */ | ||
64 | + public function getUserInfo($params = []) | ||
65 | + { | ||
66 | + $params = $params ? $params : request()->get(); | ||
67 | + if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) { | ||
68 | + //获取access_token | ||
69 | + $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params; | ||
70 | + $access_token = isset($data['access_token']) ? $data['access_token'] : ''; | ||
71 | + $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : ''; | ||
72 | + $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0; | ||
73 | + if ($access_token) { | ||
74 | + $openid = isset($data['openid']) ? $data['openid'] : ''; | ||
75 | + $unionid = isset($data['unionid']) ? $data['unionid'] : ''; | ||
76 | + if (stripos($this->config['scope'], 'snsapi_userinfo') !== false) { | ||
77 | + //获取用户信息 | ||
78 | + $queryarr = [ | ||
79 | + "access_token" => $access_token, | ||
80 | + "openid" => $openid, | ||
81 | + "lang" => 'zh_CN' | ||
82 | + ]; | ||
83 | + $ret = Http::get(self::GET_USERINFO_URL, $queryarr); | ||
84 | + $userinfo = (array)json_decode($ret, true); | ||
85 | + if (!$userinfo || isset($userinfo['errcode'])) { | ||
86 | + return []; | ||
87 | + } | ||
88 | + $userinfo = $userinfo ? $userinfo : []; | ||
89 | + $userinfo['avatar'] = isset($userinfo['headimgurl']) ? $userinfo['headimgurl'] : ''; | ||
90 | + } else { | ||
91 | + $userinfo = []; | ||
92 | + } | ||
93 | + $data = [ | ||
94 | + 'access_token' => $access_token, | ||
95 | + 'refresh_token' => $refresh_token, | ||
96 | + 'expires_in' => $expires_in, | ||
97 | + 'openid' => $openid, | ||
98 | + 'unionid' => $unionid, | ||
99 | + 'userinfo' => $userinfo | ||
100 | + ]; | ||
101 | + return $data; | ||
102 | + } | ||
103 | + } | ||
104 | + return []; | ||
105 | + } | ||
106 | + | ||
107 | + /** | ||
108 | + * 获取access_token | ||
109 | + * @param string code | ||
110 | + * @return array | ||
111 | + */ | ||
112 | + public function getAccessToken($code = '') | ||
113 | + { | ||
114 | + if (!$code) { | ||
115 | + return []; | ||
116 | + } | ||
117 | + $queryarr = array( | ||
118 | + "appid" => $this->config['app_id'], | ||
119 | + "secret" => $this->config['app_secret'], | ||
120 | + "code" => $code, | ||
121 | + "grant_type" => "authorization_code", | ||
122 | + ); | ||
123 | + $response = Http::get(self::GET_ACCESS_TOKEN_URL, $queryarr); | ||
124 | + $ret = (array)json_decode($response, true); | ||
125 | + return $ret ? $ret : []; | ||
126 | + } | ||
127 | +} |
addons/third/library/Weibo.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\library; | ||
4 | + | ||
5 | +use fast\Http; | ||
6 | +use think\Config; | ||
7 | +use think\Session; | ||
8 | + | ||
9 | +/** | ||
10 | + * 微博 | ||
11 | + */ | ||
12 | +class Weibo | ||
13 | +{ | ||
14 | + const GET_AUTH_CODE_URL = "https://api.weibo.com/oauth2/authorize"; | ||
15 | + const GET_ACCESS_TOKEN_URL = "https://api.weibo.com/oauth2/access_token"; | ||
16 | + const GET_USERINFO_URL = "https://api.weibo.com/2/users/show.json"; | ||
17 | + | ||
18 | + /** | ||
19 | + * 配置信息 | ||
20 | + * @var array | ||
21 | + */ | ||
22 | + private $config = []; | ||
23 | + | ||
24 | + public function __construct($options = []) | ||
25 | + { | ||
26 | + if ($config = Config::get('third.weibo')) { | ||
27 | + $this->config = array_merge($this->config, $config); | ||
28 | + } | ||
29 | + $this->config = array_merge($this->config, is_array($options) ? $options : []); | ||
30 | + } | ||
31 | + | ||
32 | + /** | ||
33 | + * 登陆 | ||
34 | + */ | ||
35 | + public function login() | ||
36 | + { | ||
37 | + header("Location:" . $this->getAuthorizeUrl()); | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * 获取authorize_url | ||
42 | + */ | ||
43 | + public function getAuthorizeUrl() | ||
44 | + { | ||
45 | + $state = md5(uniqid(rand(), true)); | ||
46 | + Session::set('state', $state); | ||
47 | + $queryarr = array( | ||
48 | + "response_type" => "code", | ||
49 | + "client_id" => $this->config['app_id'], | ||
50 | + "redirect_uri" => $this->config['callback'], | ||
51 | + "state" => $state, | ||
52 | + ); | ||
53 | + request()->isMobile() && $queryarr['display'] = 'mobile'; | ||
54 | + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr); | ||
55 | + return $url; | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * 获取用户信息 | ||
60 | + * @param array $params | ||
61 | + * @return array | ||
62 | + */ | ||
63 | + public function getUserInfo($params = []) | ||
64 | + { | ||
65 | + $params = $params ? $params : $_GET; | ||
66 | + if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) { | ||
67 | + //获取access_token | ||
68 | + $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params; | ||
69 | + $access_token = isset($data['access_token']) ? $data['access_token'] : ''; | ||
70 | + $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : ''; | ||
71 | + $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0; | ||
72 | + if ($access_token) { | ||
73 | + $uid = isset($data['uid']) ? $data['uid'] : ''; | ||
74 | + //获取用户信息 | ||
75 | + $queryarr = [ | ||
76 | + "access_token" => $access_token, | ||
77 | + "uid" => $uid, | ||
78 | + ]; | ||
79 | + $ret = Http::get(self::GET_USERINFO_URL, $queryarr); | ||
80 | + $userinfo = (array)json_decode($ret, true); | ||
81 | + if (!$userinfo || isset($userinfo['error_code'])) { | ||
82 | + return []; | ||
83 | + } | ||
84 | + $userinfo = $userinfo ? $userinfo : []; | ||
85 | + $userinfo['nickname'] = isset($userinfo['screen_name']) ? $userinfo['screen_name'] : ''; | ||
86 | + $userinfo['avatar'] = isset($userinfo['profile_image_url']) ? $userinfo['profile_image_url'] : ''; | ||
87 | + $data = [ | ||
88 | + 'access_token' => $access_token, | ||
89 | + 'refresh_token' => $refresh_token, | ||
90 | + 'expires_in' => $expires_in, | ||
91 | + 'openid' => $uid, | ||
92 | + 'userinfo' => $userinfo | ||
93 | + ]; | ||
94 | + return $data; | ||
95 | + } | ||
96 | + } | ||
97 | + return []; | ||
98 | + } | ||
99 | + | ||
100 | + /** | ||
101 | + * 获取access_token | ||
102 | + * @param string code | ||
103 | + * @return array | ||
104 | + */ | ||
105 | + public function getAccessToken($code = '') | ||
106 | + { | ||
107 | + if (!$code) { | ||
108 | + return ''; | ||
109 | + } | ||
110 | + $queryarr = array( | ||
111 | + "grant_type" => "authorization_code", | ||
112 | + "client_id" => $this->config['app_id'], | ||
113 | + "client_secret" => $this->config['app_secret'], | ||
114 | + "redirect_uri" => $this->config['callback'], | ||
115 | + "code" => $code, | ||
116 | + ); | ||
117 | + $response = Http::post(self::GET_ACCESS_TOKEN_URL, $queryarr); | ||
118 | + $ret = (array)json_decode($response, true); | ||
119 | + return $ret ? $ret : []; | ||
120 | + } | ||
121 | +} |
addons/third/model/Third.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\third\model; | ||
4 | + | ||
5 | +use think\Model; | ||
6 | + | ||
7 | +/** | ||
8 | + * 第三方登录模型 | ||
9 | + */ | ||
10 | +class Third extends Model | ||
11 | +{ | ||
12 | + | ||
13 | + // 开启自动写入时间戳字段 | ||
14 | + protected $autoWriteTimestamp = 'int'; | ||
15 | + // 定义时间戳字段名 | ||
16 | + protected $createTime = 'createtime'; | ||
17 | + protected $updateTime = 'updatetime'; | ||
18 | + // 追加属性 | ||
19 | + protected $append = [ | ||
20 | + ]; | ||
21 | + | ||
22 | + public function user() | ||
23 | + { | ||
24 | + return $this->belongsTo('\app\common\model\User', 'user_id', 'id', [], 'LEFT'); | ||
25 | + } | ||
26 | +} |
addons/third/view/hook/user_profile_end.html
0 → 100644
1 | +<div class="form-group"> | ||
2 | + <label class="control-label col-xs-12 col-sm-2">第三方登录:</label> | ||
3 | + <div class="col-xs-12 col-sm-4"> | ||
4 | + <!--@formatter:off--> | ||
5 | + {if in_array('wechat', $platform)} | ||
6 | + <a href="{:addon_url('third/index/unbind', [':platform'=>'wechat'], false)}" data-toggle="tooltip" data-title="点击取消绑定微信登录" class="btn btn-primary btn-sm btn-cancel"><i class="fa fa-wechat"></i></a> | ||
7 | + {else /} | ||
8 | + <a href="{:addon_url('third/index/bind', [':platform'=>'wechat'], false)}" data-toggle="tooltip" data-title="点击绑定微信登录" class="btn btn-default btn-sm btn-gray"><i class="fa fa-wechat"></i></a> | ||
9 | + {/if} | ||
10 | + {if in_array('qq', $platform)} | ||
11 | + <a href="{:addon_url('third/index/unbind', [':platform'=>'qq'], false)}" data-toggle="tooltip" data-title="点击取消绑定QQ登录" class="btn btn-primary btn-sm btn-cancel"><i class="fa fa-qq"></i></a> | ||
12 | + {else /} | ||
13 | + <a href="{:addon_url('third/index/bind', [':platform'=>'qq'], false)}" data-toggle="tooltip" data-title="点击绑定QQ登录" class="btn btn-default btn-sm btn-gray"><i class="fa fa-qq"></i></a> | ||
14 | + {/if} | ||
15 | + {if in_array('weibo', $platform)} | ||
16 | + <a href="{:addon_url('third/index/unbind', [':platform'=>'weibo'], false)}" data-toggle="tooltip" data-title="点击取消绑定微博登录" class="btn btn-primary btn-sm btn-cancel"><i class="fa fa-weibo"></i></a> | ||
17 | + {else /} | ||
18 | + <a href="{:addon_url('third/index/bind', [':platform'=>'weibo'], false)}" data-toggle="tooltip" data-title="点击绑定微博登录" class="btn btn-default btn-sm btn-gray"><i class="fa fa-weibo"></i></a> | ||
19 | + {/if} | ||
20 | + <!--@formatter:on--> | ||
21 | + | ||
22 | + </div> | ||
23 | +</div> | ||
24 | + |
1 | +<style>.social-login { | ||
2 | + display: flex | ||
3 | +} | ||
4 | + | ||
5 | +.social-login a { | ||
6 | + flex: 1; | ||
7 | + margin: 0 2px; | ||
8 | +} | ||
9 | + | ||
10 | +.social-login a:first-child { | ||
11 | + margin-left: 0; | ||
12 | +} | ||
13 | + | ||
14 | +.social-login a:last-child { | ||
15 | + margin-right: 0; | ||
16 | +}</style> | ||
17 | + | ||
18 | +<div class="form-group social-login"> | ||
19 | + {if in_array('wechat', $status)} | ||
20 | + <a class="btn btn-success" href="{:url('/third/connect/wechat')}?url={$url|urlencode}"><i class="fa fa-wechat"></i> 微信登录</a> | ||
21 | + {/if} | ||
22 | + {if in_array('qq', $status)} | ||
23 | + <a class="btn btn-info" href="{:url('/third/connect/qq')}?url={$url|urlencode}"><i class="fa fa-qq"></i> QQ登录</a> | ||
24 | + {/if} | ||
25 | + {if in_array('weibo', $status)} | ||
26 | + <a class="btn btn-danger" href="{:url('/third/connect/weibo')}?url={$url|urlencode}"><i class="fa fa-weibo"></i> 微博登录</a> | ||
27 | + {/if} | ||
28 | +</div> |
addons/third/view/index/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | +<head> | ||
4 | + <title>第三方登录 - {$site.name}</title> | ||
5 | + <meta charset="UTF-8"> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
7 | + | ||
8 | + <link href="__CDN__/assets/css/frontend.min.css" rel="stylesheet"> | ||
9 | + | ||
10 | + <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | ||
11 | + <!--[if lt IE 9]> | ||
12 | + <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script> | ||
13 | + <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script> | ||
14 | + <![endif]--> | ||
15 | + | ||
16 | +</head> | ||
17 | +<body> | ||
18 | +<div class="container"> | ||
19 | + <h2>第三方登录</h2> | ||
20 | + <hr> | ||
21 | + <div class="well"> | ||
22 | + <div class="row"> | ||
23 | + <div class="col-xs-4"> | ||
24 | + {if $user && in_array('qq', $platformList)} | ||
25 | + <a href="{:addon_url('third/index/unbind',[':platform'=>'qq'])}" class="btn btn-block btn-info"> | ||
26 | + <i class="fa fa-qq"></i> 点击解绑 | ||
27 | + </a> | ||
28 | + {else/} | ||
29 | + <a href="{:addon_url('third/index/connect',[':platform'=>'qq'])}" class="btn btn-block btn-info"> | ||
30 | + <i class="fa fa-qq"></i> QQ登录 | ||
31 | + </a> | ||
32 | + {/if} | ||
33 | + </div> | ||
34 | + <div class="col-xs-4"> | ||
35 | + {if $user && in_array('wechat', $platformList)} | ||
36 | + <a href="{:addon_url('third/index/unbind',[':platform'=>'wechat'])}" class="btn btn-block btn-success"> | ||
37 | + <i class="fa fa-wechat"></i> 点击解绑 | ||
38 | + </a> | ||
39 | + {else/} | ||
40 | + <a href="{:addon_url('third/index/connect',[':platform'=>'wechat'])}" class="btn btn-block btn-success"> | ||
41 | + <i class="fa fa-wechat"></i> 微信登录 | ||
42 | + </a> | ||
43 | + {/if} | ||
44 | + </div> | ||
45 | + <div class="col-xs-4"> | ||
46 | + {if $user && in_array('weibo', $platformList)} | ||
47 | + <a href="{:addon_url('third/index/unbind',[':platform'=>'weibo'])}" class="btn btn-block btn-danger"> | ||
48 | + <i class="fa fa-weibo"></i> 点击解绑 | ||
49 | + </a> | ||
50 | + {else/} | ||
51 | + <a href="{:addon_url('third/index/connect',[':platform'=>'weibo'])}" class="btn btn-block btn-danger"> | ||
52 | + <i class="fa fa-weibo"></i> 微博登录 | ||
53 | + </a> | ||
54 | + {/if} | ||
55 | + </div> | ||
56 | + </div> | ||
57 | + </div> | ||
58 | + <h2>相关链接</h2> | ||
59 | + <hr> | ||
60 | + <table class="table table-striped table-hover"> | ||
61 | + <thead> | ||
62 | + <tr> | ||
63 | + <th>QQ</th> | ||
64 | + <th>链接</th> | ||
65 | + </tr> | ||
66 | + </thead> | ||
67 | + <tbody> | ||
68 | + <tr> | ||
69 | + <td>QQ 连接</td> | ||
70 | + <td>{:addon_url('third/index/connect',[':platform'=>'qq'], false, true)}</td> | ||
71 | + </tr> | ||
72 | + <tr> | ||
73 | + <td>QQ 绑定</td> | ||
74 | + <td>{:addon_url('third/index/bind',[':platform'=>'qq'], false, true)}</td> | ||
75 | + </tr> | ||
76 | + <tr> | ||
77 | + <td>QQ 解绑</td> | ||
78 | + <td>{:addon_url('third/index/unbind',[':platform'=>'qq'], false, true)}</td> | ||
79 | + </tr> | ||
80 | + </tbody> | ||
81 | + </table> | ||
82 | + <table class="table table-striped table-hover"> | ||
83 | + <thead> | ||
84 | + <tr> | ||
85 | + <th>微信</th> | ||
86 | + <th>链接</th> | ||
87 | + </tr> | ||
88 | + </thead> | ||
89 | + <tbody> | ||
90 | + <tr> | ||
91 | + <td>微信 连接</td> | ||
92 | + <td>{:addon_url('third/index/connect',[':platform'=>'wechat'], false, true)}</td> | ||
93 | + </tr> | ||
94 | + <tr> | ||
95 | + <td>微信 绑定</td> | ||
96 | + <td>{:addon_url('third/index/bind',[':platform'=>'wechat'], false, true)}</td> | ||
97 | + </tr> | ||
98 | + <tr> | ||
99 | + <td>微信 解绑</td> | ||
100 | + <td>{:addon_url('third/index/unbind',[':platform'=>'wechat'], false, true)}</td> | ||
101 | + </tr> | ||
102 | + </tbody> | ||
103 | + </table> | ||
104 | + <table class="table table-striped table-hover"> | ||
105 | + <thead> | ||
106 | + <tr> | ||
107 | + <th>微博</th> | ||
108 | + <th>链接</th> | ||
109 | + </tr> | ||
110 | + </thead> | ||
111 | + <tbody> | ||
112 | + <tr> | ||
113 | + <td>微博 连接</td> | ||
114 | + <td>{:addon_url('third/index/connect',[':platform'=>'weibo'], false, true)}</td> | ||
115 | + </tr> | ||
116 | + <tr> | ||
117 | + <td>微博 绑定</td> | ||
118 | + <td>{:addon_url('third/index/bind',[':platform'=>'weibo'], false, true)}</td> | ||
119 | + </tr> | ||
120 | + <tr> | ||
121 | + <td>微博 解绑</td> | ||
122 | + <td>{:addon_url('third/index/unbind',[':platform'=>'weibo'], false, true)}</td> | ||
123 | + </tr> | ||
124 | + </tbody> | ||
125 | + </table> | ||
126 | +</div> | ||
127 | +<!-- jQuery --> | ||
128 | +<script src="https://cdn.jsdelivr.net/npm/jquery@2.1.4/dist/jquery.min.js"></script> | ||
129 | + | ||
130 | +<!-- Bootstrap Core JavaScript --> | ||
131 | +<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script> | ||
132 | + | ||
133 | +<script type="text/javascript"> | ||
134 | + $(function () { | ||
135 | + | ||
136 | + }); | ||
137 | +</script> | ||
138 | +</body> | ||
139 | +</html> |
addons/ueditor/.addonrc
0 → 100644
1 | +{"files":["public\\assets\\addons\\ueditor\\dialogs\\anchor\\anchor.html","public\\assets\\addons\\ueditor\\dialogs\\attachment\\attachment.css","public\\assets\\addons\\ueditor\\dialogs\\attachment\\attachment.html","public\\assets\\addons\\ueditor\\dialogs\\attachment\\attachment.js","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_chm.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_default.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_doc.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_exe.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_jpg.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_mp3.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_mv.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_pdf.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_ppt.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_psd.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_rar.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_txt.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\fileTypeImages\\icon_xls.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\alignicon.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\alignicon.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\bg.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\file-icons.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\file-icons.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\icons.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\icons.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\image.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\progress.png","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\success.gif","public\\assets\\addons\\ueditor\\dialogs\\attachment\\images\\success.png","public\\assets\\addons\\ueditor\\dialogs\\background\\background.css","public\\assets\\addons\\ueditor\\dialogs\\background\\background.html","public\\assets\\addons\\ueditor\\dialogs\\background\\background.js","public\\assets\\addons\\ueditor\\dialogs\\background\\images\\bg.png","public\\assets\\addons\\ueditor\\dialogs\\background\\images\\success.png","public\\assets\\addons\\ueditor\\dialogs\\charts\\chart.config.js","public\\assets\\addons\\ueditor\\dialogs\\charts\\charts.css","public\\assets\\addons\\ueditor\\dialogs\\charts\\charts.html","public\\assets\\addons\\ueditor\\dialogs\\charts\\charts.js","public\\assets\\addons\\ueditor\\dialogs\\charts\\images\\charts0.png","public\\assets\\addons\\ueditor\\dialogs\\charts\\images\\charts1.png","public\\assets\\addons\\ueditor\\dialogs\\charts\\images\\charts2.png","public\\assets\\addons\\ueditor\\dialogs\\charts\\images\\charts3.png","public\\assets\\addons\\ueditor\\dialogs\\charts\\images\\charts4.png","public\\assets\\addons\\ueditor\\dialogs\\charts\\images\\charts5.png","public\\assets\\addons\\ueditor\\dialogs\\emotion\\emotion.css","public\\assets\\addons\\ueditor\\dialogs\\emotion\\emotion.html","public\\assets\\addons\\ueditor\\dialogs\\emotion\\emotion.js","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\0.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\bface.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\cface.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\fface.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\jxface2.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\neweditor-tab-bg.png","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\tface.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\wface.gif","public\\assets\\addons\\ueditor\\dialogs\\emotion\\images\\yface.gif","public\\assets\\addons\\ueditor\\dialogs\\fonts\\buttoniconex.css","public\\assets\\addons\\ueditor\\dialogs\\fonts\\iconfont.eot","public\\assets\\addons\\ueditor\\dialogs\\fonts\\iconfont.svg","public\\assets\\addons\\ueditor\\dialogs\\fonts\\iconfont.ttf","public\\assets\\addons\\ueditor\\dialogs\\fonts\\iconfont.woff","public\\assets\\addons\\ueditor\\dialogs\\fonts\\images\\addfile.svg","public\\assets\\addons\\ueditor\\dialogs\\fonts\\images\\selected.svg","public\\assets\\addons\\ueditor\\dialogs\\gmap\\gmap.html","public\\assets\\addons\\ueditor\\dialogs\\help\\help.css","public\\assets\\addons\\ueditor\\dialogs\\help\\help.html","public\\assets\\addons\\ueditor\\dialogs\\help\\help.js","public\\assets\\addons\\ueditor\\dialogs\\image\\image.css","public\\assets\\addons\\ueditor\\dialogs\\image\\image.html","public\\assets\\addons\\ueditor\\dialogs\\image\\image.js","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\alignicon.jpg","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\bg.png","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\icons.gif","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\icons.png","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\image.png","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\progress.png","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\success.gif","public\\assets\\addons\\ueditor\\dialogs\\image\\images\\success.png","public\\assets\\addons\\ueditor\\dialogs\\insertframe\\insertframe.html","public\\assets\\addons\\ueditor\\dialogs\\internal.js","public\\assets\\addons\\ueditor\\dialogs\\link\\link.html","public\\assets\\addons\\ueditor\\dialogs\\map\\map.html","public\\assets\\addons\\ueditor\\dialogs\\map\\show.html","public\\assets\\addons\\ueditor\\dialogs\\music\\balls.svg","public\\assets\\addons\\ueditor\\dialogs\\music\\music.css","public\\assets\\addons\\ueditor\\dialogs\\music\\music.html","public\\assets\\addons\\ueditor\\dialogs\\music\\music.js","public\\assets\\addons\\ueditor\\dialogs\\preview\\preview.html","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\addimg.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\brush.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\delimg.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\delimgH.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\empty.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\emptyH.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\eraser.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\redo.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\redoH.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\scale.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\scaleH.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\size.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\undo.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\images\\undoH.png","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\scrawl.css","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\scrawl.html","public\\assets\\addons\\ueditor\\dialogs\\scrawl\\scrawl.js","public\\assets\\addons\\ueditor\\dialogs\\searchreplace\\searchreplace.html","public\\assets\\addons\\ueditor\\dialogs\\searchreplace\\searchreplace.js","public\\assets\\addons\\ueditor\\dialogs\\snapscreen\\snapscreen.html","public\\assets\\addons\\ueditor\\dialogs\\spechars\\spechars.html","public\\assets\\addons\\ueditor\\dialogs\\spechars\\spechars.js","public\\assets\\addons\\ueditor\\dialogs\\table\\dragicon.png","public\\assets\\addons\\ueditor\\dialogs\\table\\edittable.css","public\\assets\\addons\\ueditor\\dialogs\\table\\edittable.html","public\\assets\\addons\\ueditor\\dialogs\\table\\edittable.js","public\\assets\\addons\\ueditor\\dialogs\\table\\edittd.html","public\\assets\\addons\\ueditor\\dialogs\\table\\edittip.html","public\\assets\\addons\\ueditor\\dialogs\\template\\config.js","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\bg.gif","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\pre0.png","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\pre1.png","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\pre2.png","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\pre3.png","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\pre4.png","public\\assets\\addons\\ueditor\\dialogs\\template\\images\\temp.gif","public\\assets\\addons\\ueditor\\dialogs\\template\\template.css","public\\assets\\addons\\ueditor\\dialogs\\template\\template.html","public\\assets\\addons\\ueditor\\dialogs\\template\\template.js","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\bg.png","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\center_focus.jpg","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\file-icons.gif","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\file-icons.png","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\icons.gif","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\icons.png","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\image.png","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\left_focus.jpg","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\none_focus.jpg","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\progress.png","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\right_focus.jpg","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\success.gif","public\\assets\\addons\\ueditor\\dialogs\\video\\images\\success.png","public\\assets\\addons\\ueditor\\dialogs\\video\\video.css","public\\assets\\addons\\ueditor\\dialogs\\video\\video.html","public\\assets\\addons\\ueditor\\dialogs\\video\\video.js","public\\assets\\addons\\ueditor\\dialogs\\webapp\\webapp.html","public\\assets\\addons\\ueditor\\dialogs\\wordimage\\fClipboard_ueditor.swf","public\\assets\\addons\\ueditor\\dialogs\\wordimage\\imageUploader.swf","public\\assets\\addons\\ueditor\\dialogs\\wordimage\\tangram.js","public\\assets\\addons\\ueditor\\dialogs\\wordimage\\wordimage.html","public\\assets\\addons\\ueditor\\dialogs\\wordimage\\wordimage.js","public\\assets\\addons\\ueditor\\i18n\\zh-cn\\images\\copy.png","public\\assets\\addons\\ueditor\\i18n\\zh-cn\\images\\localimage.png","public\\assets\\addons\\ueditor\\i18n\\zh-cn\\images\\music.png","public\\assets\\addons\\ueditor\\i18n\\zh-cn\\images\\upload.png","public\\assets\\addons\\ueditor\\i18n\\zh-cn\\zh-cn.js","public\\assets\\addons\\ueditor\\index.html","public\\assets\\addons\\ueditor\\package.json","public\\assets\\addons\\ueditor\\README.md","public\\assets\\addons\\ueditor\\themes\\default\\css\\ueditor.css","public\\assets\\addons\\ueditor\\themes\\default\\css\\ueditor.min.css","public\\assets\\addons\\ueditor\\themes\\default\\dialogbase.css","public\\assets\\addons\\ueditor\\themes\\default\\images\\anchor.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\arrow.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\arrow_down.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\arrow_up.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\button-bg.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\cancelbutton.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\charts.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\cursor_h.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\cursor_h.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\cursor_v.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\cursor_v.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\dialog-title-bg.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\filescan.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\highlighted.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\icons-all.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\icons.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\icons.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\img-cracked.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\loaderror.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\loading.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\lock.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\neweditor-tab-bg.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\pagebreak.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\scale.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\sortable.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\spacer.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\sparator_v.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\table-cell-align.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\tangram-colorpicker.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\toolbar_bg.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\unhighlighted.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\upload.png","public\\assets\\addons\\ueditor\\themes\\default\\images\\videologo.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\word.gif","public\\assets\\addons\\ueditor\\themes\\default\\images\\wordpaste.png","public\\assets\\addons\\ueditor\\themes\\iframe.css","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\dash.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\dot.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-1.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-10.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-11.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-12.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-13.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-14.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-15.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-16.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-17.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-18.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-19.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-2.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-20.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-21.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-22.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-23.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-24.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-25.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-26.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-27.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-28.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-29.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-3.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-30.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-31.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-32.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-33.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-34.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-35.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-36.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-37.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-38.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-39.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-4.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-40.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-41.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-42.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-43.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-44.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-45.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-46.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-47.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-48.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-49.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-5.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-50.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-51.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-52.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-53.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-54.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-55.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-56.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-57.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-58.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-59.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-6.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-60.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-61.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-62.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-63.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-64.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-65.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-66.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-67.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-68.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-69.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-7.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-70.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-71.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-72.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-73.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-74.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-75.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-76.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-77.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-78.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-79.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-8.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-80.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-81.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-82.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-83.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-84.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-85.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-86.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-87.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-88.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-89.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-9.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-90.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-91.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-92.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-93.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-94.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-95.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-96.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-97.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-1-98.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-1.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-10.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-11.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-12.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-13.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-14.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-15.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-16.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-17.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-18.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-19.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-2.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-20.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-21.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-22.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-23.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-24.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-25.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-26.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-27.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-28.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-29.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-3.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-30.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-31.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-32.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-33.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-34.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-35.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-36.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-37.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-38.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-39.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-4.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-40.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-41.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-42.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-43.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-44.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-45.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-46.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-47.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-48.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-49.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-5.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-50.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-51.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-52.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-53.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-54.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-55.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-56.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-57.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-58.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-59.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-6.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-60.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-61.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-62.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-63.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-64.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-65.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-66.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-67.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-68.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-69.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-7.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-70.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-71.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-72.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-73.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-74.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-75.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-76.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-77.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-78.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-79.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-8.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-80.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-81.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-82.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-83.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-84.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-85.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-86.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-87.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-88.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-89.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-9.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-90.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-91.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-92.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-93.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-94.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-95.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-96.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-97.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-2-98.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-1.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-10.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-11.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-12.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-13.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-14.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-15.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-16.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-17.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-18.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-19.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-2.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-20.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-21.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-22.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-23.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-24.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-25.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-26.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-27.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-28.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-29.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-3.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-30.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-31.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-32.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-33.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-34.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-35.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-36.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-37.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-38.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-39.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-4.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-40.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-41.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-42.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-43.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-44.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-45.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-46.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-47.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-48.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-49.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-5.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-50.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-51.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-52.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-53.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-54.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-55.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-56.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-57.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-58.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-59.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-6.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-60.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-61.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-62.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-63.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-64.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-65.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-66.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-67.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-68.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-69.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-7.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-70.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-71.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-72.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-73.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-74.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-75.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-76.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-77.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-78.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-79.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-8.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-80.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-81.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-82.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-83.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-84.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-85.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-86.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-87.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-88.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-89.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-9.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-90.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-91.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-92.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-93.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-94.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-95.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-96.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-97.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-cn-3-98.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-1.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-10.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-11.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-12.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-13.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-14.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-15.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-16.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-17.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-18.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-19.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-2.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-20.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-21.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-22.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-23.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-24.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-25.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-26.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-27.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-28.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-29.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-3.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-30.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-31.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-32.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-33.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-34.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-35.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-36.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-37.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-38.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-39.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-4.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-40.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-41.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-42.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-43.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-44.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-45.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-46.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-47.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-48.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-49.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-5.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-50.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-51.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-52.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-53.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-54.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-55.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-56.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-57.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-58.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-59.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-6.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-60.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-61.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-62.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-63.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-64.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-65.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-66.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-67.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-68.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-69.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-7.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-70.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-71.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-72.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-73.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-74.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-75.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-76.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-77.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-78.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-79.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-8.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-80.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-81.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-82.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-83.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-84.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-85.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-86.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-87.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-88.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-89.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-9.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-90.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-91.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-92.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-93.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-94.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-95.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-96.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-97.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-1-98.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-1.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-10.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-11.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-12.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-13.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-14.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-15.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-16.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-17.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-18.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-19.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-2.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-20.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-21.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-22.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-23.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-24.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-25.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-26.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-27.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-28.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-29.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-3.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-30.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-31.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-32.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-33.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-34.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-35.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-36.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-37.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-38.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-39.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-4.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-40.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-41.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-42.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-43.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-44.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-45.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-46.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-47.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-48.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-49.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-5.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-50.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-51.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-52.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-53.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-54.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-55.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-56.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-57.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-58.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-59.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-6.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-60.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-61.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-62.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-63.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-64.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-65.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-66.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-67.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-68.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-69.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-7.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-70.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-71.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-72.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-73.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-74.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-75.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-76.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-77.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-78.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-79.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-8.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-80.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-81.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-82.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-83.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-84.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-85.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-86.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-87.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-88.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-89.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-9.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-90.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-91.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-92.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-93.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-94.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-95.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-96.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-97.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-2-98.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-1.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-10.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-11.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-12.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-13.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-14.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-15.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-16.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-17.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-18.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-19.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-2.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-20.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-21.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-22.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-23.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-24.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-25.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-26.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-27.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-28.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-29.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-3.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-30.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-31.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-32.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-33.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-34.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-35.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-36.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-37.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-38.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-39.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-4.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-40.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-41.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-42.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-43.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-44.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-45.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-46.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-47.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-48.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-49.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-5.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-50.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-51.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-52.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-53.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-54.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-55.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-56.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-57.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-58.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-59.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-6.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-60.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-61.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-62.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-63.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-64.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-65.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-66.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-67.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-68.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-69.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-7.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-70.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-71.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-72.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-73.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-74.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-75.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-76.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-77.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-78.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-79.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-8.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-80.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-81.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-82.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-83.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-84.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-85.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-86.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-87.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-88.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-89.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-9.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-90.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-91.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-92.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-93.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-94.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-95.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-96.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-97.gif","public\\assets\\addons\\ueditor\\themes\\ueditor-list\\list-num-3-98.gif","public\\assets\\addons\\ueditor\\third-party\\browser-md5-file.min.js","public\\assets\\addons\\ueditor\\third-party\\codemirror\\codemirror.css","public\\assets\\addons\\ueditor\\third-party\\codemirror\\codemirror.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\adapters\\mootools-adapter.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\adapters\\prototype-adapter.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\adapters\\standalone-framework.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\highcharts-more.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\highcharts.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\annotations.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\canvas-tools.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\data.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\drilldown.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\exporting.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\funnel.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\heatmap.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\map.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\modules\\no-data-to-display.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\themes\\dark-blue.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\themes\\dark-green.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\themes\\gray.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\themes\\grid.js","public\\assets\\addons\\ueditor\\third-party\\highcharts\\themes\\skies.js","public\\assets\\addons\\ueditor\\third-party\\jquery-1.10.2.min.js","public\\assets\\addons\\ueditor\\third-party\\jquery-1.10.2.min.map","public\\assets\\addons\\ueditor\\third-party\\snapscreen\\UEditorSnapscreen.exe","public\\assets\\addons\\ueditor\\third-party\\SyntaxHighlighter\\shCore.js","public\\assets\\addons\\ueditor\\third-party\\SyntaxHighlighter\\shCoreDefault.css","public\\assets\\addons\\ueditor\\third-party\\video-js\\font\\vjs.eot","public\\assets\\addons\\ueditor\\third-party\\video-js\\font\\vjs.svg","public\\assets\\addons\\ueditor\\third-party\\video-js\\font\\vjs.ttf","public\\assets\\addons\\ueditor\\third-party\\video-js\\font\\vjs.woff","public\\assets\\addons\\ueditor\\third-party\\video-js\\video-js.css","public\\assets\\addons\\ueditor\\third-party\\video-js\\video-js.min.css","public\\assets\\addons\\ueditor\\third-party\\video-js\\video-js.swf","public\\assets\\addons\\ueditor\\third-party\\video-js\\video.js","public\\assets\\addons\\ueditor\\third-party\\webuploader\\Uploader.swf","public\\assets\\addons\\ueditor\\third-party\\webuploader\\webuploader.css","public\\assets\\addons\\ueditor\\third-party\\webuploader\\webuploader.custom.min.js","public\\assets\\addons\\ueditor\\third-party\\webuploader\\webuploader.flashonly.min.js","public\\assets\\addons\\ueditor\\third-party\\webuploader\\webuploader.html5only.min.js","public\\assets\\addons\\ueditor\\third-party\\webuploader\\webuploader.min.js","public\\assets\\addons\\ueditor\\third-party\\webuploader\\webuploader.withoutimage.min.js","public\\assets\\addons\\ueditor\\third-party\\zeroclipboard\\ZeroClipboard.min.js","public\\assets\\addons\\ueditor\\third-party\\zeroclipboard\\ZeroClipboard.swf","public\\assets\\addons\\ueditor\\ueditor.all.js","public\\assets\\addons\\ueditor\\ueditor.all.min.js","public\\assets\\addons\\ueditor\\ueditor.config.js","public\\assets\\addons\\ueditor\\ueditor.parse.js","public\\assets\\addons\\ueditor\\ueditor.parse.min.js"],"license":"basic","licenseto":"10789","licensekey":"JRF9Dxel1p68O73I M6Uw\/umPrtgQdmR3GRUuDg==","domains":["campus.cn"],"licensecodes":[],"validations":["c51a74160ee0aa6a19729b33d4a9fcdc"]} |
addons/ueditor/Ueditor.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\ueditor; | ||
4 | + | ||
5 | +use think\Addons; | ||
6 | + | ||
7 | +/** | ||
8 | + * 百度Ueditor插件 | ||
9 | + */ | ||
10 | +class Ueditor extends Addons | ||
11 | +{ | ||
12 | + | ||
13 | + /** | ||
14 | + * 插件安装方法 | ||
15 | + * @return bool | ||
16 | + */ | ||
17 | + public function install() | ||
18 | + { | ||
19 | + return true; | ||
20 | + } | ||
21 | + | ||
22 | + /** | ||
23 | + * 插件卸载方法 | ||
24 | + * @return bool | ||
25 | + */ | ||
26 | + public function uninstall() | ||
27 | + { | ||
28 | + return true; | ||
29 | + } | ||
30 | + | ||
31 | + /** | ||
32 | + * @param $params | ||
33 | + */ | ||
34 | + public function configInit(&$params) | ||
35 | + { | ||
36 | + $config = $this->getConfig(); | ||
37 | + $params['ueditor'] = ['classname' => $config['classname'] ?? '.editor']; | ||
38 | + $params['ueditor'] = ['baiduMapAk' => $config['baiduMapAk'] ?? '']; | ||
39 | + } | ||
40 | +} |
addons/ueditor/bootstrap.js
0 → 100644
1 | +window.UEDITOR_HOME_URL = Config.__CDN__ + "/assets/addons/ueditor/"; | ||
2 | +require.config({ | ||
3 | + paths: { | ||
4 | + 'ueditor.config': '../addons/ueditor/ueditor.config', | ||
5 | + 'ueditor': '../addons/ueditor/ueditor.all.min', | ||
6 | + 'ueditor.zh': '../addons/ueditor/i18n/zh-cn/zh-cn', | ||
7 | + 'zeroclipboard': '../addons/ueditor/third-party/zeroclipboard/ZeroClipboard.min', | ||
8 | + }, | ||
9 | + shim: { | ||
10 | + 'ueditor': { | ||
11 | + deps: ['zeroclipboard', 'ueditor.config'], | ||
12 | + exports: 'UE', | ||
13 | + init: function (ZeroClipboard) { | ||
14 | + //导出到全局变量,供ueditor使用 | ||
15 | + window.ZeroClipboard = ZeroClipboard; | ||
16 | + }, | ||
17 | + }, | ||
18 | + 'ueditor.zh': ['ueditor'] | ||
19 | + } | ||
20 | +}); | ||
21 | +require(['form', 'upload'], function (Form, Upload) { | ||
22 | + var _bindevent = Form.events.bindevent; | ||
23 | + Form.events.bindevent = function (form) { | ||
24 | + _bindevent.apply(this, [form]); | ||
25 | + try { | ||
26 | + //绑定editor事件 | ||
27 | + require(['ueditor', 'ueditor.zh'], function (UE, undefined) { | ||
28 | + UE.list = []; | ||
29 | + window.UEDITOR_CONFIG['uploadService'] = function (context, editor) { | ||
30 | + return { | ||
31 | + Upload: () => { return Upload }, | ||
32 | + Fast: () => { return Fast }, | ||
33 | + } | ||
34 | + }; | ||
35 | + $(Config.ueditor.classname || '.editor', form).each(function () { | ||
36 | + var id = $(this).attr("id"); | ||
37 | + var name = $(this).attr("name"); | ||
38 | + $(this).removeClass('form-control'); | ||
39 | + UE.list[id] = UE.getEditor(id, { | ||
40 | + allowDivTransToP: false, //阻止div自动转p标签 | ||
41 | + initialFrameWidth: '100%', | ||
42 | + initialFrameHeight: 320, | ||
43 | + autoFloatEnabled: false, | ||
44 | + baiduMapAk: Config.ueditor.baiduMapAk || '', //百度地图api密钥(ak) | ||
45 | + // autoHeightEnabled: true, //自动高度 | ||
46 | + zIndex: 90, | ||
47 | + xssFilterRules: false, | ||
48 | + outputXssFilter: false, | ||
49 | + inputXssFilter: false, | ||
50 | + catchRemoteImageEnable: true, | ||
51 | + imageAllowFiles: '',//允许上传的图片格式,编辑器默认[".png", ".jpg", ".jpeg", ".gif", ".bmp"] | ||
52 | + }); | ||
53 | + UE.list[id].addListener("contentChange", function () { | ||
54 | + $('#' + id).val(this.getContent()); | ||
55 | + $('textarea[name="' + name + '"]').val(this.getContent()); | ||
56 | + }) | ||
57 | + }); | ||
58 | + }) | ||
59 | + } catch (e) { | ||
60 | + console.log('绑定editor事件', e) | ||
61 | + } | ||
62 | + } | ||
63 | +}); |
addons/ueditor/config.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + [ | ||
5 | + 'name' => 'classname', | ||
6 | + 'title' => '渲染文本框元素', | ||
7 | + 'type' => 'string', | ||
8 | + 'content' => [], | ||
9 | + 'value' => '.editor', | ||
10 | + 'rule' => 'required', | ||
11 | + 'msg' => '', | ||
12 | + 'tip' => '用于对指定的元素渲染,一般情况下无需修改', | ||
13 | + 'ok' => '', | ||
14 | + 'extend' => '', | ||
15 | + ], | ||
16 | + [ | ||
17 | + 'name' => 'baiduMapAk', | ||
18 | + 'title' => '百度地图ak', | ||
19 | + 'type' => 'string', | ||
20 | + 'content' => [], | ||
21 | + 'value' => '', | ||
22 | + 'rule' => '', | ||
23 | + 'msg' => '', | ||
24 | + 'tip' => '需要设置百度地图api密钥(ak),否则地图无法使用', | ||
25 | + 'ok' => '', | ||
26 | + 'extend' => '', | ||
27 | + ], | ||
28 | +]; |
addons/ueditor/info.ini
0 → 100644
application/.htaccess
0 → 100644
1 | +deny from all |
application/admin/behavior/AdminLog.php
0 → 100644
application/admin/command/Addon.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command; | ||
4 | + | ||
5 | +use think\addons\AddonException; | ||
6 | +use think\addons\Service; | ||
7 | +use think\Config; | ||
8 | +use think\console\Command; | ||
9 | +use think\console\Input; | ||
10 | +use think\console\input\Option; | ||
11 | +use think\console\Output; | ||
12 | +use think\Db; | ||
13 | +use think\Exception; | ||
14 | +use think\exception\PDOException; | ||
15 | + | ||
16 | +class Addon extends Command | ||
17 | +{ | ||
18 | + | ||
19 | + protected function configure() | ||
20 | + { | ||
21 | + $this | ||
22 | + ->setName('addon') | ||
23 | + ->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null) | ||
24 | + ->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/uninstall/refresh/package/move)', 'create') | ||
25 | + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) | ||
26 | + ->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null) | ||
27 | + ->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null) | ||
28 | + ->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null) | ||
29 | + ->addOption('domain', 'd', Option::VALUE_OPTIONAL, 'domain', null) | ||
30 | + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null) | ||
31 | + ->setDescription('Addon manager'); | ||
32 | + } | ||
33 | + | ||
34 | + protected function execute(Input $input, Output $output) | ||
35 | + { | ||
36 | + $name = $input->getOption('name') ?: ''; | ||
37 | + $action = $input->getOption('action') ?: ''; | ||
38 | + if (stripos($name, 'addons' . DS) !== false) { | ||
39 | + $name = explode(DS, $name)[1]; | ||
40 | + } | ||
41 | + //强制覆盖 | ||
42 | + $force = $input->getOption('force'); | ||
43 | + //版本 | ||
44 | + $release = $input->getOption('release') ?: ''; | ||
45 | + //uid | ||
46 | + $uid = $input->getOption('uid') ?: ''; | ||
47 | + //token | ||
48 | + $token = $input->getOption('token') ?: ''; | ||
49 | + | ||
50 | + include dirname(__DIR__) . DS . 'common.php'; | ||
51 | + | ||
52 | + if (!$name && !in_array($action, ['refresh'])) { | ||
53 | + throw new Exception('Addon name could not be empty'); | ||
54 | + } | ||
55 | + if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package', 'move'])) { | ||
56 | + throw new Exception('Please input correct action name'); | ||
57 | + } | ||
58 | + | ||
59 | + // 查询一次SQL,判断连接是否正常 | ||
60 | + Db::execute("SELECT 1"); | ||
61 | + | ||
62 | + $addonDir = ADDON_PATH . $name . DS; | ||
63 | + switch ($action) { | ||
64 | + case 'create': | ||
65 | + //非覆盖模式时如果存在则报错 | ||
66 | + if (is_dir($addonDir) && !$force) { | ||
67 | + throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true "); | ||
68 | + } | ||
69 | + //如果存在先移除 | ||
70 | + if (is_dir($addonDir)) { | ||
71 | + rmdirs($addonDir); | ||
72 | + } | ||
73 | + mkdir($addonDir, 0755, true); | ||
74 | + mkdir($addonDir . DS . 'controller', 0755, true); | ||
75 | + $menuList = \app\common\library\Menu::export($name); | ||
76 | + $createMenu = $this->getCreateMenu($menuList); | ||
77 | + $prefix = Config::get('database.prefix'); | ||
78 | + $createTableSql = ''; | ||
79 | + try { | ||
80 | + $result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;"); | ||
81 | + if (isset($result[0]) && isset($result[0]['Create Table'])) { | ||
82 | + $createTableSql = $result[0]['Create Table']; | ||
83 | + } | ||
84 | + } catch (PDOException $e) { | ||
85 | + | ||
86 | + } | ||
87 | + | ||
88 | + $data = [ | ||
89 | + 'name' => $name, | ||
90 | + 'addon' => $name, | ||
91 | + 'addonClassName' => ucfirst($name), | ||
92 | + 'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu) . ";\n\tMenu::create(\$menu);" : '', | ||
93 | + 'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '', | ||
94 | + 'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '', | ||
95 | + 'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '', | ||
96 | + ]; | ||
97 | + $this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php'); | ||
98 | + $this->writeToFile("config", $data, $addonDir . 'config.php'); | ||
99 | + $this->writeToFile("info", $data, $addonDir . 'info.ini'); | ||
100 | + $this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php'); | ||
101 | + if ($createTableSql) { | ||
102 | + $createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql); | ||
103 | + file_put_contents($addonDir . 'install.sql', $createTableSql); | ||
104 | + } | ||
105 | + | ||
106 | + $output->info("Create Successed!"); | ||
107 | + break; | ||
108 | + case 'disable': | ||
109 | + case 'enable': | ||
110 | + try { | ||
111 | + //调用启用、禁用的方法 | ||
112 | + Service::$action($name, 0); | ||
113 | + } catch (AddonException $e) { | ||
114 | + if ($e->getCode() != -3) { | ||
115 | + throw new Exception($e->getMessage()); | ||
116 | + } | ||
117 | + if (!$force) { | ||
118 | + //如果有冲突文件则提醒 | ||
119 | + $data = $e->getData(); | ||
120 | + foreach ($data['conflictlist'] as $k => $v) { | ||
121 | + $output->warning($v); | ||
122 | + } | ||
123 | + $output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: "); | ||
124 | + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); | ||
125 | + if (trim($line) != 'yes') { | ||
126 | + throw new Exception("Operation is aborted!"); | ||
127 | + } | ||
128 | + } | ||
129 | + //调用启用、禁用的方法 | ||
130 | + Service::$action($name, 1); | ||
131 | + } catch (Exception $e) { | ||
132 | + throw new Exception($e->getMessage()); | ||
133 | + } | ||
134 | + $output->info(ucfirst($action) . " Successed!"); | ||
135 | + break; | ||
136 | + case 'uninstall': | ||
137 | + //非覆盖模式时如果存在则报错 | ||
138 | + if (!$force) { | ||
139 | + throw new Exception("If you need to uninstall addon, use the parameter --force=true "); | ||
140 | + } | ||
141 | + try { | ||
142 | + Service::uninstall($name, 0); | ||
143 | + } catch (AddonException $e) { | ||
144 | + if ($e->getCode() != -3) { | ||
145 | + throw new Exception($e->getMessage()); | ||
146 | + } | ||
147 | + if (!$force) { | ||
148 | + //如果有冲突文件则提醒 | ||
149 | + $data = $e->getData(); | ||
150 | + foreach ($data['conflictlist'] as $k => $v) { | ||
151 | + $output->warning($v); | ||
152 | + } | ||
153 | + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); | ||
154 | + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); | ||
155 | + if (trim($line) != 'yes') { | ||
156 | + throw new Exception("Operation is aborted!"); | ||
157 | + } | ||
158 | + } | ||
159 | + Service::uninstall($name, 1); | ||
160 | + } catch (Exception $e) { | ||
161 | + throw new Exception($e->getMessage()); | ||
162 | + } | ||
163 | + | ||
164 | + $output->info("Uninstall Successed!"); | ||
165 | + break; | ||
166 | + case 'refresh': | ||
167 | + Service::refresh(); | ||
168 | + $output->info("Refresh Successed!"); | ||
169 | + break; | ||
170 | + case 'package': | ||
171 | + $infoFile = $addonDir . 'info.ini'; | ||
172 | + if (!is_file($infoFile)) { | ||
173 | + throw new Exception(__('Addon info file was not found')); | ||
174 | + } | ||
175 | + | ||
176 | + $info = get_addon_info($name); | ||
177 | + if (!$info) { | ||
178 | + throw new Exception(__('Addon info file data incorrect')); | ||
179 | + } | ||
180 | + $infoname = isset($info['name']) ? $info['name'] : ''; | ||
181 | + if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) { | ||
182 | + throw new Exception(__('Addon info name incorrect')); | ||
183 | + } | ||
184 | + | ||
185 | + $infoversion = isset($info['version']) ? $info['version'] : ''; | ||
186 | + if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) { | ||
187 | + throw new Exception(__('Addon info version incorrect')); | ||
188 | + } | ||
189 | + | ||
190 | + $addonTmpDir = RUNTIME_PATH . 'addons' . DS; | ||
191 | + if (!is_dir($addonTmpDir)) { | ||
192 | + @mkdir($addonTmpDir, 0755, true); | ||
193 | + } | ||
194 | + $addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip'; | ||
195 | + if (!class_exists('ZipArchive')) { | ||
196 | + throw new Exception(__('ZinArchive not install')); | ||
197 | + } | ||
198 | + $zip = new \ZipArchive; | ||
199 | + $zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); | ||
200 | + | ||
201 | + $files = new \RecursiveIteratorIterator( | ||
202 | + new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY | ||
203 | + ); | ||
204 | + | ||
205 | + foreach ($files as $name => $file) { | ||
206 | + if (!$file->isDir()) { | ||
207 | + $filePath = $file->getRealPath(); | ||
208 | + $relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir))); | ||
209 | + if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) { | ||
210 | + $zip->addFile($filePath, $relativePath); | ||
211 | + } | ||
212 | + } | ||
213 | + } | ||
214 | + $zip->close(); | ||
215 | + $output->info("Package Successed!"); | ||
216 | + break; | ||
217 | + case 'move': | ||
218 | + $movePath = [ | ||
219 | + 'adminOnlySelfDir' => ['admin/behavior', 'admin/controller', 'admin/library', 'admin/model', 'admin/validate', 'admin/view'], | ||
220 | + 'adminAllSubDir' => ['admin/lang'], | ||
221 | + 'publicDir' => ['public/assets/addons', 'public/assets/js/backend'] | ||
222 | + ]; | ||
223 | + $paths = []; | ||
224 | + $appPath = str_replace('/', DS, APP_PATH); | ||
225 | + $rootPath = str_replace('/', DS, ROOT_PATH); | ||
226 | + foreach ($movePath as $k => $items) { | ||
227 | + switch ($k) { | ||
228 | + case 'adminOnlySelfDir': | ||
229 | + foreach ($items as $v) { | ||
230 | + $v = str_replace('/', DS, $v); | ||
231 | + $oldPath = $appPath . $v . DS . $name; | ||
232 | + $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $name; | ||
233 | + $paths[$oldPath] = $newPath; | ||
234 | + } | ||
235 | + break; | ||
236 | + case 'adminAllSubDir': | ||
237 | + foreach ($items as $v) { | ||
238 | + $v = str_replace('/', DS, $v); | ||
239 | + $vPath = $appPath . $v; | ||
240 | + $list = scandir($vPath); | ||
241 | + foreach ($list as $_v) { | ||
242 | + if (!in_array($_v, ['.', '..']) && is_dir($vPath . DS . $_v)) { | ||
243 | + $oldPath = $appPath . $v . DS . $_v . DS . $name; | ||
244 | + $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $_v . DS . $name; | ||
245 | + $paths[$oldPath] = $newPath; | ||
246 | + } | ||
247 | + } | ||
248 | + } | ||
249 | + break; | ||
250 | + case 'publicDir': | ||
251 | + foreach ($items as $v) { | ||
252 | + $v = str_replace('/', DS, $v); | ||
253 | + $oldPath = $rootPath . $v . DS . $name; | ||
254 | + $newPath = $rootPath . 'addons' . DS . $name . DS . $v . DS . $name; | ||
255 | + $paths[$oldPath] = $newPath; | ||
256 | + } | ||
257 | + break; | ||
258 | + } | ||
259 | + } | ||
260 | + foreach ($paths as $oldPath => $newPath) { | ||
261 | + if (is_dir($oldPath)) { | ||
262 | + if ($force) { | ||
263 | + if (is_dir($newPath)) { | ||
264 | + $list = scandir($newPath); | ||
265 | + foreach ($list as $_v) { | ||
266 | + if (!in_array($_v, ['.', '..'])) { | ||
267 | + $file = $newPath . DS . $_v; | ||
268 | + @chmod($file, 0777); | ||
269 | + @unlink($file); | ||
270 | + } | ||
271 | + } | ||
272 | + @rmdir($newPath); | ||
273 | + } | ||
274 | + } | ||
275 | + copydirs($oldPath, $newPath); | ||
276 | + } | ||
277 | + } | ||
278 | + break; | ||
279 | + default: | ||
280 | + break; | ||
281 | + } | ||
282 | + } | ||
283 | + | ||
284 | + /** | ||
285 | + * 获取创建菜单的数组 | ||
286 | + * @param array $menu | ||
287 | + * @return array | ||
288 | + */ | ||
289 | + protected function getCreateMenu($menu) | ||
290 | + { | ||
291 | + $result = []; | ||
292 | + foreach ($menu as $k => & $v) { | ||
293 | + $arr = [ | ||
294 | + 'name' => $v['name'], | ||
295 | + 'title' => $v['title'], | ||
296 | + ]; | ||
297 | + if ($v['icon'] != 'fa fa-circle-o') { | ||
298 | + $arr['icon'] = $v['icon']; | ||
299 | + } | ||
300 | + if ($v['ismenu']) { | ||
301 | + $arr['ismenu'] = $v['ismenu']; | ||
302 | + } | ||
303 | + if (isset($v['childlist']) && $v['childlist']) { | ||
304 | + $arr['sublist'] = $this->getCreateMenu($v['childlist']); | ||
305 | + } | ||
306 | + $result[] = $arr; | ||
307 | + } | ||
308 | + return $result; | ||
309 | + } | ||
310 | + | ||
311 | + /** | ||
312 | + * 写入到文件 | ||
313 | + * @param string $name | ||
314 | + * @param array $data | ||
315 | + * @param string $pathname | ||
316 | + * @return mixed | ||
317 | + */ | ||
318 | + protected function writeToFile($name, $data, $pathname) | ||
319 | + { | ||
320 | + $search = $replace = []; | ||
321 | + foreach ($data as $k => $v) { | ||
322 | + $search[] = "{%{$k}%}"; | ||
323 | + $replace[] = $v; | ||
324 | + } | ||
325 | + $stub = file_get_contents($this->getStub($name)); | ||
326 | + $content = str_replace($search, $replace, $stub); | ||
327 | + | ||
328 | + if (!is_dir(dirname($pathname))) { | ||
329 | + mkdir(strtolower(dirname($pathname)), 0755, true); | ||
330 | + } | ||
331 | + return file_put_contents($pathname, $content); | ||
332 | + } | ||
333 | + | ||
334 | + /** | ||
335 | + * 获取基础模板 | ||
336 | + * @param string $name | ||
337 | + * @return string | ||
338 | + */ | ||
339 | + protected function getStub($name) | ||
340 | + { | ||
341 | + return __DIR__ . '/Addon/stubs/' . $name . '.stub'; | ||
342 | + } | ||
343 | + | ||
344 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\{%name%}; | ||
4 | + | ||
5 | +use app\common\library\Menu; | ||
6 | +use think\Addons; | ||
7 | + | ||
8 | +/** | ||
9 | + * 插件 | ||
10 | + */ | ||
11 | +class {%addonClassName%} extends Addons | ||
12 | +{ | ||
13 | + | ||
14 | + /** | ||
15 | + * 插件安装方法 | ||
16 | + * @return bool | ||
17 | + */ | ||
18 | + public function install() | ||
19 | + { | ||
20 | + {%addonInstallMenu%} | ||
21 | + return true; | ||
22 | + } | ||
23 | + | ||
24 | + /** | ||
25 | + * 插件卸载方法 | ||
26 | + * @return bool | ||
27 | + */ | ||
28 | + public function uninstall() | ||
29 | + { | ||
30 | + {%addonUninstallMenu%} | ||
31 | + return true; | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * 插件启用方法 | ||
36 | + * @return bool | ||
37 | + */ | ||
38 | + public function enable() | ||
39 | + { | ||
40 | + {%addonEnableMenu%} | ||
41 | + return true; | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * 插件禁用方法 | ||
46 | + * @return bool | ||
47 | + */ | ||
48 | + public function disable() | ||
49 | + { | ||
50 | + {%addonDisableMenu%} | ||
51 | + return true; | ||
52 | + } | ||
53 | + | ||
54 | +} |
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + [ | ||
5 | + //配置唯一标识 | ||
6 | + 'name' => 'usernmae', | ||
7 | + //显示的标题 | ||
8 | + 'title' => '用户名', | ||
9 | + //类型 | ||
10 | + 'type' => 'string', | ||
11 | + //分组 | ||
12 | + 'group' => '', | ||
13 | + //动态显示 | ||
14 | + 'visible' => '', | ||
15 | + //数据字典 | ||
16 | + 'content' => [ | ||
17 | + ], | ||
18 | + //值 | ||
19 | + 'value' => '', | ||
20 | + //验证规则 | ||
21 | + 'rule' => 'required', | ||
22 | + //错误消息 | ||
23 | + 'msg' => '', | ||
24 | + //提示消息 | ||
25 | + 'tip' => '', | ||
26 | + //成功消息 | ||
27 | + 'ok' => '', | ||
28 | + //扩展信息 | ||
29 | + 'extend' => '' | ||
30 | + ], | ||
31 | + [ | ||
32 | + 'name' => 'password', | ||
33 | + 'title' => '密码', | ||
34 | + 'type' => 'string', | ||
35 | + 'content' => [ | ||
36 | + ], | ||
37 | + 'value' => '', | ||
38 | + 'rule' => 'required', | ||
39 | + 'msg' => '', | ||
40 | + 'tip' => '', | ||
41 | + 'ok' => '', | ||
42 | + 'extend' => '' | ||
43 | + ], | ||
44 | +]; |
application/admin/command/Api.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command; | ||
4 | + | ||
5 | +use app\admin\command\Api\library\Builder; | ||
6 | +use think\Config; | ||
7 | +use think\console\Command; | ||
8 | +use think\console\Input; | ||
9 | +use think\console\input\Option; | ||
10 | +use think\console\Output; | ||
11 | +use think\Exception; | ||
12 | + | ||
13 | +class Api extends Command | ||
14 | +{ | ||
15 | + protected function configure() | ||
16 | + { | ||
17 | + $site = Config::get('site'); | ||
18 | + $this | ||
19 | + ->setName('api') | ||
20 | + ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') | ||
21 | + ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') | ||
22 | + ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') | ||
23 | + ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') | ||
24 | + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) | ||
25 | + ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'] ?? '') | ||
26 | + ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) | ||
27 | + ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') | ||
28 | + ->addOption('addon', 'a', Option::VALUE_OPTIONAL, 'addon name', null) | ||
29 | + ->addOption('controller', 'r', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name', null) | ||
30 | + ->setDescription('Build Api document from controller'); | ||
31 | + } | ||
32 | + | ||
33 | + protected function execute(Input $input, Output $output) | ||
34 | + { | ||
35 | + $apiDir = __DIR__ . DS . 'Api' . DS; | ||
36 | + | ||
37 | + $force = $input->getOption('force'); | ||
38 | + $url = $input->getOption('url'); | ||
39 | + $language = $input->getOption('language'); | ||
40 | + $template = $input->getOption('template'); | ||
41 | + if (!preg_match("/^([a-z0-9]+)\.html\$/i", $template)) { | ||
42 | + throw new Exception('template file not correct'); | ||
43 | + } | ||
44 | + $language = $language ? $language : 'zh-cn'; | ||
45 | + $langFile = $apiDir . 'lang' . DS . $language . '.php'; | ||
46 | + if (!is_file($langFile)) { | ||
47 | + throw new Exception('language file not found'); | ||
48 | + } | ||
49 | + $lang = include_once $langFile; | ||
50 | + // 目标目录 | ||
51 | + $output_dir = ROOT_PATH . 'public' . DS; | ||
52 | + $output_file = $output_dir . $input->getOption('output'); | ||
53 | + if (is_file($output_file) && !$force) { | ||
54 | + throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true "); | ||
55 | + } | ||
56 | + // 模板文件 | ||
57 | + $template_dir = $apiDir . 'template' . DS; | ||
58 | + $template_file = $template_dir . $template; | ||
59 | + if (!is_file($template_file)) { | ||
60 | + throw new Exception('template file not found'); | ||
61 | + } | ||
62 | + // 额外的类 | ||
63 | + $classes = $input->getOption('class'); | ||
64 | + // 标题 | ||
65 | + $title = $input->getOption('title'); | ||
66 | + // 模块 | ||
67 | + $module = $input->getOption('module'); | ||
68 | + // 插件 | ||
69 | + $addon = $input->getOption('addon'); | ||
70 | + | ||
71 | + $moduleDir = $addonDir = ''; | ||
72 | + if ($addon) { | ||
73 | + $addonInfo = get_addon_info($addon); | ||
74 | + if (!$addonInfo) { | ||
75 | + throw new Exception('addon not found'); | ||
76 | + } | ||
77 | + $moduleDir = ADDON_PATH . $addon . DS; | ||
78 | + } else { | ||
79 | + $moduleDir = APP_PATH . $module . DS; | ||
80 | + } | ||
81 | + if (!is_dir($moduleDir)) { | ||
82 | + throw new Exception('module not found'); | ||
83 | + } | ||
84 | + | ||
85 | + if (version_compare(PHP_VERSION, '7.0.0', '<')) { | ||
86 | + throw new Exception("Requires PHP version 7.0 or newer"); | ||
87 | + } | ||
88 | + | ||
89 | + //控制器名 | ||
90 | + $controller = $input->getOption('controller') ?: []; | ||
91 | + if (!$controller) { | ||
92 | + $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; | ||
93 | + $files = new \RecursiveIteratorIterator( | ||
94 | + new \RecursiveDirectoryIterator($controllerDir), | ||
95 | + \RecursiveIteratorIterator::LEAVES_ONLY | ||
96 | + ); | ||
97 | + | ||
98 | + foreach ($files as $name => $file) { | ||
99 | + if (!$file->isDir() && $file->getExtension() == 'php') { | ||
100 | + $filePath = $file->getRealPath(); | ||
101 | + $classes[] = $this->get_class_from_file($filePath); | ||
102 | + } | ||
103 | + } | ||
104 | + } else { | ||
105 | + foreach ($controller as $index => $item) { | ||
106 | + $filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php'; | ||
107 | + $classes[] = $this->get_class_from_file($filePath); | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | + $classes = array_unique(array_filter($classes)); | ||
112 | + | ||
113 | + $config = [ | ||
114 | + 'sitename' => config('site.name'), | ||
115 | + 'title' => $title, | ||
116 | + 'author' => config('site.name'), | ||
117 | + 'description' => '', | ||
118 | + 'apiurl' => $url, | ||
119 | + 'language' => $language, | ||
120 | + ]; | ||
121 | + | ||
122 | + $builder = new Builder($classes); | ||
123 | + $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); | ||
124 | + | ||
125 | + if (!file_put_contents($output_file, $content)) { | ||
126 | + throw new Exception('Cannot save the content to ' . $output_file); | ||
127 | + } | ||
128 | + $output->info("Build Successed!"); | ||
129 | + } | ||
130 | + | ||
131 | + /** | ||
132 | + * get full qualified class name | ||
133 | + * | ||
134 | + * @param string $path_to_file | ||
135 | + * @return string | ||
136 | + * @author JBYRNE http://jarretbyrne.com/2015/06/197/ | ||
137 | + */ | ||
138 | + protected function get_class_from_file($path_to_file) | ||
139 | + { | ||
140 | + //Grab the contents of the file | ||
141 | + $contents = file_get_contents($path_to_file); | ||
142 | + | ||
143 | + //Start with a blank namespace and class | ||
144 | + $namespace = $class = ""; | ||
145 | + | ||
146 | + //Set helper values to know that we have found the namespace/class token and need to collect the string values after them | ||
147 | + $getting_namespace = $getting_class = false; | ||
148 | + | ||
149 | + //Go through each token and evaluate it as necessary | ||
150 | + foreach (token_get_all($contents) as $token) { | ||
151 | + | ||
152 | + //If this token is the namespace declaring, then flag that the next tokens will be the namespace name | ||
153 | + if (is_array($token) && $token[0] == T_NAMESPACE) { | ||
154 | + $getting_namespace = true; | ||
155 | + } | ||
156 | + | ||
157 | + //If this token is the class declaring, then flag that the next tokens will be the class name | ||
158 | + if (is_array($token) && $token[0] == T_CLASS) { | ||
159 | + $getting_class = true; | ||
160 | + } | ||
161 | + | ||
162 | + //While we're grabbing the namespace name... | ||
163 | + if ($getting_namespace === true) { | ||
164 | + | ||
165 | + //If the token is a string or the namespace separator... | ||
166 | + if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) { | ||
167 | + | ||
168 | + //Append the token's value to the name of the namespace | ||
169 | + $namespace .= $token[1]; | ||
170 | + } elseif ($token === ';') { | ||
171 | + | ||
172 | + //If the token is the semicolon, then we're done with the namespace declaration | ||
173 | + $getting_namespace = false; | ||
174 | + } | ||
175 | + } | ||
176 | + | ||
177 | + //While we're grabbing the class name... | ||
178 | + if ($getting_class === true) { | ||
179 | + | ||
180 | + //If the token is a string, it's the name of the class | ||
181 | + if (is_array($token) && $token[0] == T_STRING) { | ||
182 | + | ||
183 | + //Store the token's value as the class name | ||
184 | + $class = $token[1]; | ||
185 | + | ||
186 | + //Got what we need, stope here | ||
187 | + break; | ||
188 | + } | ||
189 | + } | ||
190 | + } | ||
191 | + | ||
192 | + //Build the fully-qualified class name and return it | ||
193 | + return $namespace ? $namespace . '\\' . $class : $class; | ||
194 | + } | ||
195 | +} |
application/admin/command/Api/lang/zh-cn.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + 'Info' => '基础信息', | ||
5 | + 'Sandbox' => '在线测试', | ||
6 | + 'Sampleoutput' => '返回示例', | ||
7 | + 'Headers' => 'Headers', | ||
8 | + 'Parameters' => '参数', | ||
9 | + 'Body' => '正文', | ||
10 | + 'Name' => '名称', | ||
11 | + 'Type' => '类型', | ||
12 | + 'Required' => '必选', | ||
13 | + 'Description' => '描述', | ||
14 | + 'Send' => '提交', | ||
15 | + 'Reset' => '重置', | ||
16 | + 'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中', | ||
17 | + 'Apiurltips' => 'API接口URL', | ||
18 | + 'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中', | ||
19 | + 'Authorization' => '权限', | ||
20 | + 'NeedLogin' => '登录', | ||
21 | + 'NeedRight' => '鉴权', | ||
22 | + 'ReturnHeaders' => '响应头', | ||
23 | + 'ReturnParameters' => '返回参数', | ||
24 | + 'Response' => '响应输出', | ||
25 | +]; |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command\Api\library; | ||
4 | + | ||
5 | +use think\Config; | ||
6 | + | ||
7 | +/** | ||
8 | + * @website https://github.com/calinrada/php-apidoc | ||
9 | + * @author Calin Rada <rada.calin@gmail.com> | ||
10 | + * @author Karson <karson@fastadmin.net> | ||
11 | + */ | ||
12 | +class Builder | ||
13 | +{ | ||
14 | + | ||
15 | + /** | ||
16 | + * | ||
17 | + * @var \think\View | ||
18 | + */ | ||
19 | + public $view = null; | ||
20 | + | ||
21 | + /** | ||
22 | + * parse classes | ||
23 | + * @var array | ||
24 | + */ | ||
25 | + protected $classes = []; | ||
26 | + | ||
27 | + /** | ||
28 | + * | ||
29 | + * @param array $classes | ||
30 | + */ | ||
31 | + public function __construct($classes = []) | ||
32 | + { | ||
33 | + $this->classes = array_merge($this->classes, $classes); | ||
34 | + $this->view = new \think\View(Config::get('template'), Config::get('view_replace_str')); | ||
35 | + } | ||
36 | + | ||
37 | + protected function extractAnnotations() | ||
38 | + { | ||
39 | + foreach ($this->classes as $class) { | ||
40 | + $classAnnotation = Extractor::getClassAnnotations($class); | ||
41 | + // 如果忽略 | ||
42 | + if (isset($classAnnotation['ApiInternal'])) { | ||
43 | + continue; | ||
44 | + } | ||
45 | + Extractor::getClassMethodAnnotations($class); | ||
46 | + //Extractor::getClassPropertyValues($class); | ||
47 | + } | ||
48 | + $allClassAnnotation = Extractor::getAllClassAnnotations(); | ||
49 | + $allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations(); | ||
50 | + //$allClassPropertyValue = Extractor::getAllClassPropertyValues(); | ||
51 | + | ||
52 | +// foreach ($allClassMethodAnnotation as $className => &$methods) { | ||
53 | +// foreach ($methods as &$method) { | ||
54 | +// //权重判断 | ||
55 | +// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) { | ||
56 | +// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh']; | ||
57 | +// } | ||
58 | +// } | ||
59 | +// } | ||
60 | +// unset($methods); | ||
61 | + return [$allClassAnnotation, $allClassMethodAnnotation]; | ||
62 | + } | ||
63 | + | ||
64 | + protected function generateHeadersTemplate($docs) | ||
65 | + { | ||
66 | + if (!isset($docs['ApiHeaders'])) { | ||
67 | + return []; | ||
68 | + } | ||
69 | + | ||
70 | + $headerslist = array(); | ||
71 | + foreach ($docs['ApiHeaders'] as $params) { | ||
72 | + $tr = array( | ||
73 | + 'name' => $params['name'] ?? '', | ||
74 | + 'type' => $params['type'] ?? 'string', | ||
75 | + 'sample' => $params['sample'] ?? '', | ||
76 | + 'required' => $params['required'] ?? false, | ||
77 | + 'description' => $params['description'] ?? '', | ||
78 | + ); | ||
79 | + $headerslist[] = $tr; | ||
80 | + } | ||
81 | + | ||
82 | + return $headerslist; | ||
83 | + } | ||
84 | + | ||
85 | + protected function generateParamsTemplate($docs) | ||
86 | + { | ||
87 | + if (!isset($docs['ApiParams'])) { | ||
88 | + return []; | ||
89 | + } | ||
90 | + | ||
91 | + $paramslist = array(); | ||
92 | + foreach ($docs['ApiParams'] as $params) { | ||
93 | + $tr = array( | ||
94 | + 'name' => $params['name'], | ||
95 | + 'type' => $params['type'] ?? 'string', | ||
96 | + 'sample' => $params['sample'] ?? '', | ||
97 | + 'required' => $params['required'] ?? true, | ||
98 | + 'description' => $params['description'] ?? '', | ||
99 | + ); | ||
100 | + $paramslist[] = $tr; | ||
101 | + } | ||
102 | + | ||
103 | + return $paramslist; | ||
104 | + } | ||
105 | + | ||
106 | + protected function generateReturnHeadersTemplate($docs) | ||
107 | + { | ||
108 | + if (!isset($docs['ApiReturnHeaders'])) { | ||
109 | + return []; | ||
110 | + } | ||
111 | + | ||
112 | + $headerslist = array(); | ||
113 | + foreach ($docs['ApiReturnHeaders'] as $params) { | ||
114 | + $tr = array( | ||
115 | + 'name' => $params['name'] ?? '', | ||
116 | + 'type' => 'string', | ||
117 | + 'sample' => $params['sample'] ?? '', | ||
118 | + 'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No', | ||
119 | + 'description' => $params['description'] ?? '', | ||
120 | + ); | ||
121 | + $headerslist[] = $tr; | ||
122 | + } | ||
123 | + | ||
124 | + return $headerslist; | ||
125 | + } | ||
126 | + | ||
127 | + protected function generateReturnParamsTemplate($st_params) | ||
128 | + { | ||
129 | + if (!isset($st_params['ApiReturnParams'])) { | ||
130 | + return []; | ||
131 | + } | ||
132 | + | ||
133 | + $paramslist = array(); | ||
134 | + foreach ($st_params['ApiReturnParams'] as $params) { | ||
135 | + $tr = array( | ||
136 | + 'name' => $params['name'] ?? '', | ||
137 | + 'type' => $params['type'] ?? 'string', | ||
138 | + 'sample' => $params['sample'] ?? '', | ||
139 | + 'description' => $params['description'] ?? '', | ||
140 | + ); | ||
141 | + $paramslist[] = $tr; | ||
142 | + } | ||
143 | + | ||
144 | + return $paramslist; | ||
145 | + } | ||
146 | + | ||
147 | + protected function generateBadgeForMethod($data) | ||
148 | + { | ||
149 | + $method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]); | ||
150 | + $labes = array( | ||
151 | + 'POST' => 'label-primary', | ||
152 | + 'GET' => 'label-success', | ||
153 | + 'PUT' => 'label-warning', | ||
154 | + 'DELETE' => 'label-danger', | ||
155 | + 'PATCH' => 'label-default', | ||
156 | + 'OPTIONS' => 'label-info' | ||
157 | + ); | ||
158 | + | ||
159 | + return isset($labes[$method]) ? $labes[$method] : $labes['GET']; | ||
160 | + } | ||
161 | + | ||
162 | + public function parse() | ||
163 | + { | ||
164 | + list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations(); | ||
165 | + | ||
166 | + $sectorArr = []; | ||
167 | + foreach ($allClassAnnotations as $index => &$allClassAnnotation) { | ||
168 | + // 如果设置隐藏,则不显示在文档 | ||
169 | + if (isset($allClassAnnotation['ApiInternal'])) { | ||
170 | + continue; | ||
171 | + } | ||
172 | + $sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0]; | ||
173 | + $sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0; | ||
174 | + } | ||
175 | + unset($allClassAnnotation); | ||
176 | + | ||
177 | + arsort($sectorArr); | ||
178 | + $routes = include_once CONF_PATH . 'route.php'; | ||
179 | + $subdomain = false; | ||
180 | + if (config('url_domain_deploy') && isset($routes['__domain__']) && isset($routes['__domain__']['api']) && $routes['__domain__']['api']) { | ||
181 | + $subdomain = true; | ||
182 | + } | ||
183 | + $counter = 0; | ||
184 | + $section = null; | ||
185 | + $weigh = 0; | ||
186 | + $docsList = []; | ||
187 | + foreach ($allClassMethodAnnotations as $class => $methods) { | ||
188 | + foreach ($methods as $name => $docs) { | ||
189 | + if (isset($docs['ApiSector'][0])) { | ||
190 | + $section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0]; | ||
191 | + } else { | ||
192 | + $section = $class; | ||
193 | + } | ||
194 | + if (0 === count($docs)) { | ||
195 | + continue; | ||
196 | + } | ||
197 | + $route = is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0]; | ||
198 | + if ($subdomain) { | ||
199 | + $route = substr($route, 4); | ||
200 | + } | ||
201 | + $docsList[$section][$name] = [ | ||
202 | + 'id' => $counter, | ||
203 | + 'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0], | ||
204 | + 'methodLabel' => $this->generateBadgeForMethod($docs), | ||
205 | + 'section' => $section, | ||
206 | + 'route' => $route, | ||
207 | + 'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0], | ||
208 | + 'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0], | ||
209 | + 'body' => isset($docs['ApiBody'][0]) ? (is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0]) : '', | ||
210 | + 'headersList' => $this->generateHeadersTemplate($docs), | ||
211 | + 'paramsList' => $this->generateParamsTemplate($docs), | ||
212 | + 'returnHeadersList' => $this->generateReturnHeadersTemplate($docs), | ||
213 | + 'returnParamsList' => $this->generateReturnParamsTemplate($docs), | ||
214 | + 'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0], | ||
215 | + 'return' => isset($docs['ApiReturn']) ? (is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0]) : '', | ||
216 | + 'needLogin' => $docs['ApiPermissionLogin'][0], | ||
217 | + 'needRight' => $docs['ApiPermissionRight'][0], | ||
218 | + ]; | ||
219 | + $counter++; | ||
220 | + } | ||
221 | + } | ||
222 | + | ||
223 | + //重建排序 | ||
224 | + foreach ($docsList as $index => &$methods) { | ||
225 | + $methodSectorArr = []; | ||
226 | + foreach ($methods as $name => $method) { | ||
227 | + $methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0; | ||
228 | + } | ||
229 | + arsort($methodSectorArr); | ||
230 | + $methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods); | ||
231 | + } | ||
232 | + $docsList = array_merge(array_flip(array_keys($sectorArr)), $docsList); | ||
233 | + return $docsList; | ||
234 | + } | ||
235 | + | ||
236 | + public function getView() | ||
237 | + { | ||
238 | + return $this->view; | ||
239 | + } | ||
240 | + | ||
241 | + /** | ||
242 | + * 渲染 | ||
243 | + * @param string $template | ||
244 | + * @param array $vars | ||
245 | + * @return string | ||
246 | + */ | ||
247 | + public function render($template, $vars = []) | ||
248 | + { | ||
249 | + $docsList = $this->parse(); | ||
250 | + | ||
251 | + return $this->view->display(file_get_contents($template), array_merge($vars, ['docsList' => $docsList])); | ||
252 | + } | ||
253 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command\Api\library; | ||
4 | + | ||
5 | +use Exception; | ||
6 | + | ||
7 | +/** | ||
8 | + * Class imported from https://github.com/eriknyk/Annotations | ||
9 | + * @author Erik Amaru Ortiz https://github.com/eriknyk | ||
10 | + * | ||
11 | + * @license http://opensource.org/licenses/bsd-license.php The BSD License | ||
12 | + * @author Calin Rada <rada.calin@gmail.com> | ||
13 | + */ | ||
14 | +class Extractor | ||
15 | +{ | ||
16 | + | ||
17 | + /** | ||
18 | + * Static array to store already parsed annotations | ||
19 | + * @var array | ||
20 | + */ | ||
21 | + private static $annotationCache; | ||
22 | + | ||
23 | + private static $classAnnotationCache; | ||
24 | + | ||
25 | + private static $classMethodAnnotationCache; | ||
26 | + | ||
27 | + private static $classPropertyValueCache; | ||
28 | + | ||
29 | + /** | ||
30 | + * Indicates that annotations should has strict behavior, 'false' by default | ||
31 | + * @var boolean | ||
32 | + */ | ||
33 | + private $strict = false; | ||
34 | + | ||
35 | + /** | ||
36 | + * Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects() | ||
37 | + * @var string | ||
38 | + */ | ||
39 | + public $defaultNamespace = ''; | ||
40 | + | ||
41 | + /** | ||
42 | + * Sets strict variable to true/false | ||
43 | + * @param bool $value boolean value to indicate that annotations to has strict behavior | ||
44 | + */ | ||
45 | + public function setStrict($value) | ||
46 | + { | ||
47 | + $this->strict = (bool)$value; | ||
48 | + } | ||
49 | + | ||
50 | + /** | ||
51 | + * Sets default namespace to use in object instantiation | ||
52 | + * @param string $namespace default namespace | ||
53 | + */ | ||
54 | + public function setDefaultNamespace($namespace) | ||
55 | + { | ||
56 | + $this->defaultNamespace = $namespace; | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * Gets default namespace used in object instantiation | ||
61 | + * @return string $namespace default namespace | ||
62 | + */ | ||
63 | + public function getDefaultAnnotationNamespace() | ||
64 | + { | ||
65 | + return $this->defaultNamespace; | ||
66 | + } | ||
67 | + | ||
68 | + /** | ||
69 | + * Gets all anotations with pattern @SomeAnnotation() from a given class | ||
70 | + * | ||
71 | + * @param string $className class name to get annotations | ||
72 | + * @return array self::$classAnnotationCache all annotated elements | ||
73 | + */ | ||
74 | + public static function getClassAnnotations($className) | ||
75 | + { | ||
76 | + if (!isset(self::$classAnnotationCache[$className])) { | ||
77 | + $class = new \ReflectionClass($className); | ||
78 | + $annotationArr = self::parseAnnotations($class->getDocComment()); | ||
79 | + $annotationArr['ApiTitle'] = !isset($annotationArr['ApiTitle'][0]) || !trim($annotationArr['ApiTitle'][0]) ? [$class->getShortName()] : $annotationArr['ApiTitle']; | ||
80 | + self::$classAnnotationCache[$className] = $annotationArr; | ||
81 | + } | ||
82 | + | ||
83 | + return self::$classAnnotationCache[$className]; | ||
84 | + } | ||
85 | + | ||
86 | + /** | ||
87 | + * 获取类所有方法的属性配置 | ||
88 | + * @param $className | ||
89 | + * @return mixed | ||
90 | + * @throws \ReflectionException | ||
91 | + */ | ||
92 | + public static function getClassMethodAnnotations($className) | ||
93 | + { | ||
94 | + $class = new \ReflectionClass($className); | ||
95 | + | ||
96 | + foreach ($class->getMethods() as $object) { | ||
97 | + self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name); | ||
98 | + } | ||
99 | + | ||
100 | + return self::$classMethodAnnotationCache[$className]; | ||
101 | + } | ||
102 | + | ||
103 | + public static function getClassPropertyValues($className) | ||
104 | + { | ||
105 | + $class = new \ReflectionClass($className); | ||
106 | + | ||
107 | + foreach ($class->getProperties() as $object) { | ||
108 | + self::$classPropertyValueCache[$className][$object->name] = self::getClassPropertyValue($className, $object->name); | ||
109 | + } | ||
110 | + | ||
111 | + return self::$classMethodAnnotationCache[$className]; | ||
112 | + } | ||
113 | + | ||
114 | + public static function getAllClassAnnotations() | ||
115 | + { | ||
116 | + return self::$classAnnotationCache; | ||
117 | + } | ||
118 | + | ||
119 | + public static function getAllClassMethodAnnotations() | ||
120 | + { | ||
121 | + return self::$classMethodAnnotationCache; | ||
122 | + } | ||
123 | + | ||
124 | + public static function getAllClassPropertyValues() | ||
125 | + { | ||
126 | + return self::$classPropertyValueCache; | ||
127 | + } | ||
128 | + | ||
129 | + public static function getClassPropertyValue($className, $property) | ||
130 | + { | ||
131 | + $_SERVER['REQUEST_METHOD'] = 'GET'; | ||
132 | + $reflectionClass = new \ReflectionClass($className); | ||
133 | + $reflectionProperty = $reflectionClass->getProperty($property); | ||
134 | + $reflectionProperty->setAccessible(true); | ||
135 | + return $reflectionProperty->getValue($reflectionClass->newInstanceWithoutConstructor()); | ||
136 | + } | ||
137 | + | ||
138 | + /** | ||
139 | + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class | ||
140 | + * | ||
141 | + * @param string $className class name | ||
142 | + * @param string $methodName method name to get annotations | ||
143 | + * @return array self::$annotationCache all annotated elements of a method given | ||
144 | + */ | ||
145 | + public static function getMethodAnnotations($className, $methodName) | ||
146 | + { | ||
147 | + if (!isset(self::$annotationCache[$className . '::' . $methodName])) { | ||
148 | + try { | ||
149 | + $method = new \ReflectionMethod($className, $methodName); | ||
150 | + $class = new \ReflectionClass($className); | ||
151 | + if (!$method->isPublic() || $method->isConstructor()) { | ||
152 | + $annotations = array(); | ||
153 | + } else { | ||
154 | + $annotations = self::consolidateAnnotations($method, $class); | ||
155 | + } | ||
156 | + } catch (\ReflectionException $e) { | ||
157 | + $annotations = array(); | ||
158 | + } | ||
159 | + | ||
160 | + self::$annotationCache[$className . '::' . $methodName] = $annotations; | ||
161 | + } | ||
162 | + | ||
163 | + return self::$annotationCache[$className . '::' . $methodName]; | ||
164 | + } | ||
165 | + | ||
166 | + /** | ||
167 | + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class | ||
168 | + * and instance its abcAnnotation class | ||
169 | + * | ||
170 | + * @param string $className class name | ||
171 | + * @param string $methodName method name to get annotations | ||
172 | + * @return array self::$annotationCache all annotated objects of a method given | ||
173 | + */ | ||
174 | + public function getMethodAnnotationsObjects($className, $methodName) | ||
175 | + { | ||
176 | + $annotations = $this->getMethodAnnotations($className, $methodName); | ||
177 | + $objects = array(); | ||
178 | + | ||
179 | + $i = 0; | ||
180 | + | ||
181 | + foreach ($annotations as $annotationClass => $listParams) { | ||
182 | + $annotationClass = ucfirst($annotationClass); | ||
183 | + $class = $this->defaultNamespace . $annotationClass . 'Annotation'; | ||
184 | + | ||
185 | + // verify is the annotation class exists, depending if Annotations::strict is true | ||
186 | + // if not, just skip the annotation instance creation. | ||
187 | + if (!class_exists($class)) { | ||
188 | + if ($this->strict) { | ||
189 | + throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class)); | ||
190 | + } else { | ||
191 | + // silent skip & continue | ||
192 | + continue; | ||
193 | + } | ||
194 | + } | ||
195 | + | ||
196 | + if (empty($objects[$annotationClass])) { | ||
197 | + $objects[$annotationClass] = new $class(); | ||
198 | + } | ||
199 | + | ||
200 | + foreach ($listParams as $params) { | ||
201 | + if (is_array($params)) { | ||
202 | + foreach ($params as $key => $value) { | ||
203 | + $objects[$annotationClass]->set($key, $value); | ||
204 | + } | ||
205 | + } else { | ||
206 | + $objects[$annotationClass]->set($i++, $params); | ||
207 | + } | ||
208 | + } | ||
209 | + } | ||
210 | + | ||
211 | + return $objects; | ||
212 | + } | ||
213 | + | ||
214 | + private static function consolidateAnnotations($method, $class) | ||
215 | + { | ||
216 | + $dockblockClass = $class->getDocComment(); | ||
217 | + $docblockMethod = $method->getDocComment(); | ||
218 | + $methodName = $method->getName(); | ||
219 | + | ||
220 | + $methodAnnotations = self::parseAnnotations($docblockMethod); | ||
221 | + $methodAnnotations['ApiTitle'] = !isset($methodAnnotations['ApiTitle'][0]) || !trim($methodAnnotations['ApiTitle'][0]) ? [$method->getName()] : $methodAnnotations['ApiTitle']; | ||
222 | + | ||
223 | + $classAnnotations = self::parseAnnotations($dockblockClass); | ||
224 | + $classAnnotations['ApiTitle'] = !isset($classAnnotations['ApiTitle'][0]) || !trim($classAnnotations['ApiTitle'][0]) ? [$class->getShortName()] : $classAnnotations['ApiTitle']; | ||
225 | + | ||
226 | + if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') { | ||
227 | + return []; | ||
228 | + } | ||
229 | + | ||
230 | + $properties = $class->getDefaultProperties(); | ||
231 | + $noNeedLogin = isset($properties['noNeedLogin']) ? (is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']]) : []; | ||
232 | + $noNeedRight = isset($properties['noNeedRight']) ? (is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']]) : []; | ||
233 | + | ||
234 | + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr); | ||
235 | + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr); | ||
236 | + | ||
237 | + if (!isset($methodAnnotations['ApiMethod'])) { | ||
238 | + $methodAnnotations['ApiMethod'] = ['get']; | ||
239 | + } | ||
240 | + if (!isset($methodAnnotations['ApiWeigh'])) { | ||
241 | + $methodAnnotations['ApiWeigh'] = [0]; | ||
242 | + } | ||
243 | + if (!isset($methodAnnotations['ApiSummary'])) { | ||
244 | + $methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle']; | ||
245 | + } | ||
246 | + | ||
247 | + if ($methodAnnotations) { | ||
248 | + foreach ($classAnnotations as $name => $valueClass) { | ||
249 | + if (count($valueClass) !== 1) { | ||
250 | + continue; | ||
251 | + } | ||
252 | + | ||
253 | + if ($name === 'ApiRoute') { | ||
254 | + if (isset($methodAnnotations[$name])) { | ||
255 | + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]]; | ||
256 | + } else { | ||
257 | + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()]; | ||
258 | + } | ||
259 | + } | ||
260 | + | ||
261 | + if ($name === 'ApiSector') { | ||
262 | + $methodAnnotations[$name] = $valueClass; | ||
263 | + } | ||
264 | + } | ||
265 | + } | ||
266 | + if (!isset($methodAnnotations['ApiRoute'])) { | ||
267 | + $urlArr = []; | ||
268 | + $className = $class->getName(); | ||
269 | + | ||
270 | + list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className); | ||
271 | + $prefixArr = explode('\\', $prefix); | ||
272 | + $suffixArr = explode('\\', $suffix); | ||
273 | + if ($prefixArr[0] == \think\Config::get('app_namespace')) { | ||
274 | + $prefixArr[0] = ''; | ||
275 | + } | ||
276 | + $urlArr = array_merge($urlArr, $prefixArr); | ||
277 | + $urlArr[] = implode('.', array_map(function ($item) { | ||
278 | + return \think\Loader::parseName($item); | ||
279 | + }, $suffixArr)); | ||
280 | + $urlArr[] = $method->getName(); | ||
281 | + | ||
282 | + $methodAnnotations['ApiRoute'] = [implode('/', $urlArr)]; | ||
283 | + } | ||
284 | + if (!isset($methodAnnotations['ApiSector'])) { | ||
285 | + $methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : $classAnnotations['ApiTitle']; | ||
286 | + } | ||
287 | + if (!isset($methodAnnotations['ApiParams'])) { | ||
288 | + $params = self::parseCustomAnnotations($docblockMethod, 'param'); | ||
289 | + foreach ($params as $k => $v) { | ||
290 | + $arr = explode(' ', preg_replace("/[\s]+/", " ", $v)); | ||
291 | + $methodAnnotations['ApiParams'][] = [ | ||
292 | + 'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '', | ||
293 | + 'nullable' => false, | ||
294 | + 'type' => isset($arr[0]) ? $arr[0] : 'string', | ||
295 | + 'description' => isset($arr[2]) ? $arr[2] : '' | ||
296 | + ]; | ||
297 | + } | ||
298 | + } | ||
299 | + $methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)]; | ||
300 | + $methodAnnotations['ApiPermissionRight'] = !$methodAnnotations['ApiPermissionLogin'][0] ? [false] : [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)]; | ||
301 | + return $methodAnnotations; | ||
302 | + } | ||
303 | + | ||
304 | + /** | ||
305 | + * Parse annotations | ||
306 | + * | ||
307 | + * @param string $docblock | ||
308 | + * @param string $name | ||
309 | + * @return array parsed annotations params | ||
310 | + */ | ||
311 | + private static function parseCustomAnnotations($docblock, $name = 'param') | ||
312 | + { | ||
313 | + $annotations = array(); | ||
314 | + | ||
315 | + $docblock = substr($docblock, 3, -2); | ||
316 | + if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) { | ||
317 | + foreach ($matches[1] as $k => $v) { | ||
318 | + $annotations[] = $v; | ||
319 | + } | ||
320 | + } | ||
321 | + return $annotations; | ||
322 | + } | ||
323 | + | ||
324 | + /** | ||
325 | + * Parse annotations | ||
326 | + * | ||
327 | + * @param string $docblock | ||
328 | + * @return array parsed annotations params | ||
329 | + */ | ||
330 | + private static function parseAnnotations($docblock) | ||
331 | + { | ||
332 | + $annotations = array(); | ||
333 | + | ||
334 | + // Strip away the docblock header and footer to ease parsing of one line annotations | ||
335 | + $docblock = substr($docblock, 3, -2); | ||
336 | + if (preg_match_all('/@(?<name>[A-Za-z_-]+)[\s\t]*\((?<args>(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) { | ||
337 | + $numMatches = count($matches[0]); | ||
338 | + for ($i = 0; $i < $numMatches; ++$i) { | ||
339 | + $name = $matches['name'][$i]; | ||
340 | + $value = ''; | ||
341 | + // annotations has arguments | ||
342 | + if (isset($matches['args'][$i])) { | ||
343 | + $argsParts = trim($matches['args'][$i]); | ||
344 | + if ($name == 'ApiReturn') { | ||
345 | + $value = $argsParts; | ||
346 | + } elseif ($matches['args'][$i] != '') { | ||
347 | + $argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts); | ||
348 | + $value = self::parseArgs($argsParts); | ||
349 | + if (is_string($value)) { | ||
350 | + $value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts); | ||
351 | + } | ||
352 | + } | ||
353 | + } | ||
354 | + | ||
355 | + $annotations[$name][] = $value; | ||
356 | + } | ||
357 | + } | ||
358 | + if (stripos($docblock, '@ApiInternal') !== false) { | ||
359 | + $annotations['ApiInternal'] = [true]; | ||
360 | + } | ||
361 | + if (!isset($annotations['ApiTitle'])) { | ||
362 | + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr); | ||
363 | + $title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : ''; | ||
364 | + $annotations['ApiTitle'] = [$title]; | ||
365 | + } | ||
366 | + | ||
367 | + return $annotations; | ||
368 | + } | ||
369 | + | ||
370 | + /** | ||
371 | + * Parse individual annotation arguments | ||
372 | + * | ||
373 | + * @param string $content arguments string | ||
374 | + * @return array annotated arguments | ||
375 | + */ | ||
376 | + private static function parseArgs($content) | ||
377 | + { | ||
378 | + // Replace initial stars | ||
379 | + $content = preg_replace('/^\s*\*/m', '', $content); | ||
380 | + | ||
381 | + $data = array(); | ||
382 | + $len = strlen($content); | ||
383 | + $i = 0; | ||
384 | + $var = ''; | ||
385 | + $val = ''; | ||
386 | + $level = 1; | ||
387 | + | ||
388 | + $prevDelimiter = ''; | ||
389 | + $nextDelimiter = ''; | ||
390 | + $nextToken = ''; | ||
391 | + $composing = false; | ||
392 | + $type = 'plain'; | ||
393 | + $delimiter = null; | ||
394 | + $quoted = false; | ||
395 | + $tokens = array('"', '"', '{', '}', ',', '='); | ||
396 | + | ||
397 | + while ($i <= $len) { | ||
398 | + $prev_c = substr($content, $i - 1, 1); | ||
399 | + $c = substr($content, $i++, 1); | ||
400 | + | ||
401 | + if ($c === '"' && $prev_c !== "\\") { | ||
402 | + $delimiter = $c; | ||
403 | + //open delimiter | ||
404 | + if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) { | ||
405 | + $prevDelimiter = $nextDelimiter = $delimiter; | ||
406 | + $val = ''; | ||
407 | + $composing = true; | ||
408 | + $quoted = true; | ||
409 | + } else { | ||
410 | + // close delimiter | ||
411 | + if ($c !== $nextDelimiter) { | ||
412 | + throw new Exception(sprintf( | ||
413 | + "Parse Error: enclosing error -> expected: [%s], given: [%s]", | ||
414 | + $nextDelimiter, | ||
415 | + $c | ||
416 | + )); | ||
417 | + } | ||
418 | + | ||
419 | + // validating syntax | ||
420 | + if ($i < $len) { | ||
421 | + if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) { | ||
422 | + throw new Exception(sprintf( | ||
423 | + "Parse Error: missing comma separator near: ...%s<--", | ||
424 | + substr($content, ($i - 10), $i) | ||
425 | + )); | ||
426 | + } | ||
427 | + } | ||
428 | + | ||
429 | + $prevDelimiter = $nextDelimiter = ''; | ||
430 | + $composing = false; | ||
431 | + $delimiter = null; | ||
432 | + } | ||
433 | + } elseif (!$composing && in_array($c, $tokens)) { | ||
434 | + switch ($c) { | ||
435 | + case '=': | ||
436 | + $prevDelimiter = $nextDelimiter = ''; | ||
437 | + $level = 2; | ||
438 | + $composing = false; | ||
439 | + $type = 'assoc'; | ||
440 | + $quoted = false; | ||
441 | + break; | ||
442 | + case ',': | ||
443 | + $level = 3; | ||
444 | + | ||
445 | + // If composing flag is true yet, | ||
446 | + // it means that the string was not enclosed, so it is parsing error. | ||
447 | + if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) { | ||
448 | + throw new Exception(sprintf( | ||
449 | + "Parse Error: enclosing error -> expected: [%s], given: [%s]", | ||
450 | + $nextDelimiter, | ||
451 | + $c | ||
452 | + )); | ||
453 | + } | ||
454 | + | ||
455 | + $prevDelimiter = $nextDelimiter = ''; | ||
456 | + break; | ||
457 | + case '{': | ||
458 | + $subc = ''; | ||
459 | + $subComposing = true; | ||
460 | + | ||
461 | + while ($i <= $len) { | ||
462 | + $c = substr($content, $i++, 1); | ||
463 | + | ||
464 | + if (isset($delimiter) && $c === $delimiter) { | ||
465 | + throw new Exception(sprintf( | ||
466 | + "Parse Error: Composite variable is not enclosed correctly." | ||
467 | + )); | ||
468 | + } | ||
469 | + | ||
470 | + if ($c === '}') { | ||
471 | + $subComposing = false; | ||
472 | + break; | ||
473 | + } | ||
474 | + $subc .= $c; | ||
475 | + } | ||
476 | + | ||
477 | + // if the string is composing yet means that the structure of var. never was enclosed with '}' | ||
478 | + if ($subComposing) { | ||
479 | + throw new Exception(sprintf( | ||
480 | + "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", | ||
481 | + $subc | ||
482 | + )); | ||
483 | + } | ||
484 | + | ||
485 | + $val = self::parseArgs($subc); | ||
486 | + break; | ||
487 | + } | ||
488 | + } else { | ||
489 | + if ($level == 1) { | ||
490 | + $var .= $c; | ||
491 | + } elseif ($level == 2) { | ||
492 | + $val .= $c; | ||
493 | + } | ||
494 | + } | ||
495 | + | ||
496 | + if ($level === 3 || $i === $len) { | ||
497 | + if ($type == 'plain' && $i === $len) { | ||
498 | + $data = self::castValue($var); | ||
499 | + } else { | ||
500 | + $data[trim($var)] = self::castValue($val, !$quoted); | ||
501 | + } | ||
502 | + | ||
503 | + $level = 1; | ||
504 | + $var = $val = ''; | ||
505 | + $composing = false; | ||
506 | + $quoted = false; | ||
507 | + } | ||
508 | + } | ||
509 | + | ||
510 | + return $data; | ||
511 | + } | ||
512 | + | ||
513 | + /** | ||
514 | + * Try determinate the original type variable of a string | ||
515 | + * | ||
516 | + * @param string $val string containing possibles variables that can be cast to bool or int | ||
517 | + * @param boolean $trim indicate if the value passed should be trimmed after to try cast | ||
518 | + * @return mixed returns the value converted to original type if was possible | ||
519 | + */ | ||
520 | + private static function castValue($val, $trim = false) | ||
521 | + { | ||
522 | + if (is_array($val)) { | ||
523 | + foreach ($val as $key => $value) { | ||
524 | + $val[$key] = self::castValue($value); | ||
525 | + } | ||
526 | + } elseif (is_string($val)) { | ||
527 | + if ($trim) { | ||
528 | + $val = trim($val); | ||
529 | + } | ||
530 | + $val = stripslashes($val); | ||
531 | + $tmp = strtolower($val); | ||
532 | + | ||
533 | + if ($tmp === 'false' || $tmp === 'true') { | ||
534 | + $val = $tmp === 'true'; | ||
535 | + } elseif (is_numeric($val)) { | ||
536 | + return $val + 0; | ||
537 | + } | ||
538 | + | ||
539 | + unset($tmp); | ||
540 | + } | ||
541 | + | ||
542 | + return $val; | ||
543 | + } | ||
544 | +} |
1 | +<!DOCTYPE html> | ||
2 | +<html> | ||
3 | + <head> | ||
4 | + <meta charset="utf-8"> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
7 | + <meta name="description" content=""> | ||
8 | + <title>{$config.title}</title> | ||
9 | + | ||
10 | + <!-- Bootstrap Core CSS --> | ||
11 | + <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> | ||
12 | + | ||
13 | + <!-- Plugin CSS --> | ||
14 | + <link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> | ||
15 | + | ||
16 | + <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | ||
17 | + <!--[if lt IE 9]> | ||
18 | + <script src="https://cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script> | ||
19 | + <script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> | ||
20 | + <![endif]--> | ||
21 | + | ||
22 | + <style type="text/css"> | ||
23 | + body { | ||
24 | + padding-top: 70px; margin-bottom: 15px; | ||
25 | + -webkit-font-smoothing: antialiased; | ||
26 | + -moz-osx-font-smoothing: grayscale; | ||
27 | + 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; | ||
28 | + font-weight: 400; | ||
29 | + } | ||
30 | + h2 { font-size: 1.2em; } | ||
31 | + hr { margin-top: 10px; } | ||
32 | + .tab-pane { padding-top: 10px; } | ||
33 | + .mt0 { margin-top: 0px; } | ||
34 | + .footer { font-size: 12px; color: #666; } | ||
35 | + .docs-list .label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; } | ||
36 | + .string { color: green; } | ||
37 | + .number { color: darkorange; } | ||
38 | + .boolean { color: blue; } | ||
39 | + .null { color: magenta; } | ||
40 | + .key { color: red; } | ||
41 | + .popover { max-width: 400px; max-height: 400px; overflow-y: auto;} | ||
42 | + .list-group.panel > .list-group-item { | ||
43 | + } | ||
44 | + .list-group-item:last-child { | ||
45 | + border-radius:0; | ||
46 | + } | ||
47 | + h4.panel-title a { | ||
48 | + font-weight:normal; | ||
49 | + font-size:14px; | ||
50 | + } | ||
51 | + h4.panel-title a .text-muted { | ||
52 | + font-size:12px; | ||
53 | + font-weight:normal; | ||
54 | + font-family: 'Verdana'; | ||
55 | + } | ||
56 | + #sidebar { | ||
57 | + width: 220px; | ||
58 | + position: fixed; | ||
59 | + margin-left: -240px; | ||
60 | + overflow-y:auto; | ||
61 | + } | ||
62 | + #sidebar > .list-group { | ||
63 | + margin-bottom:0; | ||
64 | + } | ||
65 | + #sidebar > .list-group > a{ | ||
66 | + text-indent:0; | ||
67 | + } | ||
68 | + #sidebar .child > a .tag{ | ||
69 | + position: absolute; | ||
70 | + right: 10px; | ||
71 | + top: 11px; | ||
72 | + } | ||
73 | + #sidebar .child > a .pull-right{ | ||
74 | + margin-left:3px; | ||
75 | + } | ||
76 | + #sidebar .child { | ||
77 | + border:1px solid #ddd; | ||
78 | + border-bottom:none; | ||
79 | + } | ||
80 | + #sidebar .child:last-child { | ||
81 | + border-bottom:1px solid #ddd; | ||
82 | + } | ||
83 | + #sidebar .child > a { | ||
84 | + border:0; | ||
85 | + min-height: 40px; | ||
86 | + } | ||
87 | + #sidebar .list-group a.current { | ||
88 | + background:#f5f5f5; | ||
89 | + } | ||
90 | + @media (max-width: 1620px){ | ||
91 | + #sidebar { | ||
92 | + margin:0; | ||
93 | + } | ||
94 | + #accordion { | ||
95 | + padding-left:235px; | ||
96 | + } | ||
97 | + } | ||
98 | + @media (max-width: 768px){ | ||
99 | + #sidebar { | ||
100 | + display: none; | ||
101 | + } | ||
102 | + #accordion { | ||
103 | + padding-left:0px; | ||
104 | + } | ||
105 | + } | ||
106 | + .label-primary { | ||
107 | + background-color: #248aff; | ||
108 | + } | ||
109 | + .docs-list .panel .panel-body .table { | ||
110 | + margin-bottom: 0; | ||
111 | + } | ||
112 | + | ||
113 | + </style> | ||
114 | + </head> | ||
115 | + <body> | ||
116 | + <!-- Fixed navbar --> | ||
117 | + <div class="navbar navbar-default navbar-fixed-top" role="navigation"> | ||
118 | + <div class="container"> | ||
119 | + <div class="navbar-header"> | ||
120 | + <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> | ||
121 | + <span class="sr-only">Toggle navigation</span> | ||
122 | + <span class="icon-bar"></span> | ||
123 | + <span class="icon-bar"></span> | ||
124 | + <span class="icon-bar"></span> | ||
125 | + </button> | ||
126 | + <a class="navbar-brand" href="./" target="_blank">{$config.title}</a> | ||
127 | + </div> | ||
128 | + <div class="navbar-collapse collapse"> | ||
129 | + <form class="navbar-form navbar-right"> | ||
130 | + <div class="form-group"> | ||
131 | + Token: | ||
132 | + </div> | ||
133 | + <div class="form-group"> | ||
134 | + <input type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Tokentips}" placeholder="token" id="token" /> | ||
135 | + </div> | ||
136 | + <div class="form-group"> | ||
137 | + Apiurl: | ||
138 | + </div> | ||
139 | + <div class="form-group"> | ||
140 | + <input id="apiUrl" type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Apiurltips}" placeholder="https://api.mydomain.com" value="{$config.apiurl}" /> | ||
141 | + </div> | ||
142 | + <div class="form-group"> | ||
143 | + <button type="button" class="btn btn-success btn-sm" data-toggle="tooltip" title="{$lang.Savetips}" id="save_data"> | ||
144 | + <span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> | ||
145 | + </button> | ||
146 | + </div> | ||
147 | + </form> | ||
148 | + </div><!--/.nav-collapse --> | ||
149 | + </div> | ||
150 | + </div> | ||
151 | + | ||
152 | + <div class="container"> | ||
153 | + <!-- menu --> | ||
154 | + <div id="sidebar"> | ||
155 | + <div class="list-group panel"> | ||
156 | + {foreach name="docsList" id="docs"} | ||
157 | + <a href="#{$key}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a> | ||
158 | + <div class="child collapse" id="{$key}"> | ||
159 | + {foreach name="docs" id="api" } | ||
160 | + <a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title} | ||
161 | + <span class="tag"> | ||
162 | + {if $api.needRight} | ||
163 | + <span class="label label-danger pull-right">鉴</span> | ||
164 | + {/if} | ||
165 | + {if $api.needLogin} | ||
166 | + <span class="label label-success pull-right noneedlogin">登</span> | ||
167 | + {/if} | ||
168 | + </span> | ||
169 | + </a> | ||
170 | + {/foreach} | ||
171 | + </div> | ||
172 | + {/foreach} | ||
173 | + </div> | ||
174 | + </div> | ||
175 | + <div class="panel-group docs-list" id="accordion"> | ||
176 | + {foreach name="docsList" id="docs"} | ||
177 | + <h2>{$key}</h2> | ||
178 | + <hr> | ||
179 | + {foreach name="docs" id="api" } | ||
180 | + <div class="panel panel-default"> | ||
181 | + <div class="panel-heading" id="heading-{$api.id}"> | ||
182 | + <h4 class="panel-title"> | ||
183 | + <span class="label {$api.methodLabel}">{$api.method|strtoupper}</span> | ||
184 | + <a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></a> | ||
185 | + </h4> | ||
186 | + </div> | ||
187 | + <div id="collapseOne{$api.id}" class="panel-collapse collapse"> | ||
188 | + <div class="panel-body"> | ||
189 | + | ||
190 | + <!-- Nav tabs --> | ||
191 | + <ul class="nav nav-tabs" id="doctab{$api.id}"> | ||
192 | + <li class="active"><a href="#info{$api.id}" data-toggle="tab">{$lang.Info}</a></li> | ||
193 | + <li><a href="#sandbox{$api.id}" data-toggle="tab">{$lang.Sandbox}</a></li> | ||
194 | + <li><a href="#sample{$api.id}" data-toggle="tab">{$lang.Sampleoutput}</a></li> | ||
195 | + </ul> | ||
196 | + | ||
197 | + <!-- Tab panes --> | ||
198 | + <div class="tab-content"> | ||
199 | + | ||
200 | + <div class="tab-pane active" id="info{$api.id}"> | ||
201 | + <div class="well"> | ||
202 | + {$api.summary} | ||
203 | + </div> | ||
204 | + <div class="panel panel-default"> | ||
205 | + <div class="panel-heading"><strong>{$lang.Authorization}</strong></div> | ||
206 | + <div class="panel-body"> | ||
207 | + <table class="table table-hover"> | ||
208 | + <tbody> | ||
209 | + <tr> | ||
210 | + <td>{$lang.NeedLogin}</td> | ||
211 | + <td>{$api.needLogin?'是':'否'}</td> | ||
212 | + </tr> | ||
213 | + <tr> | ||
214 | + <td>{$lang.NeedRight}</td> | ||
215 | + <td>{$api.needRight?'是':'否'}</td> | ||
216 | + </tr> | ||
217 | + </tbody> | ||
218 | + </table> | ||
219 | + </div> | ||
220 | + </div> | ||
221 | + <div class="panel panel-default"> | ||
222 | + <div class="panel-heading"><strong>{$lang.Headers}</strong></div> | ||
223 | + <div class="panel-body"> | ||
224 | + {if $api.headersList} | ||
225 | + <table class="table table-hover"> | ||
226 | + <thead> | ||
227 | + <tr> | ||
228 | + <th>{$lang.Name}</th> | ||
229 | + <th>{$lang.Type}</th> | ||
230 | + <th>{$lang.Required}</th> | ||
231 | + <th>{$lang.Description}</th> | ||
232 | + </tr> | ||
233 | + </thead> | ||
234 | + <tbody> | ||
235 | + {foreach name="api['headersList']" id="header"} | ||
236 | + <tr> | ||
237 | + <td>{$header.name}</td> | ||
238 | + <td>{$header.type}</td> | ||
239 | + <td>{$header.required?'是':'否'}</td> | ||
240 | + <td>{$header.description}</td> | ||
241 | + </tr> | ||
242 | + {/foreach} | ||
243 | + </tbody> | ||
244 | + </table> | ||
245 | + {else /} | ||
246 | + 无 | ||
247 | + {/if} | ||
248 | + </div> | ||
249 | + </div> | ||
250 | + <div class="panel panel-default"> | ||
251 | + <div class="panel-heading"><strong>{$lang.Parameters}</strong></div> | ||
252 | + <div class="panel-body"> | ||
253 | + {if $api.paramsList} | ||
254 | + <table class="table table-hover"> | ||
255 | + <thead> | ||
256 | + <tr> | ||
257 | + <th>{$lang.Name}</th> | ||
258 | + <th>{$lang.Type}</th> | ||
259 | + <th>{$lang.Required}</th> | ||
260 | + <th>{$lang.Description}</th> | ||
261 | + </tr> | ||
262 | + </thead> | ||
263 | + <tbody> | ||
264 | + {foreach name="api['paramsList']" id="param"} | ||
265 | + <tr> | ||
266 | + <td>{$param.name}</td> | ||
267 | + <td>{$param.type}</td> | ||
268 | + <td>{:$param.required?'是':'否'}</td> | ||
269 | + <td>{$param.description}</td> | ||
270 | + </tr> | ||
271 | + {/foreach} | ||
272 | + </tbody> | ||
273 | + </table> | ||
274 | + {else /} | ||
275 | + 无 | ||
276 | + {/if} | ||
277 | + </div> | ||
278 | + </div> | ||
279 | + <div class="panel panel-default"> | ||
280 | + <div class="panel-heading"><strong>{$lang.Body}</strong></div> | ||
281 | + <div class="panel-body"> | ||
282 | + {$api.body|default='无'} | ||
283 | + </div> | ||
284 | + </div> | ||
285 | + </div><!-- #info --> | ||
286 | + | ||
287 | + <div class="tab-pane" id="sandbox{$api.id}"> | ||
288 | + <div class="row"> | ||
289 | + <div class="col-md-12"> | ||
290 | + {if $api.headersList} | ||
291 | + <div class="panel panel-default"> | ||
292 | + <div class="panel-heading"><strong>{$lang.Headers}</strong></div> | ||
293 | + <div class="panel-body"> | ||
294 | + <div class="headers"> | ||
295 | + {foreach name="api['headersList']" id="param"} | ||
296 | + <div class="form-group"> | ||
297 | + <label class="control-label" for="{$param.name}">{$param.name}</label> | ||
298 | + <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}"> | ||
299 | + </div> | ||
300 | + {/foreach} | ||
301 | + </div> | ||
302 | + </div> | ||
303 | + </div> | ||
304 | + {/if} | ||
305 | + <div class="panel panel-default"> | ||
306 | + <div class="panel-heading"><strong>{$lang.Parameters}</strong> | ||
307 | + <div class="pull-right"> | ||
308 | + <a href="javascript:" class="btn btn-xs btn-info btn-append">追加</a> | ||
309 | + </div> | ||
310 | + </div> | ||
311 | + <div class="panel-body"> | ||
312 | + <form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}"> | ||
313 | + {if $api.paramsList} | ||
314 | + {foreach name="api['paramsList']" id="param"} | ||
315 | + <div class="form-group"> | ||
316 | + <label class="control-label" for="{$param.name}">{$param.name}</label> | ||
317 | + <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}"> | ||
318 | + </div> | ||
319 | + {/foreach} | ||
320 | + {else /} | ||
321 | + <div class="form-group"> | ||
322 | + 无 | ||
323 | + </div> | ||
324 | + {/if} | ||
325 | + <div class="form-group form-group-submit"> | ||
326 | + <button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button> | ||
327 | + <button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button> | ||
328 | + </div> | ||
329 | + </form> | ||
330 | + </div> | ||
331 | + </div> | ||
332 | + <div class="panel panel-default"> | ||
333 | + <div class="panel-heading"><strong>{$lang.Response}</strong></div> | ||
334 | + <div class="panel-body"> | ||
335 | + <div class="row"> | ||
336 | + <div class="col-md-12" style="overflow-x:auto"> | ||
337 | + <pre id="response_headers{$api.id}"></pre> | ||
338 | + <pre id="response{$api.id}"></pre> | ||
339 | + </div> | ||
340 | + </div> | ||
341 | + </div> | ||
342 | + </div> | ||
343 | + <div class="panel panel-default"> | ||
344 | + <div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div> | ||
345 | + <div class="panel-body"> | ||
346 | + {if $api.returnParamsList} | ||
347 | + <table class="table table-hover"> | ||
348 | + <thead> | ||
349 | + <tr> | ||
350 | + <th>{$lang.Name}</th> | ||
351 | + <th>{$lang.Type}</th> | ||
352 | + <th>{$lang.Description}</th> | ||
353 | + </tr> | ||
354 | + </thead> | ||
355 | + <tbody> | ||
356 | + {foreach name="api['returnParamsList']" id="param"} | ||
357 | + <tr> | ||
358 | + <td>{$param.name}</td> | ||
359 | + <td>{$param.type}</td> | ||
360 | + <td>{$param.description}</td> | ||
361 | + </tr> | ||
362 | + {/foreach} | ||
363 | + </tbody> | ||
364 | + </table> | ||
365 | + {else /} | ||
366 | + 无 | ||
367 | + {/if} | ||
368 | + </div> | ||
369 | + </div> | ||
370 | + </div> | ||
371 | + </div> | ||
372 | + </div><!-- #sandbox --> | ||
373 | + | ||
374 | + <div class="tab-pane" id="sample{$api.id}"> | ||
375 | + <div class="row"> | ||
376 | + <div class="col-md-12"> | ||
377 | + <pre id="sample_response{$api.id}">{$api.return|default='无'}</pre> | ||
378 | + </div> | ||
379 | + </div> | ||
380 | + </div><!-- #sample --> | ||
381 | + | ||
382 | + </div><!-- .tab-content --> | ||
383 | + </div> | ||
384 | + </div> | ||
385 | + </div> | ||
386 | + {/foreach} | ||
387 | + {/foreach} | ||
388 | + </div> | ||
389 | + | ||
390 | + <hr> | ||
391 | + | ||
392 | + <div class="row mt0 footer"> | ||
393 | + <div class="col-md-6" align="left"> | ||
394 | + | ||
395 | + </div> | ||
396 | + <div class="col-md-6" align="right"> | ||
397 | + Generated on {:date('Y-m-d H:i:s')} <a href="./" target="_blank">{$config.sitename}</a> | ||
398 | + </div> | ||
399 | + </div> | ||
400 | + | ||
401 | + </div> <!-- /container --> | ||
402 | + | ||
403 | + <!-- jQuery --> | ||
404 | + <script src="https://cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script> | ||
405 | + | ||
406 | + <!-- Bootstrap Core JavaScript --> | ||
407 | + <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> | ||
408 | + | ||
409 | + <script type="text/javascript"> | ||
410 | + function syntaxHighlight(json) { | ||
411 | + if (typeof json != 'string') { | ||
412 | + json = JSON.stringify(json, undefined, 2); | ||
413 | + } | ||
414 | + json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | ||
415 | + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { | ||
416 | + var cls = 'number'; | ||
417 | + if (/^"/.test(match)) { | ||
418 | + if (/:$/.test(match)) { | ||
419 | + cls = 'key'; | ||
420 | + } else { | ||
421 | + cls = 'string'; | ||
422 | + } | ||
423 | + } else if (/true|false/.test(match)) { | ||
424 | + cls = 'boolean'; | ||
425 | + } else if (/null/.test(match)) { | ||
426 | + cls = 'null'; | ||
427 | + } | ||
428 | + return '<span class="' + cls + '">' + match + '</span>'; | ||
429 | + }); | ||
430 | + } | ||
431 | + | ||
432 | + function prepareStr(str) { | ||
433 | + try { | ||
434 | + return syntaxHighlight(JSON.stringify(JSON.parse(str.replace(/'/g, '"')), null, 2)); | ||
435 | + } catch (e) { | ||
436 | + return str; | ||
437 | + } | ||
438 | + } | ||
439 | + var storage = (function () { | ||
440 | + var uid = new Date; | ||
441 | + var storage; | ||
442 | + var result; | ||
443 | + try { | ||
444 | + (storage = window.localStorage).setItem(uid, uid); | ||
445 | + result = storage.getItem(uid) == uid; | ||
446 | + storage.removeItem(uid); | ||
447 | + return result && storage; | ||
448 | + } catch (exception) { | ||
449 | + } | ||
450 | + }()); | ||
451 | + | ||
452 | + $.fn.serializeObject = function () | ||
453 | + { | ||
454 | + var o = {}; | ||
455 | + var a = this.serializeArray(); | ||
456 | + $.each(a, function () { | ||
457 | + if (!this.value) { | ||
458 | + return; | ||
459 | + } | ||
460 | + if (o[this.name] !== undefined) { | ||
461 | + if (!o[this.name].push) { | ||
462 | + o[this.name] = [o[this.name]]; | ||
463 | + } | ||
464 | + o[this.name].push(this.value || ''); | ||
465 | + } else { | ||
466 | + o[this.name] = this.value || ''; | ||
467 | + } | ||
468 | + }); | ||
469 | + return o; | ||
470 | + }; | ||
471 | + | ||
472 | + $(document).ready(function () { | ||
473 | + | ||
474 | + if (storage) { | ||
475 | + storage.getItem('token') && $('#token').val(storage.getItem('token')); | ||
476 | + storage.getItem('apiUrl') && $('#apiUrl').val(storage.getItem('apiUrl')); | ||
477 | + } | ||
478 | + | ||
479 | + $('[data-toggle="tooltip"]').tooltip({ | ||
480 | + placement: 'bottom' | ||
481 | + }); | ||
482 | + | ||
483 | + $(window).on("resize", function(){ | ||
484 | + $("#sidebar").css("max-height", $(window).height()-80); | ||
485 | + }); | ||
486 | + | ||
487 | + $(window).trigger("resize"); | ||
488 | + | ||
489 | + $(document).on("click", "#sidebar .list-group > .list-group-item", function(){ | ||
490 | + $("#sidebar .list-group > .list-group-item").removeClass("current"); | ||
491 | + $(this).addClass("current"); | ||
492 | + }); | ||
493 | + $(document).on("click", "#sidebar .child a", function(){ | ||
494 | + var heading = $("#heading-"+$(this).data("id")); | ||
495 | + if(!heading.next().hasClass("in")){ | ||
496 | + $("a", heading).trigger("click"); | ||
497 | + } | ||
498 | + $("html,body").animate({scrollTop:heading.offset().top-70}); | ||
499 | + }); | ||
500 | + | ||
501 | + $('code[id^=response]').hide(); | ||
502 | + | ||
503 | + $.each($('pre[id^=sample_response],pre[id^=sample_post_body]'), function () { | ||
504 | + if ($(this).html() == 'NA') { | ||
505 | + return; | ||
506 | + } | ||
507 | + var str = prepareStr($(this).html()); | ||
508 | + $(this).html(str); | ||
509 | + }); | ||
510 | + | ||
511 | + $("[data-toggle=popover]").popover({placement: 'right'}); | ||
512 | + | ||
513 | + $('[data-toggle=popover]').on('shown.bs.popover', function () { | ||
514 | + var $sample = $(this).parent().find(".popover-content"), | ||
515 | + str = $(this).data('content'); | ||
516 | + if (typeof str == "undefined" || str === "") { | ||
517 | + return; | ||
518 | + } | ||
519 | + var str = prepareStr(str); | ||
520 | + $sample.html('<pre>' + str + '</pre>'); | ||
521 | + }); | ||
522 | + | ||
523 | + $(document).on('click', '#save_data', function (e) { | ||
524 | + if (storage) { | ||
525 | + storage.setItem('token', $('#token').val()); | ||
526 | + storage.setItem('apiUrl', $('#apiUrl').val()); | ||
527 | + } else { | ||
528 | + alert('Your browser does not support local storage'); | ||
529 | + } | ||
530 | + }); | ||
531 | + $(document).on('click', '.btn-append', function (e) { | ||
532 | + $($("#appendtpl").html()).insertBefore($(this).closest(".panel").find(".form-group-submit")); | ||
533 | + return false; | ||
534 | + }); | ||
535 | + $(document).on('click', '.btn-remove', function (e) { | ||
536 | + $(this).closest(".form-group").remove(); | ||
537 | + return false; | ||
538 | + }); | ||
539 | + $(document).on('keyup', '.input-custom-name', function (e) { | ||
540 | + $(this).closest(".row").find(".input-custom-value").attr("name", $(this).val()); | ||
541 | + return false; | ||
542 | + }); | ||
543 | + | ||
544 | + $(document).on('click', '.send', function (e) { | ||
545 | + e.preventDefault(); | ||
546 | + var form = $(this).closest('form'); | ||
547 | + //added /g to get all the matched params instead of only first | ||
548 | + var matchedParamsInRoute = $(form).attr('action').match(/[^{]+(?=\})/g); | ||
549 | + var theId = $(this).attr('rel'); | ||
550 | + //keep a copy of action attribute in order to modify the copy | ||
551 | + //instead of the initial attribute | ||
552 | + var url = $(form).attr('action'); | ||
553 | + var method = $(form).prop('method').toLowerCase() || 'get'; | ||
554 | + | ||
555 | + var formData = new FormData(); | ||
556 | + | ||
557 | + $(form).find('input').each(function (i, input) { | ||
558 | + if ($(input).attr('type').toLowerCase() == 'file') { | ||
559 | + formData.append($(input).attr('name'), $(input)[0].files[0]); | ||
560 | + method = 'post'; | ||
561 | + } else { | ||
562 | + formData.append($(input).attr('name'), $(input).val()) | ||
563 | + } | ||
564 | + }); | ||
565 | + | ||
566 | + var index, key, value; | ||
567 | + | ||
568 | + if (matchedParamsInRoute) { | ||
569 | + var params = {}; | ||
570 | + formData.forEach(function(value, key){ | ||
571 | + params[key] = value; | ||
572 | + }); | ||
573 | + for (index = 0; index < matchedParamsInRoute.length; ++index) { | ||
574 | + try { | ||
575 | + key = matchedParamsInRoute[index]; | ||
576 | + value = params[key]; | ||
577 | + if (typeof value == "undefined") | ||
578 | + value = ""; | ||
579 | + url = url.replace("\{" + key + "\}", value); | ||
580 | + formData.delete(key); | ||
581 | + } catch (err) { | ||
582 | + console.log(err); | ||
583 | + } | ||
584 | + } | ||
585 | + } | ||
586 | + | ||
587 | + var headers = {}; | ||
588 | + | ||
589 | + var token = $('#token').val(); | ||
590 | + if (token.length > 0) { | ||
591 | + headers['token'] = token; | ||
592 | + } | ||
593 | + | ||
594 | + $("#sandbox" + theId + " .headers input[type=text]").each(function () { | ||
595 | + val = $(this).val(); | ||
596 | + if (val.length > 0) { | ||
597 | + headers[$(this).prop('name')] = val; | ||
598 | + } | ||
599 | + }); | ||
600 | + | ||
601 | + $.ajax({ | ||
602 | + url: $('#apiUrl').val() + url, | ||
603 | + data: method == 'get' ? $(form).serialize() : formData, | ||
604 | + type: method, | ||
605 | + dataType: 'json', | ||
606 | + contentType: false, | ||
607 | + processData: false, | ||
608 | + headers: headers, | ||
609 | + xhrFields: { | ||
610 | + withCredentials: true | ||
611 | + }, | ||
612 | + success: function (data, textStatus, xhr) { | ||
613 | + if (typeof data === 'object') { | ||
614 | + var str = JSON.stringify(data, null, 2); | ||
615 | + $('#response' + theId).html(syntaxHighlight(str)); | ||
616 | + } else { | ||
617 | + $('#response' + theId).html(data || ''); | ||
618 | + } | ||
619 | + $('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders()); | ||
620 | + $('#response' + theId).show(); | ||
621 | + }, | ||
622 | + error: function (xhr, textStatus, error) { | ||
623 | + try { | ||
624 | + var str = JSON.stringify($.parseJSON(xhr.responseText), null, 2); | ||
625 | + } catch (e) { | ||
626 | + var str = xhr.responseText; | ||
627 | + } | ||
628 | + $('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders()); | ||
629 | + $('#response' + theId).html(syntaxHighlight(str)); | ||
630 | + $('#response' + theId).show(); | ||
631 | + } | ||
632 | + }); | ||
633 | + return false; | ||
634 | + }); | ||
635 | + }); | ||
636 | + </script> | ||
637 | + <script type="text/html" id="appendtpl"> | ||
638 | + <div class="form-group"> | ||
639 | + <label class="control-label">自定义</label> | ||
640 | + <div class="row"> | ||
641 | + <div class="col-xs-4"> | ||
642 | + <input type="text" class="form-control input-sm input-custom-name" placeholder="名称"> | ||
643 | + </div> | ||
644 | + <div class="col-xs-6"> | ||
645 | + <input type="text" class="form-control input-sm input-custom-value" placeholder="值"> | ||
646 | + </div> | ||
647 | + <div class="col-xs-2 text-center"> | ||
648 | + <a href="javascript:" class="btn btn-sm btn-danger btn-remove">删除</a> | ||
649 | + </div> | ||
650 | + </div> | ||
651 | + </div> | ||
652 | + </script> | ||
653 | + </body> | ||
654 | +</html> |
-
请 注册 或 登录 后发表评论