作者 开飞机的舒克

提交

正在显示 48 个修改的文件 包含 4214 行增加0 行删除

要显示太多修改。

为保证性能只显示 48 of 48+ 个文件。

  1 +{
  2 + "directory": "public/assets/libs",
  3 + "ignoredDependencies": [
  4 + "es6-promise",
  5 + "file-saver",
  6 + "html2canvas",
  7 + "jspdf",
  8 + "jspdf-autotable",
  9 + "pdfmake"
  10 + ]
  11 +}
  1 +[app]
  2 +debug = false
  3 +trace = false
  4 +
  5 +[database]
  6 +hostname = 127.0.0.1
  7 +database = fastadmin
  8 +username = root
  9 +password = root
  10 +hostport = 3306
  11 +prefix = fa_
  1 +/nbproject/
  2 +/runtime/*
  3 +/public/uploads/*
  4 +.idea
  5 +composer.lock
  6 +*.log
  7 +*.css.map
  8 +!.gitkeep
  9 +.env
  10 +.vscode
  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.
  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。
  1 +deny from all
  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"]}
  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 +}
  1 +<?php
  2 +
  3 +return [
  4 +];
  1 +<?php
  2 +
  3 +namespace addons\command\controller;
  4 +
  5 +use think\addons\Controller;
  6 +
  7 +class Index extends Controller
  8 +{
  9 +
  10 + public function index()
  11 + {
  12 + $this->error("当前插件暂无前台页面");
  13 + }
  14 +
  15 +}
  1 +name = command
  2 +title = 在线命令
  3 +intro = 可在线执行一键生成CRUD、一键生成菜单等相关命令
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.1.1
  7 +state = 1
  8 +url = /addons/command
  9 +license = regular
  10 +licenseto = 10789
  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 = '在线命令表';
  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 +}
  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"]}
  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 +}
  1 +if (Config.modulename === 'index' && Config.controllername === 'user' && ['login', 'register'].indexOf(Config.actionname) > -1 && $("#register-form,#login-form").length > 0 && $(".social-login").length == 0) {
  2 + $("#register-form,#login-form").append(Config.third.loginhtml || '');
  3 +}
  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 +];
  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 +}
  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 +}
  1 +name = third
  2 +title = 第三方登录
  3 +intro = 使用微信、QQ、微博登录插件
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.3.1
  7 +state = 1
  8 +url = /addons/third
  9 +license = regular
  10 +licenseto = 10789
  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 +
  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 +}
  1 +<?php
  2 +
  3 +namespace addons\third\library;
  4 +
  5 +use fast\Http;
  6 +use think\Config;
  7 +use think\Session;
  8 +
  9 +/**
  10 + * QQ
  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 +}
  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 +}
  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 +}
  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 +}
  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 +}
  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>
  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>
  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"]}
  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 +}
  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 +});
  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 +];
  1 +name = ueditor
  2 +title = 百度ueditor插件
  3 +intro = 基于百度Ueditor的富文本编辑器
  4 +author = chance
  5 +website = http://it-huai.gitee.io/ueditor-api/
  6 +version = 1.0.9
  7 +state = 1
  8 +url = /addons/ueditor
  9 +license = basic
  10 +licenseto = 10789
  1 +deny from all
  1 +<?php
  2 +
  3 +namespace app\admin\behavior;
  4 +
  5 +class AdminLog
  6 +{
  7 + public function run(&$params)
  8 + {
  9 + //只记录POST请求的日志
  10 + if (request()->isPost() && config('fastadmin.auto_record_log')) {
  11 + \app\admin\model\AdminLog::record();
  12 + }
  13 + }
  14 +}
  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 +];
  1 +<?php
  2 +
  3 +namespace addons\{%addon%}\controller;
  4 +
  5 +use think\addons\Controller;
  6 +
  7 +class Index extends Controller
  8 +{
  9 +
  10 + public function index()
  11 + {
  12 + $this->error("当前插件暂无前台页面");
  13 + }
  14 +
  15 +}
  1 +name = {%name%}
  2 +title = 插件名称{%name%}
  3 +intro = 插件介绍
  4 +author = yourname
  5 +website = https://www.fastadmin.net
  6 +version = 1.0.0
  7 +state = 1
  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 +}
  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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  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>