作者 开飞机的舒克

提交

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

要显示太多修改。

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

{
"directory": "public/assets/libs",
"ignoredDependencies": [
"es6-promise",
"file-saver",
"html2canvas",
"jspdf",
"jspdf-autotable",
"pdfmake"
]
}
... ...
[app]
debug = false
trace = false
[database]
hostname = 127.0.0.1
database = fastadmin
username = root
password = root
hostport = 3306
prefix = fa_
... ...
/nbproject/
/runtime/*
/public/uploads/*
.idea
composer.lock
*.log
*.css.map
!.gitkeep
.env
.vscode
... ...
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "{}" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright 2017 Karson
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
\ No newline at end of file
... ...
FastAdmin是一款基于ThinkPHP+Bootstrap的极速后台开发框架。
## 主要特性
* 基于`Auth`验证的权限管理系统
* 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
* 支持单管理员多角色
* 支持管理子级数据或个人数据
* 强大的一键生成功能
* 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等
* 一键压缩打包JS和CSS文件,一键CDN静态资源部署
* 一键生成控制器菜单和规则
* 一键生成API接口文档
* 完善的前端功能组件开发
* 基于`AdminLTE`二次开发
* 基于`Bootstrap`开发,自适应手机、平板、PC
* 基于`RequireJS`进行JS模块管理,按需加载
* 基于`Less`进行样式开发
* 强大的插件扩展功能,在线安装卸载升级插件
* 通用的会员模块和API模块
* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
* 二级域名部署支持,同时域名支持绑定到应用插件
* 多语言支持,服务端及客户端支持
* 支持大文件分片上传、剪切板粘贴上传、拖拽上传,进度条显示,图片上传前压缩
* 支持表格固定列、固定表头、跨页选择、Excel导出、模板渲染等功能
* 强大的第三方应用模块支持([CMS](https://www.fastadmin.net/store/cms.html)[博客](https://www.fastadmin.net/store/blog.html)[知识付费问答](https://www.fastadmin.net/store/ask.html)[在线投票系统](https://www.fastadmin.net/store/vote.html)[B2C商城](https://www.fastadmin.net/store/shopro.html)[B2B2C商城](https://www.fastadmin.net/store/wanlshop.html))
* 支持CMS、博客、知识付费问答无缝整合[Xunsearch全文搜索](https://www.fastadmin.net/store/xunsearch.html)
* 第三方小程序支持([CMS小程序](https://www.fastadmin.net/store/cms.html)[预订小程序](https://www.fastadmin.net/store/ball.html)[问答小程序](https://www.fastadmin.net/store/ask.html)[点餐小程序](https://www.fastadmin.net/store/unidrink.html)[B2C小程序](https://www.fastadmin.net/store/shopro.html)[B2B2C小程序](https://www.fastadmin.net/store/wanlshop.html)[博客小程序](https://www.fastadmin.net/store/blog.html))
* 整合第三方短信接口(阿里云、腾讯云短信)
* 无缝整合第三方云存储(七牛云、阿里云OSS、又拍云)功能,支持云储存分片上传
* 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器)
* 第三方登录(QQ、微信、微博)整合
* 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付
* 丰富的插件应用市场
## 安装使用
https://doc.fastadmin.net
## 在线演示
https://demo.fastadmin.net
用户名:admin
密 码:123456
提 示:演示站数据无法进行修改,请下载源码安装体验全部功能
## 界面截图
![控制台](https://images.gitee.com/uploads/images/2020/0929/202947_8db2d281_10933.gif "控制台")
## 问题反馈
在使用中有任何问题,请使用以下联系方式联系我们
交流社区: https://ask.fastadmin.net
QQ 1 群(满)、QQ 2 群(满)、QQ 3 群(满)、QQ 4 群(满)、QQ 5 群(满)、QQ 6 群(满)、[QQ 7 群](https://www.fastadmin.net/goto/qun)
Github: https://github.com/karsonzhang/fastadmin
Gitee: https://gitee.com/karson/fastadmin
## 特别鸣谢
感谢以下的项目,排名不分先后
ThinkPHP:http://www.thinkphp.cn
AdminLTE:https://adminlte.io
Bootstrap:http://getbootstrap.com
jQuery:http://jquery.com
Bootstrap-table:https://github.com/wenzhixin/bootstrap-table
Nice-validator: https://validator.niceue.com
SelectPage: https://github.com/TerryZ/SelectPage
Layer: https://layuion.com/layer/
DropzoneJS: https://www.dropzonejs.com
## 版权信息
FastAdmin遵循Apache2开源协议发布,并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2017-2022 by FastAdmin (https://www.fastadmin.net)
All rights reserved。
... ...
deny from all
\ No newline at end of file
... ...
{"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"]}
\ No newline at end of file
... ...
<?php
namespace addons\command;
use app\common\library\Menu;
use think\Addons;
/**
* 在线命令插件
*/
class Command extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
$menu = [
[
'name' => 'command',
'title' => '在线命令管理',
'icon' => 'fa fa-terminal',
'sublist' => [
['name' => 'command/index', 'title' => '查看'],
['name' => 'command/add', 'title' => '添加'],
['name' => 'command/detail', 'title' => '详情'],
['name' => 'command/command', 'title' => '生成并执行命令'],
['name' => 'command/execute', 'title' => '再次执行命令'],
['name' => 'command/del', 'title' => '删除'],
['name' => 'command/multi', 'title' => '批量更新'],
]
]
];
Menu::create($menu);
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
Menu::delete('command');
return true;
}
/**
* 插件启用方法
* @return bool
*/
public function enable()
{
Menu::enable('command');
return true;
}
/**
* 插件禁用方法
* @return bool
*/
public function disable()
{
Menu::disable('command');
return true;
}
}
... ...
<?php
return [
];
... ...
<?php
namespace addons\command\controller;
use think\addons\Controller;
class Index extends Controller
{
public function index()
{
$this->error("当前插件暂无前台页面");
}
}
... ...
name = command
title = 在线命令
intro = 可在线执行一键生成CRUD、一键生成菜单等相关命令
author = FastAdmin
website = https://www.fastadmin.net
version = 1.1.1
state = 1
url = /addons/command
license = regular
licenseto = 10789
... ...
CREATE TABLE IF NOT EXISTS `__PREFIX__command` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
`type` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '类型',
`params` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '参数',
`command` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '命令',
`content` text COMMENT '返回结果',
`executetime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '执行时间',
`createtime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '创建时间',
`updatetime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '更新时间',
`status` enum('successed','failured') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'failured' COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线命令表';
... ...
<?php
namespace addons\command\library;
/**
* Class Output
*/
class Output extends \think\console\Output
{
protected $message = [];
public function __construct($driver = 'console')
{
parent::__construct($driver);
}
protected function block($style, $message)
{
$this->message[] = $message;
}
public function getMessage()
{
return $this->message;
}
}
... ...
{"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"]}
\ No newline at end of file
... ...
<?php
namespace addons\third;
use app\common\library\Auth;
use app\common\library\Menu;
use think\Addons;
use think\Request;
use think\Session;
/**
* 第三方登录
*/
class Third extends Addons
{
protected static $html = ['register' => '', 'profile' => ''];
/**
* 插件安装方法
* @return bool
*/
public function install()
{
$menu = [
[
'name' => 'third',
'title' => '第三方登录管理',
'icon' => 'fa fa-users',
'sublist' => [
[
"name" => "third/index",
"title" => "查看"
],
[
"name" => "third/del",
"title" => "删除"
]
]
]
];
Menu::create($menu);
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
Menu::delete("third");
return true;
}
/**
* 插件启用方法
* @return bool
*/
public function enable()
{
Menu::enable("third");
return true;
}
/**
* 插件禁用方法
* @return bool
*/
public function disable()
{
Menu::disable("third");
return true;
}
/**
* 删除第三方登录表的关联数据
*/
public function userDeleteSuccessed(\app\common\model\User $user)
{
\addons\third\model\Third::where('user_id', $user->id)->delete();
}
/**
* 移除第三方登录信息
*/
public function userLogoutSuccessed(\app\common\model\User $user)
{
Session::delete(["wechat-userinfo", "qq-userinfo", "weibo-userinfo"]);
}
/**
* 模块开始
*/
public function moduleInit()
{
$config = $this->getConfig();
if (!$config['status']) {
return;
}
$request = Request::instance();
$module = strtolower($request->module());
$controller = strtolower($request->controller());
$action = strtolower($request->action());
if ($module !== 'index' || $controller !== 'user' || !in_array($action, ['login', 'register'])) {
return;
}
$url = $request->get('url', $request->server('HTTP_REFERER', '', 'trim'), 'trim');
$data = [
'status' => isset($config['status']) ? explode(',', $config['status']) : [],
'url' => $url
];
self::$html['register'] = $this->view->fetch('view/hook/user_register_end', $data);
}
/**
* 方法开始
*/
public function actionBegin()
{
$config = $this->getConfig();
if (!$config['status']) {
return;
}
$request = Request::instance();
$module = strtolower($request->module());
$controller = strtolower($request->controller());
$action = strtolower($request->action());
if ($module !== 'index' || $controller !== 'user' || !in_array($action, ['profile'])) {
return;
}
$platform = \addons\third\model\Third::where('user_id', Auth::instance()->id)->column('platform');
$data = [
'status' => isset($config['status']) ? explode(',', $config['status']) : [],
'platform' => $platform
];
self::$html['profile'] = $this->view->fetch('view/hook/user_profile_end', $data);
}
/**
* 配置
* @param $params
*/
public function configInit(&$params)
{
// 兼容旧版本FastAdmin
$config = $this->getConfig();
$module = strtolower(request()->module());
$controller = strtolower(request()->controller());
$action = strtolower(request()->action());
$loginhtml = version_compare(config('fastadmin.version'), '1.3.0', '<') > 0 && $module === 'index' && $controller === 'user' && in_array($action, ['login', 'register']) ? self::$html['register'] : '';
$params['third'] = ['status' => explode(',', $config['status']), 'loginhtml' => $loginhtml];
}
/**
* HTML替换
*/
public function viewFilter(& $content)
{
$config = $this->getConfig();
if (!$config['status']) {
return;
}
$request = Request::instance();
$module = strtolower($request->module());
$controller = strtolower($request->controller());
$action = strtolower($request->action());
if ($module !== 'index' || $controller !== 'user') {
return;
}
if (in_array($action, ['login', 'register'])) {
$html = self::$html['register'] ?? '';
$content = str_replace(['<!--@IndexRegisterFormEnd-->', '<!--@IndexLoginFormEnd-->'], $html, $content);
} elseif ($action === 'profile') {
$html = self::$html['profile'] ?? '';
$content = str_replace("<div class=\"form-group normal-footer\">", "{$html}<div class=\"form-group normal-footer\">", $content);
}
}
}
... ...
if (Config.modulename === 'index' && Config.controllername === 'user' && ['login', 'register'].indexOf(Config.actionname) > -1 && $("#register-form,#login-form").length > 0 && $(".social-login").length == 0) {
$("#register-form,#login-form").append(Config.third.loginhtml || '');
}
... ...
<?php
return [
[
'name' => 'qq',
'title' => 'QQ',
'type' => 'array',
'content' => [
'app_id' => '',
'app_secret' => '',
'scope' => 'get_user_info',
],
'value' => [
'app_id' => '100000000',
'app_secret' => '',
'scope' => 'get_user_info',
],
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
],
[
'name' => 'wechat',
'title' => '微信公众号',
'type' => 'array',
'content' => [
'app_id' => '',
'app_secret' => '',
'callback' => '',
'scope' => 'snsapi_base',
],
'value' => [
'app_id' => '100000000',
'app_secret' => '123456',
'scope' => 'snsapi_userinfo',
],
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
],
[
'name' => 'weibo',
'title' => '微博',
'type' => 'array',
'content' => [
'app_id' => '',
'app_secret' => '',
'scope' => 'get_user_info',
],
'value' => [
'app_id' => '100000000',
'app_secret' => '123456',
'scope' => 'get_user_info',
],
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
],
[
'name' => 'bindaccount',
'title' => '账号绑定',
'type' => 'radio',
'content' => [
1 => '开启',
0 => '关闭',
],
'value' => '1',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => 'PC端是否开启账号绑定,关闭则自动创建账号',
'extend' => '',
],
[
'name' => 'status',
'title' => 'PC端第三方登录开关',
'type' => 'checkbox',
'content' => [
'qq' => 'QQ',
'wechat' => '微信',
'weibo' => '微博',
],
'value' => 'qq,wechat,weibo',
'rule' => '',
'msg' => '',
'tip' => '',
'ok' => 'PC端第三方登录开关',
'extend' => '',
],
[
'name' => 'rewrite',
'title' => '伪静态',
'type' => 'array',
'content' => [],
'value' => [
'index/index' => '/third$',
'index/connect' => '/third/connect/[:platform]',
'index/callback' => '/third/callback/[:platform]',
'index/bind' => '/third/bind/[:platform]',
'index/unbind' => '/third/unbind/[:platform]',
],
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
],
];
... ...
<?php
namespace addons\third\controller;
use addons\third\library\Application;
use app\common\controller\Api as commonApi;
use addons\third\library\Service;
use addons\third\model\Third;
use app\common\library\Sms;
use fast\Random;
use think\Lang;
use think\Config;
use think\Session;
use think\Validate;
/**
* 第三方登录插件
*/
class Api extends commonApi
{
protected $noNeedLogin = ['getAuthUrl', 'callback', 'account']; // 无需登录即可访问的方法,同时也无需鉴权了
protected $noNeedRight = ['*']; // 无需鉴权即可访问的方法
protected $app = null;
protected $options = [];
protected $config = null;
public function _initialize()
{
//跨域检测
check_cors_request();
//设置session_id
Config::set('session.id', $this->request->server("HTTP_SID"));
parent::_initialize();
$this->config = get_addon_config('third');
$this->app = new Application($this->config);
}
/**
* H5获取授权链接
* @return void
*/
public function getAuthUrl()
{
$url = $this->request->param('url', '', 'trim');
$platform = $this->request->param('platform');
if (!$url || !$platform || !isset($this->config[$platform])) {
$this->error('参数错误');
}
$this->config[$platform]['callback'] = $url;
$this->app = new Application($this->config); //
if (!$this->app->{$platform}) {
$this->error(__('Invalid parameters'));
}
$this->success('', $this->app->{$platform}->getAuthorizeUrl());
}
/**
* 公众号:wechat 授权回调的请求【非第三方,自己的前端请求】
* @return void
*/
public function callback()
{
$platform = $this->request->param('platform');
if (!$this->app->{$platform}) {
$this->error(__('Invalid parameters'));
}
$userinfo = $this->app->{$platform}->getUserInfo($this->request->param());
if (!$userinfo) {
$this->error(__('操作失败'));
}
$userinfo['apptype'] = 'mp';
$userinfo['platform'] = $platform;
$third = [
'avatar' => $userinfo['userinfo']['avatar'],
'nickname' => $userinfo['userinfo']['nickname']
];
$user = null;
if ($this->auth->isLogin() || Service::isBindThird($userinfo['platform'], $userinfo['openid'], $userinfo['apptype'], $userinfo['unionid'])) {
Service::connect($userinfo['platform'], $userinfo);
$user = $this->auth->getUserinfo();
} else {
$user = false;
Session::set('third-userinfo', $userinfo);
}
$this->success("授权成功!", ['user' => $user, 'third' => $third]);
}
/**
* 登录或创建账号
*/
public function account()
{
if ($this->request->isPost()) {
$params = Session::get('third-userinfo');
$mobile = $this->request->post('mobile', '');
$code = $this->request->post('code', $this->request->post('captcha'));
$token = $this->request->post('__token__');
$rule = [
'mobile' => 'require|regex:/^1\d{10}$/',
'__token__' => 'require|token',
];
$msg = [
'mobile' => 'Mobile is incorrect',
];
$data = [
'mobile' => $mobile,
'__token__' => $token,
];
$ret = Sms::check($mobile, $code, 'bind');
if (!$ret) {
$this->error(__('验证码错误'));
}
$validate = new Validate($rule, $msg);
$result = $validate->check($data);
if (!$result) {
$this->error(__($validate->getError()), ['__token__' => $this->request->token()]);
}
$userinfo = \app\common\model\User::where('mobile', $mobile)->find();
if ($userinfo) {
$result = $this->auth->direct($userinfo->id);
} else {
$result = $this->auth->register($mobile, Random::alnum(), '', $mobile, isset($params['userinfo']) ? $params['userinfo'] : []);
}
if ($result) {
Service::connect($params['platform'], $params);
$this->success(__('绑定账号成功'), ['userinfo' => $this->auth->getUserinfo()]);
} else {
$this->error($this->auth->getError(), ['__token__' => $this->request->token()]);
}
}
}
}
... ...
<?php
namespace addons\third\controller;
use addons\third\library\Application;
use addons\third\library\Service;
use addons\third\model\Third;
use think\addons\Controller;
use think\Config;
use think\Cookie;
use think\Hook;
use think\Lang;
use think\Session;
/**
* 第三方登录插件
*/
class Index extends Controller
{
protected $app = null;
protected $options = [];
public function _initialize()
{
parent::_initialize();
$config = get_addon_config('third');
$this->app = new Application($config);
}
/**
* 插件首页
*/
public function index()
{
if (!\app\admin\library\Auth::instance()->id) {
$this->error('当前插件暂无前台页面');
}
$platformList = [];
if ($this->auth->id) {
$platformList = Third::where('user_id', $this->auth->id)->column('platform');
}
$this->view->assign('platformList', $platformList);
return $this->view->fetch();
}
/**
* 发起授权
*/
public function connect()
{
$platform = $this->request->param('platform');
$config = get_addon_config('third');
if (!$config['status']) {
$this->error("第三方登录已关闭");
}
$status = explode(',', $config['status']);
if (!in_array($platform, $status)) {
$this->error("该登录方式已关闭");
}
$url = $this->request->request('url', $this->request->server('HTTP_REFERER', '/', 'trim'), 'trim');
if (!$this->app->{$platform}) {
$this->error('参数错误');
}
if ($url) {
Session::set("redirecturl", $url);
}
// 跳转到登录授权页面
$this->redirect($this->app->{$platform}->getAuthorizeUrl());
return;
}
/**
* 通知回调
*/
public function callback()
{
$auth = $this->auth;
//监听注册登录注销的事件
Hook::add('user_login_successed', function ($user) use ($auth) {
$expire = input('post.keeplogin') ? 30 * 86400 : 0;
Cookie::set('uid', $user->id, $expire);
Cookie::set('token', $auth->getToken(), $expire);
});
Hook::add('user_register_successed', function ($user) use ($auth) {
Cookie::set('uid', $user->id);
Cookie::set('token', $auth->getToken());
});
Hook::add('user_logout_successed', function ($user) use ($auth) {
Cookie::delete('uid');
Cookie::delete('token');
});
$platform = $this->request->param('platform');
// 成功后返回之前页面,但忽略登录/注册页面
$url = Session::has("redirecturl") ? Session::pull("redirecturl") : url('index/user/index');
$url = preg_match("/\/user\/(register|login|resetpwd)/i", $url) ? url('index/user/index') : $url;
// 授权成功后的回调
$userinfo = $this->app->{$platform}->getUserInfo();
if (!$userinfo) {
$this->error(__('操作失败'), $url);
}
Session::set("{$platform}-userinfo", $userinfo);
//判断是否启用账号绑定
$third = Third::get(['platform' => $platform, 'openid' => $userinfo['openid']]);
if (!$third) {
$config = get_addon_config('third');
//要求绑定账号或会员当前是登录状态
if ($config['bindaccount'] || $this->auth->id) {
$this->redirect(url('index/third/prepare') . "?" . http_build_query(['platform' => $platform, 'url' => $url]));
}
}
//直接登录
$loginret = Service::connect($platform, $userinfo);
if ($loginret) {
$this->redirect($url);
} else {
$this->error("登录失败,请返回重试", $url);
}
}
/**
* 绑定账号
*/
public function bind()
{
$platform = $this->request->request('platform', $this->request->param('platform', ''));
$url = $this->request->get('url', $this->request->server('HTTP_REFERER', '', 'trim'), 'trim');
$redirecturl = url("index/third/bind") . "?" . http_build_query(['platform' => $platform, 'url' => $url]);
$this->redirect($redirecturl);
return;
}
/**
* 解绑账号
*/
public function unbind()
{
$platform = $this->request->request('platform', $this->request->param('platform', ''));
$url = $this->request->get('url', $this->request->server('HTTP_REFERER', '', 'trim'), 'trim');
$redirecturl = url("index/third/unbind") . "?" . http_build_query(['platform' => $platform, 'url' => $url]);
$this->redirect($redirecturl);
return;
}
}
... ...
name = third
title = 第三方登录
intro = 使用微信、QQ、微博登录插件
author = FastAdmin
website = https://www.fastadmin.net
version = 1.3.1
state = 1
url = /addons/third
license = regular
licenseto = 10789
... ...
CREATE TABLE IF NOT EXISTS `__PREFIX__third` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` int(10) unsigned DEFAULT '0' COMMENT '会员ID',
`platform` varchar(30) DEFAULT '' COMMENT '第三方应用',
`apptype` varchar(50) DEFAULT '' COMMENT '应用类型',
`unionid` varchar(100) DEFAULT '' COMMENT '第三方UNIONID',
`openid` varchar(100) DEFAULT '' COMMENT '第三方OPENID',
`openname` varchar(100) DEFAULT '' COMMENT '第三方会员昵称',
`access_token` varchar(255) NULL DEFAULT '' COMMENT 'AccessToken',
`refresh_token` varchar(255) DEFAULT 'RefreshToken',
`expires_in` int(10) unsigned DEFAULT '0' COMMENT '有效期',
`createtime` bigint(16) unsigned DEFAULT NULL COMMENT '创建时间',
`updatetime` bigint(16) unsigned DEFAULT NULL COMMENT '更新时间',
`logintime` bigint(16) unsigned DEFAULT NULL COMMENT '登录时间',
`expiretime` bigint(16) unsigned DEFAULT NULL COMMENT '过期时间',
PRIMARY KEY (`id`),
UNIQUE KEY `platform` (`platform`,`openid`),
KEY `user_id` (`user_id`,`platform`),
KEY `unionid` (`platform`,`unionid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='第三方登录表';
ALTER TABLE `__PREFIX__third` ADD COLUMN `apptype` varchar(50) NULL DEFAULT '' COMMENT '应用类型' AFTER `platform`;
ALTER TABLE `__PREFIX__third` ADD COLUMN `unionid` varchar(100) NULL DEFAULT '' COMMENT '第三方UnionID' AFTER `apptype`;
ALTER TABLE `__PREFIX__third` ADD INDEX `unionid`(`platform`, `unionid`);
ALTER TABLE `__PREFIX__third` CHARACTER SET = utf8mb4, COLLATE = utf8mb4_general_ci;
ALTER TABLE `__PREFIX__third` MODIFY COLUMN `openname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '第三方会员昵称' AFTER `unionid`;
... ...
<?php
namespace addons\third\library;
class Application
{
/**
* 配置信息
* @var array
*/
private $config = [];
/**
* 服务提供者
* @var array
*/
private $providers = [
'qq' => 'Qq',
'weibo' => 'Weibo',
'wechat' => 'Wechat',
];
/**
* 服务对象信息
* @var array
*/
protected $services = [];
public function __construct($options = [])
{
$options = array_intersect_key($options, $this->providers);
$options = array_merge($this->config, is_array($options) ? $options : []);
foreach ($options as $key => &$option) {
$option['app_id'] = isset($option['app_id']) ? $option['app_id'] : '';
$option['app_secret'] = isset($option['app_secret']) ? $option['app_secret'] : '';
// 如果未定义回调地址则自动生成
$option['callback'] = isset($option['callback']) && $option['callback'] ? $option['callback'] : addon_url('third/index/callback', [':platform' => $key], false, true);
}
$this->config = $options;
//注册服务器提供者
$this->registerProviders();
}
/**
* 注册服务提供者
*/
private function registerProviders()
{
foreach ($this->providers as $k => $v) {
$this->services[$k] = function () use ($k, $v) {
$options = $this->config[$k];
$objname = __NAMESPACE__ . "\\{$v}";
return new $objname($options);
};
}
}
public function __set($key, $value)
{
$this->services[$key] = $value;
}
public function __get($key)
{
return isset($this->services[$key]) ? $this->services[$key]($this) : null;
}
}
... ...
<?php
namespace addons\third\library;
use fast\Http;
use think\Config;
use think\Session;
/**
* QQ
*/
class Qq
{
const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
const GET_USERINFO_URL = "https://graph.qq.com/user/get_user_info";
const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
/**
* 配置信息
* @var array
*/
private $config = [];
public function __construct($options = [])
{
if ($config = Config::get('third.qq')) {
$this->config = array_merge($this->config, $config);
}
$this->config = array_merge($this->config, is_array($options) ? $options : []);
}
/**
* 登陆
*/
public function login()
{
header("Location:" . $this->getAuthorizeUrl());
}
/**
* 获取authorize_url
*/
public function getAuthorizeUrl()
{
$state = md5(uniqid(rand(), true));
Session::set('state', $state);
$queryarr = array(
"response_type" => "code",
"client_id" => $this->config['app_id'],
"redirect_uri" => $this->config['callback'],
"scope" => $this->config['scope'],
"state" => $state,
);
request()->isMobile() && $queryarr['display'] = 'mobile';
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
return $url;
}
/**
* 获取用户信息
* @param array $params
* @return array
*/
public function getUserInfo($params = [])
{
$params = $params ? $params : $_GET;
if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) {
//获取access_token
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
if ($access_token) {
$openid = $this->getOpenId($access_token);
//获取用户信息
$queryarr = [
"access_token" => $access_token,
"oauth_consumer_key" => $this->config['app_id'],
"openid" => $openid,
];
$ret = Http::get(self::GET_USERINFO_URL, $queryarr);
$userinfo = (array)json_decode($ret, true);
if (!$userinfo || !isset($userinfo['ret']) || $userinfo['ret'] !== 0) {
return [];
}
$userinfo = $userinfo ? $userinfo : [];
$userinfo['avatar'] = isset($userinfo['figureurl_qq_2']) ? $userinfo['figureurl_qq_2'] : '';
$data = [
'access_token' => $access_token,
'refresh_token' => $refresh_token,
'expires_in' => $expires_in,
'openid' => $openid,
'userinfo' => $userinfo
];
return $data;
}
}
return [];
}
/**
* 获取access_token
* @param string $code
* @return array
*/
public function getAccessToken($code = '')
{
if (!$code) {
return [];
}
$queryarr = array(
"grant_type" => "authorization_code",
"client_id" => $this->config['app_id'],
"client_secret" => $this->config['app_secret'],
"redirect_uri" => $this->config['callback'],
"code" => $code,
);
$ret = Http::get(self::GET_ACCESS_TOKEN_URL, $queryarr);
$params = [];
parse_str($ret, $params);
return $params ? $params : [];
}
/**
* 获取open_id
* @param string $access_token
* @return string
*/
private function getOpenId($access_token = '')
{
$response = Http::get(self::GET_OPENID_URL, ['access_token' => $access_token]);
if (strpos($response, "callback") !== false) {
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos - 1);
}
$user = (array)json_decode($response, true);
return isset($user['openid']) ? $user['openid'] : '';
}
}
... ...
<?php
namespace addons\third\library;
use addons\third\model\Third;
use app\common\model\User;
use fast\Random;
use think\Db;
use think\Exception;
/**
* 第三方登录服务类
*
*/
class Service
{
/**
* 第三方登录
* @param string $platform 平台
* @param array $params 参数
* @param array $extend 会员扩展信息
* @param int $keeptime 有效时长
* @return boolean
*/
public static function connect($platform, $params = [], $extend = [], $keeptime = 0)
{
$time = time();
$nickname = $params['nickname'] ?? ($params['userinfo']['nickname'] ?? '');
$avatar = $params['avatar'] ?? ($params['userinfo']['avatar'] ?? '');
$values = [
'platform' => $platform,
'openid' => $params['openid'],
'openname' => $nickname,
'access_token' => $params['access_token'],
'refresh_token' => $params['refresh_token'],
'expires_in' => $params['expires_in'],
'logintime' => $time,
'expiretime' => $time + $params['expires_in'],
];
$values = array_merge($values, $params);
$auth = \app\common\library\Auth::instance();
$auth->keeptime($keeptime);
//是否有自己的
$third = Third::get(['platform' => $platform, 'openid' => $params['openid']], 'user');
if ($third) {
if (!$third->user) {
$third->delete();
} else {
$third->allowField(true)->save($values);
// 写入登录Cookies和Token
return $auth->direct($third->user_id);
}
}
//存在unionid就需要判断是否需要生成新记录
if (isset($params['unionid']) && !empty($params['unionid'])) {
$third = Third::get(['platform' => $platform, 'unionid' => $params['unionid']], 'user');
if ($third) {
if (!$third->user) {
$third->delete();
} else {
// 保存第三方信息
$values['user_id'] = $third->user_id;
$third = Third::create($values, true);
// 写入登录Cookies和Token
return $auth->direct($third->user_id);
}
}
}
if ($auth->id) {
if (!$third) {
$values['user_id'] = $auth->id;
Third::create($values, true);
}
$user = $auth->getUser();
} else {
// 先随机一个用户名,随后再变更为u+数字id
$username = Random::alnum(20);
$password = Random::alnum(6);
$domain = request()->host();
Db::startTrans();
try {
// 默认注册一个会员
$result = $auth->register($username, $password, $username . '@' . $domain, '', $extend);
if (!$result) {
throw new Exception($auth->getError());
}
$user = $auth->getUser();
$fields = ['username' => 'u' . $user->id, 'email' => 'u' . $user->id . '@' . $domain];
if ($nickname) {
$fields['nickname'] = $nickname;
}
if ($avatar) {
$fields['avatar'] = function_exists("xss_clean") ? xss_clean(strip_tags($avatar)) : strip_tags($avatar);
}
// 更新会员资料
$user = User::get($user->id);
$user->save($fields);
// 保存第三方信息
$values['user_id'] = $user->id;
Third::create($values, true);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
$auth->logout();
return false;
}
}
// 写入登录Cookies和Token
return $auth->direct($user->id);
}
public static function isBindThird($platform, $openid, $apptype = '', $unionid = '')
{
$conddtions = [
'platform' => $platform,
'openid' => $openid
];
if ($apptype) {
$conddtions['apptype'] = $apptype;
}
$third = Third::get($conddtions, 'user');
//第三方存在
if ($third) {
//用户失效
if (!$third->user) {
$third->delete();
return false;
}
return true;
}
if ($unionid) {
$third = Third::get(['platform' => $platform, 'unionid' => $unionid], 'user');
if ($third) {
//
if (!$third->user) {
$third->delete();
return false;
}
return true;
}
}
return false;
}
}
... ...
<?php
namespace addons\third\library;
use fast\Http;
use think\Config;
use think\Session;
/**
* 微信
*/
class Wechat
{
const GET_AUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";
const GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
const GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
/**
* 配置信息
* @var array
*/
private $config = [];
public function __construct($options = [])
{
if ($config = Config::get('third.wechat')) {
$this->config = array_merge($this->config, $config);
}
$this->config = array_merge($this->config, is_array($options) ? $options : []);
}
/**
* 登陆
*/
public function login()
{
header("Location:" . $this->getAuthorizeUrl());
}
/**
* 获取authorize_url
*/
public function getAuthorizeUrl()
{
$state = md5(uniqid(rand(), true));
Session::set('state', $state);
$queryarr = array(
"appid" => $this->config['app_id'],
"redirect_uri" => $this->config['callback'],
"response_type" => "code",
"scope" => $this->config['scope'],
"state" => $state,
);
request()->isMobile() && $queryarr['display'] = 'mobile';
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr) . '#wechat_redirect';
return $url;
}
/**
* 获取用户信息
* @param array $params
* @return array
*/
public function getUserInfo($params = [])
{
$params = $params ? $params : request()->get();
if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) {
//获取access_token
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
if ($access_token) {
$openid = isset($data['openid']) ? $data['openid'] : '';
$unionid = isset($data['unionid']) ? $data['unionid'] : '';
if (stripos($this->config['scope'], 'snsapi_userinfo') !== false) {
//获取用户信息
$queryarr = [
"access_token" => $access_token,
"openid" => $openid,
"lang" => 'zh_CN'
];
$ret = Http::get(self::GET_USERINFO_URL, $queryarr);
$userinfo = (array)json_decode($ret, true);
if (!$userinfo || isset($userinfo['errcode'])) {
return [];
}
$userinfo = $userinfo ? $userinfo : [];
$userinfo['avatar'] = isset($userinfo['headimgurl']) ? $userinfo['headimgurl'] : '';
} else {
$userinfo = [];
}
$data = [
'access_token' => $access_token,
'refresh_token' => $refresh_token,
'expires_in' => $expires_in,
'openid' => $openid,
'unionid' => $unionid,
'userinfo' => $userinfo
];
return $data;
}
}
return [];
}
/**
* 获取access_token
* @param string code
* @return array
*/
public function getAccessToken($code = '')
{
if (!$code) {
return [];
}
$queryarr = array(
"appid" => $this->config['app_id'],
"secret" => $this->config['app_secret'],
"code" => $code,
"grant_type" => "authorization_code",
);
$response = Http::get(self::GET_ACCESS_TOKEN_URL, $queryarr);
$ret = (array)json_decode($response, true);
return $ret ? $ret : [];
}
}
... ...
<?php
namespace addons\third\library;
use fast\Http;
use think\Config;
use think\Session;
/**
* 微博
*/
class Weibo
{
const GET_AUTH_CODE_URL = "https://api.weibo.com/oauth2/authorize";
const GET_ACCESS_TOKEN_URL = "https://api.weibo.com/oauth2/access_token";
const GET_USERINFO_URL = "https://api.weibo.com/2/users/show.json";
/**
* 配置信息
* @var array
*/
private $config = [];
public function __construct($options = [])
{
if ($config = Config::get('third.weibo')) {
$this->config = array_merge($this->config, $config);
}
$this->config = array_merge($this->config, is_array($options) ? $options : []);
}
/**
* 登陆
*/
public function login()
{
header("Location:" . $this->getAuthorizeUrl());
}
/**
* 获取authorize_url
*/
public function getAuthorizeUrl()
{
$state = md5(uniqid(rand(), true));
Session::set('state', $state);
$queryarr = array(
"response_type" => "code",
"client_id" => $this->config['app_id'],
"redirect_uri" => $this->config['callback'],
"state" => $state,
);
request()->isMobile() && $queryarr['display'] = 'mobile';
$url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr);
return $url;
}
/**
* 获取用户信息
* @param array $params
* @return array
*/
public function getUserInfo($params = [])
{
$params = $params ? $params : $_GET;
if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) {
//获取access_token
$data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params;
$access_token = isset($data['access_token']) ? $data['access_token'] : '';
$refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : '';
$expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0;
if ($access_token) {
$uid = isset($data['uid']) ? $data['uid'] : '';
//获取用户信息
$queryarr = [
"access_token" => $access_token,
"uid" => $uid,
];
$ret = Http::get(self::GET_USERINFO_URL, $queryarr);
$userinfo = (array)json_decode($ret, true);
if (!$userinfo || isset($userinfo['error_code'])) {
return [];
}
$userinfo = $userinfo ? $userinfo : [];
$userinfo['nickname'] = isset($userinfo['screen_name']) ? $userinfo['screen_name'] : '';
$userinfo['avatar'] = isset($userinfo['profile_image_url']) ? $userinfo['profile_image_url'] : '';
$data = [
'access_token' => $access_token,
'refresh_token' => $refresh_token,
'expires_in' => $expires_in,
'openid' => $uid,
'userinfo' => $userinfo
];
return $data;
}
}
return [];
}
/**
* 获取access_token
* @param string code
* @return array
*/
public function getAccessToken($code = '')
{
if (!$code) {
return '';
}
$queryarr = array(
"grant_type" => "authorization_code",
"client_id" => $this->config['app_id'],
"client_secret" => $this->config['app_secret'],
"redirect_uri" => $this->config['callback'],
"code" => $code,
);
$response = Http::post(self::GET_ACCESS_TOKEN_URL, $queryarr);
$ret = (array)json_decode($response, true);
return $ret ? $ret : [];
}
}
... ...
<?php
namespace addons\third\model;
use think\Model;
/**
* 第三方登录模型
*/
class Third extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 追加属性
protected $append = [
];
public function user()
{
return $this->belongsTo('\app\common\model\User', 'user_id', 'id', [], 'LEFT');
}
}
... ...
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">第三方登录:</label>
<div class="col-xs-12 col-sm-4">
<!--@formatter:off-->
{if in_array('wechat', $platform)}
<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>
{else /}
<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>
{/if}
{if in_array('qq', $platform)}
<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>
{else /}
<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>
{/if}
{if in_array('weibo', $platform)}
<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>
{else /}
<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>
{/if}
<!--@formatter:on-->
</div>
</div>
... ...
<style>.social-login {
display: flex
}
.social-login a {
flex: 1;
margin: 0 2px;
}
.social-login a:first-child {
margin-left: 0;
}
.social-login a:last-child {
margin-right: 0;
}</style>
<div class="form-group social-login">
{if in_array('wechat', $status)}
<a class="btn btn-success" href="{:url('/third/connect/wechat')}?url={$url|urlencode}"><i class="fa fa-wechat"></i> 微信登录</a>
{/if}
{if in_array('qq', $status)}
<a class="btn btn-info" href="{:url('/third/connect/qq')}?url={$url|urlencode}"><i class="fa fa-qq"></i> QQ登录</a>
{/if}
{if in_array('weibo', $status)}
<a class="btn btn-danger" href="{:url('/third/connect/weibo')}?url={$url|urlencode}"><i class="fa fa-weibo"></i> 微博登录</a>
{/if}
</div>
... ...
<!DOCTYPE html>
<html>
<head>
<title>第三方登录 - {$site.name}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="__CDN__/assets/css/frontend.min.css" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<h2>第三方登录</h2>
<hr>
<div class="well">
<div class="row">
<div class="col-xs-4">
{if $user && in_array('qq', $platformList)}
<a href="{:addon_url('third/index/unbind',[':platform'=>'qq'])}" class="btn btn-block btn-info">
<i class="fa fa-qq"></i> 点击解绑
</a>
{else/}
<a href="{:addon_url('third/index/connect',[':platform'=>'qq'])}" class="btn btn-block btn-info">
<i class="fa fa-qq"></i> QQ登录
</a>
{/if}
</div>
<div class="col-xs-4">
{if $user && in_array('wechat', $platformList)}
<a href="{:addon_url('third/index/unbind',[':platform'=>'wechat'])}" class="btn btn-block btn-success">
<i class="fa fa-wechat"></i> 点击解绑
</a>
{else/}
<a href="{:addon_url('third/index/connect',[':platform'=>'wechat'])}" class="btn btn-block btn-success">
<i class="fa fa-wechat"></i> 微信登录
</a>
{/if}
</div>
<div class="col-xs-4">
{if $user && in_array('weibo', $platformList)}
<a href="{:addon_url('third/index/unbind',[':platform'=>'weibo'])}" class="btn btn-block btn-danger">
<i class="fa fa-weibo"></i> 点击解绑
</a>
{else/}
<a href="{:addon_url('third/index/connect',[':platform'=>'weibo'])}" class="btn btn-block btn-danger">
<i class="fa fa-weibo"></i> 微博登录
</a>
{/if}
</div>
</div>
</div>
<h2>相关链接</h2>
<hr>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>QQ</th>
<th>链接</th>
</tr>
</thead>
<tbody>
<tr>
<td>QQ 连接</td>
<td>{:addon_url('third/index/connect',[':platform'=>'qq'], false, true)}</td>
</tr>
<tr>
<td>QQ 绑定</td>
<td>{:addon_url('third/index/bind',[':platform'=>'qq'], false, true)}</td>
</tr>
<tr>
<td>QQ 解绑</td>
<td>{:addon_url('third/index/unbind',[':platform'=>'qq'], false, true)}</td>
</tr>
</tbody>
</table>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>微信</th>
<th>链接</th>
</tr>
</thead>
<tbody>
<tr>
<td>微信 连接</td>
<td>{:addon_url('third/index/connect',[':platform'=>'wechat'], false, true)}</td>
</tr>
<tr>
<td>微信 绑定</td>
<td>{:addon_url('third/index/bind',[':platform'=>'wechat'], false, true)}</td>
</tr>
<tr>
<td>微信 解绑</td>
<td>{:addon_url('third/index/unbind',[':platform'=>'wechat'], false, true)}</td>
</tr>
</tbody>
</table>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>微博</th>
<th>链接</th>
</tr>
</thead>
<tbody>
<tr>
<td>微博 连接</td>
<td>{:addon_url('third/index/connect',[':platform'=>'weibo'], false, true)}</td>
</tr>
<tr>
<td>微博 绑定</td>
<td>{:addon_url('third/index/bind',[':platform'=>'weibo'], false, true)}</td>
</tr>
<tr>
<td>微博 解绑</td>
<td>{:addon_url('third/index/unbind',[':platform'=>'weibo'], false, true)}</td>
</tr>
</tbody>
</table>
</div>
<!-- jQuery -->
<script src="https://cdn.jsdelivr.net/npm/jquery@2.1.4/dist/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function () {
});
</script>
</body>
</html>
... ...
{"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"]}
\ No newline at end of file
... ...
<?php
namespace addons\ueditor;
use think\Addons;
/**
* 百度Ueditor插件
*/
class Ueditor extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
return true;
}
/**
* @param $params
*/
public function configInit(&$params)
{
$config = $this->getConfig();
$params['ueditor'] = ['classname' => $config['classname'] ?? '.editor'];
$params['ueditor'] = ['baiduMapAk' => $config['baiduMapAk'] ?? ''];
}
}
... ...
window.UEDITOR_HOME_URL = Config.__CDN__ + "/assets/addons/ueditor/";
require.config({
paths: {
'ueditor.config': '../addons/ueditor/ueditor.config',
'ueditor': '../addons/ueditor/ueditor.all.min',
'ueditor.zh': '../addons/ueditor/i18n/zh-cn/zh-cn',
'zeroclipboard': '../addons/ueditor/third-party/zeroclipboard/ZeroClipboard.min',
},
shim: {
'ueditor': {
deps: ['zeroclipboard', 'ueditor.config'],
exports: 'UE',
init: function (ZeroClipboard) {
//导出到全局变量,供ueditor使用
window.ZeroClipboard = ZeroClipboard;
},
},
'ueditor.zh': ['ueditor']
}
});
require(['form', 'upload'], function (Form, Upload) {
var _bindevent = Form.events.bindevent;
Form.events.bindevent = function (form) {
_bindevent.apply(this, [form]);
try {
//绑定editor事件
require(['ueditor', 'ueditor.zh'], function (UE, undefined) {
UE.list = [];
window.UEDITOR_CONFIG['uploadService'] = function (context, editor) {
return {
Upload: () => { return Upload },
Fast: () => { return Fast },
}
};
$(Config.ueditor.classname || '.editor', form).each(function () {
var id = $(this).attr("id");
var name = $(this).attr("name");
$(this).removeClass('form-control');
UE.list[id] = UE.getEditor(id, {
allowDivTransToP: false, //阻止div自动转p标签
initialFrameWidth: '100%',
initialFrameHeight: 320,
autoFloatEnabled: false,
baiduMapAk: Config.ueditor.baiduMapAk || '', //百度地图api密钥(ak)
// autoHeightEnabled: true, //自动高度
zIndex: 90,
xssFilterRules: false,
outputXssFilter: false,
inputXssFilter: false,
catchRemoteImageEnable: true,
imageAllowFiles: '',//允许上传的图片格式,编辑器默认[".png", ".jpg", ".jpeg", ".gif", ".bmp"]
});
UE.list[id].addListener("contentChange", function () {
$('#' + id).val(this.getContent());
$('textarea[name="' + name + '"]').val(this.getContent());
})
});
})
} catch (e) {
console.log('绑定editor事件', e)
}
}
});
\ No newline at end of file
... ...
<?php
return [
[
'name' => 'classname',
'title' => '渲染文本框元素',
'type' => 'string',
'content' => [],
'value' => '.editor',
'rule' => 'required',
'msg' => '',
'tip' => '用于对指定的元素渲染,一般情况下无需修改',
'ok' => '',
'extend' => '',
],
[
'name' => 'baiduMapAk',
'title' => '百度地图ak',
'type' => 'string',
'content' => [],
'value' => '',
'rule' => '',
'msg' => '',
'tip' => '需要设置百度地图api密钥(ak),否则地图无法使用',
'ok' => '',
'extend' => '',
],
];
... ...
name = ueditor
title = 百度ueditor插件
intro = 基于百度Ueditor的富文本编辑器
author = chance
website = http://it-huai.gitee.io/ueditor-api/
version = 1.0.9
state = 1
url = /addons/ueditor
license = basic
licenseto = 10789
... ...
deny from all
\ No newline at end of file
... ...
<?php
namespace app\admin\behavior;
class AdminLog
{
public function run(&$params)
{
//只记录POST请求的日志
if (request()->isPost() && config('fastadmin.auto_record_log')) {
\app\admin\model\AdminLog::record();
}
}
}
... ...
<?php
namespace app\admin\command;
use think\addons\AddonException;
use think\addons\Service;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Db;
use think\Exception;
use think\exception\PDOException;
class Addon extends Command
{
protected function configure()
{
$this
->setName('addon')
->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null)
->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/uninstall/refresh/package/move)', 'create')
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null)
->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null)
->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null)
->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null)
->addOption('domain', 'd', Option::VALUE_OPTIONAL, 'domain', null)
->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null)
->setDescription('Addon manager');
}
protected function execute(Input $input, Output $output)
{
$name = $input->getOption('name') ?: '';
$action = $input->getOption('action') ?: '';
if (stripos($name, 'addons' . DS) !== false) {
$name = explode(DS, $name)[1];
}
//强制覆盖
$force = $input->getOption('force');
//版本
$release = $input->getOption('release') ?: '';
//uid
$uid = $input->getOption('uid') ?: '';
//token
$token = $input->getOption('token') ?: '';
include dirname(__DIR__) . DS . 'common.php';
if (!$name && !in_array($action, ['refresh'])) {
throw new Exception('Addon name could not be empty');
}
if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package', 'move'])) {
throw new Exception('Please input correct action name');
}
// 查询一次SQL,判断连接是否正常
Db::execute("SELECT 1");
$addonDir = ADDON_PATH . $name . DS;
switch ($action) {
case 'create':
//非覆盖模式时如果存在则报错
if (is_dir($addonDir) && !$force) {
throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true ");
}
//如果存在先移除
if (is_dir($addonDir)) {
rmdirs($addonDir);
}
mkdir($addonDir, 0755, true);
mkdir($addonDir . DS . 'controller', 0755, true);
$menuList = \app\common\library\Menu::export($name);
$createMenu = $this->getCreateMenu($menuList);
$prefix = Config::get('database.prefix');
$createTableSql = '';
try {
$result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;");
if (isset($result[0]) && isset($result[0]['Create Table'])) {
$createTableSql = $result[0]['Create Table'];
}
} catch (PDOException $e) {
}
$data = [
'name' => $name,
'addon' => $name,
'addonClassName' => ucfirst($name),
'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu) . ";\n\tMenu::create(\$menu);" : '',
'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '',
'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '',
'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '',
];
$this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php');
$this->writeToFile("config", $data, $addonDir . 'config.php');
$this->writeToFile("info", $data, $addonDir . 'info.ini');
$this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php');
if ($createTableSql) {
$createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql);
file_put_contents($addonDir . 'install.sql', $createTableSql);
}
$output->info("Create Successed!");
break;
case 'disable':
case 'enable':
try {
//调用启用、禁用的方法
Service::$action($name, 0);
} catch (AddonException $e) {
if ($e->getCode() != -3) {
throw new Exception($e->getMessage());
}
if (!$force) {
//如果有冲突文件则提醒
$data = $e->getData();
foreach ($data['conflictlist'] as $k => $v) {
$output->warning($v);
}
$output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: ");
$line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
if (trim($line) != 'yes') {
throw new Exception("Operation is aborted!");
}
}
//调用启用、禁用的方法
Service::$action($name, 1);
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
$output->info(ucfirst($action) . " Successed!");
break;
case 'uninstall':
//非覆盖模式时如果存在则报错
if (!$force) {
throw new Exception("If you need to uninstall addon, use the parameter --force=true ");
}
try {
Service::uninstall($name, 0);
} catch (AddonException $e) {
if ($e->getCode() != -3) {
throw new Exception($e->getMessage());
}
if (!$force) {
//如果有冲突文件则提醒
$data = $e->getData();
foreach ($data['conflictlist'] as $k => $v) {
$output->warning($v);
}
$output->info("Are you sure you want to delete all those files? Type 'yes' to continue: ");
$line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
if (trim($line) != 'yes') {
throw new Exception("Operation is aborted!");
}
}
Service::uninstall($name, 1);
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
$output->info("Uninstall Successed!");
break;
case 'refresh':
Service::refresh();
$output->info("Refresh Successed!");
break;
case 'package':
$infoFile = $addonDir . 'info.ini';
if (!is_file($infoFile)) {
throw new Exception(__('Addon info file was not found'));
}
$info = get_addon_info($name);
if (!$info) {
throw new Exception(__('Addon info file data incorrect'));
}
$infoname = isset($info['name']) ? $info['name'] : '';
if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) {
throw new Exception(__('Addon info name incorrect'));
}
$infoversion = isset($info['version']) ? $info['version'] : '';
if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) {
throw new Exception(__('Addon info version incorrect'));
}
$addonTmpDir = RUNTIME_PATH . 'addons' . DS;
if (!is_dir($addonTmpDir)) {
@mkdir($addonTmpDir, 0755, true);
}
$addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip';
if (!class_exists('ZipArchive')) {
throw new Exception(__('ZinArchive not install'));
}
$zip = new \ZipArchive;
$zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir)));
if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) {
$zip->addFile($filePath, $relativePath);
}
}
}
$zip->close();
$output->info("Package Successed!");
break;
case 'move':
$movePath = [
'adminOnlySelfDir' => ['admin/behavior', 'admin/controller', 'admin/library', 'admin/model', 'admin/validate', 'admin/view'],
'adminAllSubDir' => ['admin/lang'],
'publicDir' => ['public/assets/addons', 'public/assets/js/backend']
];
$paths = [];
$appPath = str_replace('/', DS, APP_PATH);
$rootPath = str_replace('/', DS, ROOT_PATH);
foreach ($movePath as $k => $items) {
switch ($k) {
case 'adminOnlySelfDir':
foreach ($items as $v) {
$v = str_replace('/', DS, $v);
$oldPath = $appPath . $v . DS . $name;
$newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $name;
$paths[$oldPath] = $newPath;
}
break;
case 'adminAllSubDir':
foreach ($items as $v) {
$v = str_replace('/', DS, $v);
$vPath = $appPath . $v;
$list = scandir($vPath);
foreach ($list as $_v) {
if (!in_array($_v, ['.', '..']) && is_dir($vPath . DS . $_v)) {
$oldPath = $appPath . $v . DS . $_v . DS . $name;
$newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $_v . DS . $name;
$paths[$oldPath] = $newPath;
}
}
}
break;
case 'publicDir':
foreach ($items as $v) {
$v = str_replace('/', DS, $v);
$oldPath = $rootPath . $v . DS . $name;
$newPath = $rootPath . 'addons' . DS . $name . DS . $v . DS . $name;
$paths[$oldPath] = $newPath;
}
break;
}
}
foreach ($paths as $oldPath => $newPath) {
if (is_dir($oldPath)) {
if ($force) {
if (is_dir($newPath)) {
$list = scandir($newPath);
foreach ($list as $_v) {
if (!in_array($_v, ['.', '..'])) {
$file = $newPath . DS . $_v;
@chmod($file, 0777);
@unlink($file);
}
}
@rmdir($newPath);
}
}
copydirs($oldPath, $newPath);
}
}
break;
default:
break;
}
}
/**
* 获取创建菜单的数组
* @param array $menu
* @return array
*/
protected function getCreateMenu($menu)
{
$result = [];
foreach ($menu as $k => & $v) {
$arr = [
'name' => $v['name'],
'title' => $v['title'],
];
if ($v['icon'] != 'fa fa-circle-o') {
$arr['icon'] = $v['icon'];
}
if ($v['ismenu']) {
$arr['ismenu'] = $v['ismenu'];
}
if (isset($v['childlist']) && $v['childlist']) {
$arr['sublist'] = $this->getCreateMenu($v['childlist']);
}
$result[] = $arr;
}
return $result;
}
/**
* 写入到文件
* @param string $name
* @param array $data
* @param string $pathname
* @return mixed
*/
protected function writeToFile($name, $data, $pathname)
{
$search = $replace = [];
foreach ($data as $k => $v) {
$search[] = "{%{$k}%}";
$replace[] = $v;
}
$stub = file_get_contents($this->getStub($name));
$content = str_replace($search, $replace, $stub);
if (!is_dir(dirname($pathname))) {
mkdir(strtolower(dirname($pathname)), 0755, true);
}
return file_put_contents($pathname, $content);
}
/**
* 获取基础模板
* @param string $name
* @return string
*/
protected function getStub($name)
{
return __DIR__ . '/Addon/stubs/' . $name . '.stub';
}
}
... ...
<?php
namespace addons\{%name%};
use app\common\library\Menu;
use think\Addons;
/**
* 插件
*/
class {%addonClassName%} extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
{%addonInstallMenu%}
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
{%addonUninstallMenu%}
return true;
}
/**
* 插件启用方法
* @return bool
*/
public function enable()
{
{%addonEnableMenu%}
return true;
}
/**
* 插件禁用方法
* @return bool
*/
public function disable()
{
{%addonDisableMenu%}
return true;
}
}
... ...
<?php
return [
[
//配置唯一标识
'name' => 'usernmae',
//显示的标题
'title' => '用户名',
//类型
'type' => 'string',
//分组
'group' => '',
//动态显示
'visible' => '',
//数据字典
'content' => [
],
//值
'value' => '',
//验证规则
'rule' => 'required',
//错误消息
'msg' => '',
//提示消息
'tip' => '',
//成功消息
'ok' => '',
//扩展信息
'extend' => ''
],
[
'name' => 'password',
'title' => '密码',
'type' => 'string',
'content' => [
],
'value' => '',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => ''
],
];
... ...
<?php
namespace addons\{%addon%}\controller;
use think\addons\Controller;
class Index extends Controller
{
public function index()
{
$this->error("当前插件暂无前台页面");
}
}
... ...
name = {%name%}
title = 插件名称{%name%}
intro = 插件介绍
author = yourname
website = https://www.fastadmin.net
version = 1.0.0
state = 1
... ...
<?php
namespace app\admin\command;
use app\admin\command\Api\library\Builder;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
class Api extends Command
{
protected function configure()
{
$site = Config::get('site');
$this
->setName('api')
->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '')
->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api')
->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html')
->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html')
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false)
->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'] ?? '')
->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null)
->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn')
->addOption('addon', 'a', Option::VALUE_OPTIONAL, 'addon name', null)
->addOption('controller', 'r', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name', null)
->setDescription('Build Api document from controller');
}
protected function execute(Input $input, Output $output)
{
$apiDir = __DIR__ . DS . 'Api' . DS;
$force = $input->getOption('force');
$url = $input->getOption('url');
$language = $input->getOption('language');
$template = $input->getOption('template');
if (!preg_match("/^([a-z0-9]+)\.html\$/i", $template)) {
throw new Exception('template file not correct');
}
$language = $language ? $language : 'zh-cn';
$langFile = $apiDir . 'lang' . DS . $language . '.php';
if (!is_file($langFile)) {
throw new Exception('language file not found');
}
$lang = include_once $langFile;
// 目标目录
$output_dir = ROOT_PATH . 'public' . DS;
$output_file = $output_dir . $input->getOption('output');
if (is_file($output_file) && !$force) {
throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true ");
}
// 模板文件
$template_dir = $apiDir . 'template' . DS;
$template_file = $template_dir . $template;
if (!is_file($template_file)) {
throw new Exception('template file not found');
}
// 额外的类
$classes = $input->getOption('class');
// 标题
$title = $input->getOption('title');
// 模块
$module = $input->getOption('module');
// 插件
$addon = $input->getOption('addon');
$moduleDir = $addonDir = '';
if ($addon) {
$addonInfo = get_addon_info($addon);
if (!$addonInfo) {
throw new Exception('addon not found');
}
$moduleDir = ADDON_PATH . $addon . DS;
} else {
$moduleDir = APP_PATH . $module . DS;
}
if (!is_dir($moduleDir)) {
throw new Exception('module not found');
}
if (version_compare(PHP_VERSION, '7.0.0', '<')) {
throw new Exception("Requires PHP version 7.0 or newer");
}
//控制器名
$controller = $input->getOption('controller') ?: [];
if (!$controller) {
$controllerDir = $moduleDir . Config::get('url_controller_layer') . DS;
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($controllerDir),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) {
if (!$file->isDir() && $file->getExtension() == 'php') {
$filePath = $file->getRealPath();
$classes[] = $this->get_class_from_file($filePath);
}
}
} else {
foreach ($controller as $index => $item) {
$filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php';
$classes[] = $this->get_class_from_file($filePath);
}
}
$classes = array_unique(array_filter($classes));
$config = [
'sitename' => config('site.name'),
'title' => $title,
'author' => config('site.name'),
'description' => '',
'apiurl' => $url,
'language' => $language,
];
$builder = new Builder($classes);
$content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]);
if (!file_put_contents($output_file, $content)) {
throw new Exception('Cannot save the content to ' . $output_file);
}
$output->info("Build Successed!");
}
/**
* get full qualified class name
*
* @param string $path_to_file
* @return string
* @author JBYRNE http://jarretbyrne.com/2015/06/197/
*/
protected function get_class_from_file($path_to_file)
{
//Grab the contents of the file
$contents = file_get_contents($path_to_file);
//Start with a blank namespace and class
$namespace = $class = "";
//Set helper values to know that we have found the namespace/class token and need to collect the string values after them
$getting_namespace = $getting_class = false;
//Go through each token and evaluate it as necessary
foreach (token_get_all($contents) as $token) {
//If this token is the namespace declaring, then flag that the next tokens will be the namespace name
if (is_array($token) && $token[0] == T_NAMESPACE) {
$getting_namespace = true;
}
//If this token is the class declaring, then flag that the next tokens will be the class name
if (is_array($token) && $token[0] == T_CLASS) {
$getting_class = true;
}
//While we're grabbing the namespace name...
if ($getting_namespace === true) {
//If the token is a string or the namespace separator...
if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) {
//Append the token's value to the name of the namespace
$namespace .= $token[1];
} elseif ($token === ';') {
//If the token is the semicolon, then we're done with the namespace declaration
$getting_namespace = false;
}
}
//While we're grabbing the class name...
if ($getting_class === true) {
//If the token is a string, it's the name of the class
if (is_array($token) && $token[0] == T_STRING) {
//Store the token's value as the class name
$class = $token[1];
//Got what we need, stope here
break;
}
}
}
//Build the fully-qualified class name and return it
return $namespace ? $namespace . '\\' . $class : $class;
}
}
... ...
<?php
return [
'Info' => '基础信息',
'Sandbox' => '在线测试',
'Sampleoutput' => '返回示例',
'Headers' => 'Headers',
'Parameters' => '参数',
'Body' => '正文',
'Name' => '名称',
'Type' => '类型',
'Required' => '必选',
'Description' => '描述',
'Send' => '提交',
'Reset' => '重置',
'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中',
'Apiurltips' => 'API接口URL',
'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中',
'Authorization' => '权限',
'NeedLogin' => '登录',
'NeedRight' => '鉴权',
'ReturnHeaders' => '响应头',
'ReturnParameters' => '返回参数',
'Response' => '响应输出',
];
... ...
<?php
namespace app\admin\command\Api\library;
use think\Config;
/**
* @website https://github.com/calinrada/php-apidoc
* @author Calin Rada <rada.calin@gmail.com>
* @author Karson <karson@fastadmin.net>
*/
class Builder
{
/**
*
* @var \think\View
*/
public $view = null;
/**
* parse classes
* @var array
*/
protected $classes = [];
/**
*
* @param array $classes
*/
public function __construct($classes = [])
{
$this->classes = array_merge($this->classes, $classes);
$this->view = new \think\View(Config::get('template'), Config::get('view_replace_str'));
}
protected function extractAnnotations()
{
foreach ($this->classes as $class) {
$classAnnotation = Extractor::getClassAnnotations($class);
// 如果忽略
if (isset($classAnnotation['ApiInternal'])) {
continue;
}
Extractor::getClassMethodAnnotations($class);
//Extractor::getClassPropertyValues($class);
}
$allClassAnnotation = Extractor::getAllClassAnnotations();
$allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations();
//$allClassPropertyValue = Extractor::getAllClassPropertyValues();
// foreach ($allClassMethodAnnotation as $className => &$methods) {
// foreach ($methods as &$method) {
// //权重判断
// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) {
// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh'];
// }
// }
// }
// unset($methods);
return [$allClassAnnotation, $allClassMethodAnnotation];
}
protected function generateHeadersTemplate($docs)
{
if (!isset($docs['ApiHeaders'])) {
return [];
}
$headerslist = array();
foreach ($docs['ApiHeaders'] as $params) {
$tr = array(
'name' => $params['name'] ?? '',
'type' => $params['type'] ?? 'string',
'sample' => $params['sample'] ?? '',
'required' => $params['required'] ?? false,
'description' => $params['description'] ?? '',
);
$headerslist[] = $tr;
}
return $headerslist;
}
protected function generateParamsTemplate($docs)
{
if (!isset($docs['ApiParams'])) {
return [];
}
$paramslist = array();
foreach ($docs['ApiParams'] as $params) {
$tr = array(
'name' => $params['name'],
'type' => $params['type'] ?? 'string',
'sample' => $params['sample'] ?? '',
'required' => $params['required'] ?? true,
'description' => $params['description'] ?? '',
);
$paramslist[] = $tr;
}
return $paramslist;
}
protected function generateReturnHeadersTemplate($docs)
{
if (!isset($docs['ApiReturnHeaders'])) {
return [];
}
$headerslist = array();
foreach ($docs['ApiReturnHeaders'] as $params) {
$tr = array(
'name' => $params['name'] ?? '',
'type' => 'string',
'sample' => $params['sample'] ?? '',
'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No',
'description' => $params['description'] ?? '',
);
$headerslist[] = $tr;
}
return $headerslist;
}
protected function generateReturnParamsTemplate($st_params)
{
if (!isset($st_params['ApiReturnParams'])) {
return [];
}
$paramslist = array();
foreach ($st_params['ApiReturnParams'] as $params) {
$tr = array(
'name' => $params['name'] ?? '',
'type' => $params['type'] ?? 'string',
'sample' => $params['sample'] ?? '',
'description' => $params['description'] ?? '',
);
$paramslist[] = $tr;
}
return $paramslist;
}
protected function generateBadgeForMethod($data)
{
$method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]);
$labes = array(
'POST' => 'label-primary',
'GET' => 'label-success',
'PUT' => 'label-warning',
'DELETE' => 'label-danger',
'PATCH' => 'label-default',
'OPTIONS' => 'label-info'
);
return isset($labes[$method]) ? $labes[$method] : $labes['GET'];
}
public function parse()
{
list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations();
$sectorArr = [];
foreach ($allClassAnnotations as $index => &$allClassAnnotation) {
// 如果设置隐藏,则不显示在文档
if (isset($allClassAnnotation['ApiInternal'])) {
continue;
}
$sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0];
$sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0;
}
unset($allClassAnnotation);
arsort($sectorArr);
$routes = include_once CONF_PATH . 'route.php';
$subdomain = false;
if (config('url_domain_deploy') && isset($routes['__domain__']) && isset($routes['__domain__']['api']) && $routes['__domain__']['api']) {
$subdomain = true;
}
$counter = 0;
$section = null;
$weigh = 0;
$docsList = [];
foreach ($allClassMethodAnnotations as $class => $methods) {
foreach ($methods as $name => $docs) {
if (isset($docs['ApiSector'][0])) {
$section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0];
} else {
$section = $class;
}
if (0 === count($docs)) {
continue;
}
$route = is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0];
if ($subdomain) {
$route = substr($route, 4);
}
$docsList[$section][$name] = [
'id' => $counter,
'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0],
'methodLabel' => $this->generateBadgeForMethod($docs),
'section' => $section,
'route' => $route,
'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0],
'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0],
'body' => isset($docs['ApiBody'][0]) ? (is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0]) : '',
'headersList' => $this->generateHeadersTemplate($docs),
'paramsList' => $this->generateParamsTemplate($docs),
'returnHeadersList' => $this->generateReturnHeadersTemplate($docs),
'returnParamsList' => $this->generateReturnParamsTemplate($docs),
'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0],
'return' => isset($docs['ApiReturn']) ? (is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0]) : '',
'needLogin' => $docs['ApiPermissionLogin'][0],
'needRight' => $docs['ApiPermissionRight'][0],
];
$counter++;
}
}
//重建排序
foreach ($docsList as $index => &$methods) {
$methodSectorArr = [];
foreach ($methods as $name => $method) {
$methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0;
}
arsort($methodSectorArr);
$methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods);
}
$docsList = array_merge(array_flip(array_keys($sectorArr)), $docsList);
return $docsList;
}
public function getView()
{
return $this->view;
}
/**
* 渲染
* @param string $template
* @param array $vars
* @return string
*/
public function render($template, $vars = [])
{
$docsList = $this->parse();
return $this->view->display(file_get_contents($template), array_merge($vars, ['docsList' => $docsList]));
}
}
... ...
<?php
namespace app\admin\command\Api\library;
use Exception;
/**
* Class imported from https://github.com/eriknyk/Annotations
* @author Erik Amaru Ortiz https://github.com/eriknyk‎
*
* @license http://opensource.org/licenses/bsd-license.php The BSD License
* @author Calin Rada <rada.calin@gmail.com>
*/
class Extractor
{
/**
* Static array to store already parsed annotations
* @var array
*/
private static $annotationCache;
private static $classAnnotationCache;
private static $classMethodAnnotationCache;
private static $classPropertyValueCache;
/**
* Indicates that annotations should has strict behavior, 'false' by default
* @var boolean
*/
private $strict = false;
/**
* Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects()
* @var string
*/
public $defaultNamespace = '';
/**
* Sets strict variable to true/false
* @param bool $value boolean value to indicate that annotations to has strict behavior
*/
public function setStrict($value)
{
$this->strict = (bool)$value;
}
/**
* Sets default namespace to use in object instantiation
* @param string $namespace default namespace
*/
public function setDefaultNamespace($namespace)
{
$this->defaultNamespace = $namespace;
}
/**
* Gets default namespace used in object instantiation
* @return string $namespace default namespace
*/
public function getDefaultAnnotationNamespace()
{
return $this->defaultNamespace;
}
/**
* Gets all anotations with pattern @SomeAnnotation() from a given class
*
* @param string $className class name to get annotations
* @return array self::$classAnnotationCache all annotated elements
*/
public static function getClassAnnotations($className)
{
if (!isset(self::$classAnnotationCache[$className])) {
$class = new \ReflectionClass($className);
$annotationArr = self::parseAnnotations($class->getDocComment());
$annotationArr['ApiTitle'] = !isset($annotationArr['ApiTitle'][0]) || !trim($annotationArr['ApiTitle'][0]) ? [$class->getShortName()] : $annotationArr['ApiTitle'];
self::$classAnnotationCache[$className] = $annotationArr;
}
return self::$classAnnotationCache[$className];
}
/**
* 获取类所有方法的属性配置
* @param $className
* @return mixed
* @throws \ReflectionException
*/
public static function getClassMethodAnnotations($className)
{
$class = new \ReflectionClass($className);
foreach ($class->getMethods() as $object) {
self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name);
}
return self::$classMethodAnnotationCache[$className];
}
public static function getClassPropertyValues($className)
{
$class = new \ReflectionClass($className);
foreach ($class->getProperties() as $object) {
self::$classPropertyValueCache[$className][$object->name] = self::getClassPropertyValue($className, $object->name);
}
return self::$classMethodAnnotationCache[$className];
}
public static function getAllClassAnnotations()
{
return self::$classAnnotationCache;
}
public static function getAllClassMethodAnnotations()
{
return self::$classMethodAnnotationCache;
}
public static function getAllClassPropertyValues()
{
return self::$classPropertyValueCache;
}
public static function getClassPropertyValue($className, $property)
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$reflectionClass = new \ReflectionClass($className);
$reflectionProperty = $reflectionClass->getProperty($property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($reflectionClass->newInstanceWithoutConstructor());
}
/**
* Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class
*
* @param string $className class name
* @param string $methodName method name to get annotations
* @return array self::$annotationCache all annotated elements of a method given
*/
public static function getMethodAnnotations($className, $methodName)
{
if (!isset(self::$annotationCache[$className . '::' . $methodName])) {
try {
$method = new \ReflectionMethod($className, $methodName);
$class = new \ReflectionClass($className);
if (!$method->isPublic() || $method->isConstructor()) {
$annotations = array();
} else {
$annotations = self::consolidateAnnotations($method, $class);
}
} catch (\ReflectionException $e) {
$annotations = array();
}
self::$annotationCache[$className . '::' . $methodName] = $annotations;
}
return self::$annotationCache[$className . '::' . $methodName];
}
/**
* Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class
* and instance its abcAnnotation class
*
* @param string $className class name
* @param string $methodName method name to get annotations
* @return array self::$annotationCache all annotated objects of a method given
*/
public function getMethodAnnotationsObjects($className, $methodName)
{
$annotations = $this->getMethodAnnotations($className, $methodName);
$objects = array();
$i = 0;
foreach ($annotations as $annotationClass => $listParams) {
$annotationClass = ucfirst($annotationClass);
$class = $this->defaultNamespace . $annotationClass . 'Annotation';
// verify is the annotation class exists, depending if Annotations::strict is true
// if not, just skip the annotation instance creation.
if (!class_exists($class)) {
if ($this->strict) {
throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class));
} else {
// silent skip & continue
continue;
}
}
if (empty($objects[$annotationClass])) {
$objects[$annotationClass] = new $class();
}
foreach ($listParams as $params) {
if (is_array($params)) {
foreach ($params as $key => $value) {
$objects[$annotationClass]->set($key, $value);
}
} else {
$objects[$annotationClass]->set($i++, $params);
}
}
}
return $objects;
}
private static function consolidateAnnotations($method, $class)
{
$dockblockClass = $class->getDocComment();
$docblockMethod = $method->getDocComment();
$methodName = $method->getName();
$methodAnnotations = self::parseAnnotations($docblockMethod);
$methodAnnotations['ApiTitle'] = !isset($methodAnnotations['ApiTitle'][0]) || !trim($methodAnnotations['ApiTitle'][0]) ? [$method->getName()] : $methodAnnotations['ApiTitle'];
$classAnnotations = self::parseAnnotations($dockblockClass);
$classAnnotations['ApiTitle'] = !isset($classAnnotations['ApiTitle'][0]) || !trim($classAnnotations['ApiTitle'][0]) ? [$class->getShortName()] : $classAnnotations['ApiTitle'];
if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') {
return [];
}
$properties = $class->getDefaultProperties();
$noNeedLogin = isset($properties['noNeedLogin']) ? (is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']]) : [];
$noNeedRight = isset($properties['noNeedRight']) ? (is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']]) : [];
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr);
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr);
if (!isset($methodAnnotations['ApiMethod'])) {
$methodAnnotations['ApiMethod'] = ['get'];
}
if (!isset($methodAnnotations['ApiWeigh'])) {
$methodAnnotations['ApiWeigh'] = [0];
}
if (!isset($methodAnnotations['ApiSummary'])) {
$methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle'];
}
if ($methodAnnotations) {
foreach ($classAnnotations as $name => $valueClass) {
if (count($valueClass) !== 1) {
continue;
}
if ($name === 'ApiRoute') {
if (isset($methodAnnotations[$name])) {
$methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]];
} else {
$methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()];
}
}
if ($name === 'ApiSector') {
$methodAnnotations[$name] = $valueClass;
}
}
}
if (!isset($methodAnnotations['ApiRoute'])) {
$urlArr = [];
$className = $class->getName();
list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className);
$prefixArr = explode('\\', $prefix);
$suffixArr = explode('\\', $suffix);
if ($prefixArr[0] == \think\Config::get('app_namespace')) {
$prefixArr[0] = '';
}
$urlArr = array_merge($urlArr, $prefixArr);
$urlArr[] = implode('.', array_map(function ($item) {
return \think\Loader::parseName($item);
}, $suffixArr));
$urlArr[] = $method->getName();
$methodAnnotations['ApiRoute'] = [implode('/', $urlArr)];
}
if (!isset($methodAnnotations['ApiSector'])) {
$methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : $classAnnotations['ApiTitle'];
}
if (!isset($methodAnnotations['ApiParams'])) {
$params = self::parseCustomAnnotations($docblockMethod, 'param');
foreach ($params as $k => $v) {
$arr = explode(' ', preg_replace("/[\s]+/", " ", $v));
$methodAnnotations['ApiParams'][] = [
'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '',
'nullable' => false,
'type' => isset($arr[0]) ? $arr[0] : 'string',
'description' => isset($arr[2]) ? $arr[2] : ''
];
}
}
$methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)];
$methodAnnotations['ApiPermissionRight'] = !$methodAnnotations['ApiPermissionLogin'][0] ? [false] : [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)];
return $methodAnnotations;
}
/**
* Parse annotations
*
* @param string $docblock
* @param string $name
* @return array parsed annotations params
*/
private static function parseCustomAnnotations($docblock, $name = 'param')
{
$annotations = array();
$docblock = substr($docblock, 3, -2);
if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) {
foreach ($matches[1] as $k => $v) {
$annotations[] = $v;
}
}
return $annotations;
}
/**
* Parse annotations
*
* @param string $docblock
* @return array parsed annotations params
*/
private static function parseAnnotations($docblock)
{
$annotations = array();
// Strip away the docblock header and footer to ease parsing of one line annotations
$docblock = substr($docblock, 3, -2);
if (preg_match_all('/@(?<name>[A-Za-z_-]+)[\s\t]*\((?<args>(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) {
$numMatches = count($matches[0]);
for ($i = 0; $i < $numMatches; ++$i) {
$name = $matches['name'][$i];
$value = '';
// annotations has arguments
if (isset($matches['args'][$i])) {
$argsParts = trim($matches['args'][$i]);
if ($name == 'ApiReturn') {
$value = $argsParts;
} elseif ($matches['args'][$i] != '') {
$argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts);
$value = self::parseArgs($argsParts);
if (is_string($value)) {
$value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts);
}
}
}
$annotations[$name][] = $value;
}
}
if (stripos($docblock, '@ApiInternal') !== false) {
$annotations['ApiInternal'] = [true];
}
if (!isset($annotations['ApiTitle'])) {
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr);
$title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : '';
$annotations['ApiTitle'] = [$title];
}
return $annotations;
}
/**
* Parse individual annotation arguments
*
* @param string $content arguments string
* @return array annotated arguments
*/
private static function parseArgs($content)
{
// Replace initial stars
$content = preg_replace('/^\s*\*/m', '', $content);
$data = array();
$len = strlen($content);
$i = 0;
$var = '';
$val = '';
$level = 1;
$prevDelimiter = '';
$nextDelimiter = '';
$nextToken = '';
$composing = false;
$type = 'plain';
$delimiter = null;
$quoted = false;
$tokens = array('"', '"', '{', '}', ',', '=');
while ($i <= $len) {
$prev_c = substr($content, $i - 1, 1);
$c = substr($content, $i++, 1);
if ($c === '"' && $prev_c !== "\\") {
$delimiter = $c;
//open delimiter
if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) {
$prevDelimiter = $nextDelimiter = $delimiter;
$val = '';
$composing = true;
$quoted = true;
} else {
// close delimiter
if ($c !== $nextDelimiter) {
throw new Exception(sprintf(
"Parse Error: enclosing error -> expected: [%s], given: [%s]",
$nextDelimiter,
$c
));
}
// validating syntax
if ($i < $len) {
if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) {
throw new Exception(sprintf(
"Parse Error: missing comma separator near: ...%s<--",
substr($content, ($i - 10), $i)
));
}
}
$prevDelimiter = $nextDelimiter = '';
$composing = false;
$delimiter = null;
}
} elseif (!$composing && in_array($c, $tokens)) {
switch ($c) {
case '=':
$prevDelimiter = $nextDelimiter = '';
$level = 2;
$composing = false;
$type = 'assoc';
$quoted = false;
break;
case ',':
$level = 3;
// If composing flag is true yet,
// it means that the string was not enclosed, so it is parsing error.
if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) {
throw new Exception(sprintf(
"Parse Error: enclosing error -> expected: [%s], given: [%s]",
$nextDelimiter,
$c
));
}
$prevDelimiter = $nextDelimiter = '';
break;
case '{':
$subc = '';
$subComposing = true;
while ($i <= $len) {
$c = substr($content, $i++, 1);
if (isset($delimiter) && $c === $delimiter) {
throw new Exception(sprintf(
"Parse Error: Composite variable is not enclosed correctly."
));
}
if ($c === '}') {
$subComposing = false;
break;
}
$subc .= $c;
}
// if the string is composing yet means that the structure of var. never was enclosed with '}'
if ($subComposing) {
throw new Exception(sprintf(
"Parse Error: Composite variable is not enclosed correctly. near: ...%s'",
$subc
));
}
$val = self::parseArgs($subc);
break;
}
} else {
if ($level == 1) {
$var .= $c;
} elseif ($level == 2) {
$val .= $c;
}
}
if ($level === 3 || $i === $len) {
if ($type == 'plain' && $i === $len) {
$data = self::castValue($var);
} else {
$data[trim($var)] = self::castValue($val, !$quoted);
}
$level = 1;
$var = $val = '';
$composing = false;
$quoted = false;
}
}
return $data;
}
/**
* Try determinate the original type variable of a string
*
* @param string $val string containing possibles variables that can be cast to bool or int
* @param boolean $trim indicate if the value passed should be trimmed after to try cast
* @return mixed returns the value converted to original type if was possible
*/
private static function castValue($val, $trim = false)
{
if (is_array($val)) {
foreach ($val as $key => $value) {
$val[$key] = self::castValue($value);
}
} elseif (is_string($val)) {
if ($trim) {
$val = trim($val);
}
$val = stripslashes($val);
$tmp = strtolower($val);
if ($tmp === 'false' || $tmp === 'true') {
$val = $tmp === 'true';
} elseif (is_numeric($val)) {
return $val + 0;
}
unset($tmp);
}
return $val;
}
}
... ...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<title>{$config.title}</title>
<!-- Bootstrap Core CSS -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- Plugin CSS -->
<link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<style type="text/css">
body {
padding-top: 70px; margin-bottom: 15px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: "Roboto", "SF Pro SC", "SF Pro Display", "SF Pro Icons", "PingFang SC", BlinkMacSystemFont, -apple-system, "Segoe UI", "Microsoft Yahei", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-weight: 400;
}
h2 { font-size: 1.2em; }
hr { margin-top: 10px; }
.tab-pane { padding-top: 10px; }
.mt0 { margin-top: 0px; }
.footer { font-size: 12px; color: #666; }
.docs-list .label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; }
.string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
.popover { max-width: 400px; max-height: 400px; overflow-y: auto;}
.list-group.panel > .list-group-item {
}
.list-group-item:last-child {
border-radius:0;
}
h4.panel-title a {
font-weight:normal;
font-size:14px;
}
h4.panel-title a .text-muted {
font-size:12px;
font-weight:normal;
font-family: 'Verdana';
}
#sidebar {
width: 220px;
position: fixed;
margin-left: -240px;
overflow-y:auto;
}
#sidebar > .list-group {
margin-bottom:0;
}
#sidebar > .list-group > a{
text-indent:0;
}
#sidebar .child > a .tag{
position: absolute;
right: 10px;
top: 11px;
}
#sidebar .child > a .pull-right{
margin-left:3px;
}
#sidebar .child {
border:1px solid #ddd;
border-bottom:none;
}
#sidebar .child:last-child {
border-bottom:1px solid #ddd;
}
#sidebar .child > a {
border:0;
min-height: 40px;
}
#sidebar .list-group a.current {
background:#f5f5f5;
}
@media (max-width: 1620px){
#sidebar {
margin:0;
}
#accordion {
padding-left:235px;
}
}
@media (max-width: 768px){
#sidebar {
display: none;
}
#accordion {
padding-left:0px;
}
}
.label-primary {
background-color: #248aff;
}
.docs-list .panel .panel-body .table {
margin-bottom: 0;
}
</style>
</head>
<body>
<!-- Fixed navbar -->
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="./" target="_blank">{$config.title}</a>
</div>
<div class="navbar-collapse collapse">
<form class="navbar-form navbar-right">
<div class="form-group">
Token:
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Tokentips}" placeholder="token" id="token" />
</div>
<div class="form-group">
Apiurl:
</div>
<div class="form-group">
<input id="apiUrl" type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Apiurltips}" placeholder="https://api.mydomain.com" value="{$config.apiurl}" />
</div>
<div class="form-group">
<button type="button" class="btn btn-success btn-sm" data-toggle="tooltip" title="{$lang.Savetips}" id="save_data">
<span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
</button>
</div>
</form>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container">
<!-- menu -->
<div id="sidebar">
<div class="list-group panel">
{foreach name="docsList" id="docs"}
<a href="#{$key}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a>
<div class="child collapse" id="{$key}">
{foreach name="docs" id="api" }
<a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title}
<span class="tag">
{if $api.needRight}
<span class="label label-danger pull-right"></span>
{/if}
{if $api.needLogin}
<span class="label label-success pull-right noneedlogin"></span>
{/if}
</span>
</a>
{/foreach}
</div>
{/foreach}
</div>
</div>
<div class="panel-group docs-list" id="accordion">
{foreach name="docsList" id="docs"}
<h2>{$key}</h2>
<hr>
{foreach name="docs" id="api" }
<div class="panel panel-default">
<div class="panel-heading" id="heading-{$api.id}">
<h4 class="panel-title">
<span class="label {$api.methodLabel}">{$api.method|strtoupper}</span>
<a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></a>
</h4>
</div>
<div id="collapseOne{$api.id}" class="panel-collapse collapse">
<div class="panel-body">
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="doctab{$api.id}">
<li class="active"><a href="#info{$api.id}" data-toggle="tab">{$lang.Info}</a></li>
<li><a href="#sandbox{$api.id}" data-toggle="tab">{$lang.Sandbox}</a></li>
<li><a href="#sample{$api.id}" data-toggle="tab">{$lang.Sampleoutput}</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="info{$api.id}">
<div class="well">
{$api.summary}
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Authorization}</strong></div>
<div class="panel-body">
<table class="table table-hover">
<tbody>
<tr>
<td>{$lang.NeedLogin}</td>
<td>{$api.needLogin?'是':'否'}</td>
</tr>
<tr>
<td>{$lang.NeedRight}</td>
<td>{$api.needRight?'是':'否'}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Headers}</strong></div>
<div class="panel-body">
{if $api.headersList}
<table class="table table-hover">
<thead>
<tr>
<th>{$lang.Name}</th>
<th>{$lang.Type}</th>
<th>{$lang.Required}</th>
<th>{$lang.Description}</th>
</tr>
</thead>
<tbody>
{foreach name="api['headersList']" id="header"}
<tr>
<td>{$header.name}</td>
<td>{$header.type}</td>
<td>{$header.required?'是':'否'}</td>
<td>{$header.description}</td>
</tr>
{/foreach}
</tbody>
</table>
{else /}
{/if}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
<div class="panel-body">
{if $api.paramsList}
<table class="table table-hover">
<thead>
<tr>
<th>{$lang.Name}</th>
<th>{$lang.Type}</th>
<th>{$lang.Required}</th>
<th>{$lang.Description}</th>
</tr>
</thead>
<tbody>
{foreach name="api['paramsList']" id="param"}
<tr>
<td>{$param.name}</td>
<td>{$param.type}</td>
<td>{:$param.required?'是':'否'}</td>
<td>{$param.description}</td>
</tr>
{/foreach}
</tbody>
</table>
{else /}
{/if}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Body}</strong></div>
<div class="panel-body">
{$api.body|default='无'}
</div>
</div>
</div><!-- #info -->
<div class="tab-pane" id="sandbox{$api.id}">
<div class="row">
<div class="col-md-12">
{if $api.headersList}
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Headers}</strong></div>
<div class="panel-body">
<div class="headers">
{foreach name="api['headersList']" id="param"}
<div class="form-group">
<label class="control-label" for="{$param.name}">{$param.name}</label>
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}">
</div>
{/foreach}
</div>
</div>
</div>
{/if}
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Parameters}</strong>
<div class="pull-right">
<a href="javascript:" class="btn btn-xs btn-info btn-append">追加</a>
</div>
</div>
<div class="panel-body">
<form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}">
{if $api.paramsList}
{foreach name="api['paramsList']" id="param"}
<div class="form-group">
<label class="control-label" for="{$param.name}">{$param.name}</label>
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}">
</div>
{/foreach}
{else /}
<div class="form-group">
</div>
{/if}
<div class="form-group form-group-submit">
<button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button>
<button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button>
</div>
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Response}</strong></div>
<div class="panel-body">
<div class="row">
<div class="col-md-12" style="overflow-x:auto">
<pre id="response_headers{$api.id}"></pre>
<pre id="response{$api.id}"></pre>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div>
<div class="panel-body">
{if $api.returnParamsList}
<table class="table table-hover">
<thead>
<tr>
<th>{$lang.Name}</th>
<th>{$lang.Type}</th>
<th>{$lang.Description}</th>
</tr>
</thead>
<tbody>
{foreach name="api['returnParamsList']" id="param"}
<tr>
<td>{$param.name}</td>
<td>{$param.type}</td>
<td>{$param.description}</td>
</tr>
{/foreach}
</tbody>
</table>
{else /}
{/if}
</div>
</div>
</div>
</div>
</div><!-- #sandbox -->
<div class="tab-pane" id="sample{$api.id}">
<div class="row">
<div class="col-md-12">
<pre id="sample_response{$api.id}">{$api.return|default='无'}</pre>
</div>
</div>
</div><!-- #sample -->
</div><!-- .tab-content -->
</div>
</div>
</div>
{/foreach}
{/foreach}
</div>
<hr>
<div class="row mt0 footer">
<div class="col-md-6" align="left">
</div>
<div class="col-md-6" align="right">
Generated on {:date('Y-m-d H:i:s')} <a href="./" target="_blank">{$config.sitename}</a>
</div>
</div>
</div> <!-- /container -->
<!-- jQuery -->
<script src="https://cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript">
function syntaxHighlight(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
function prepareStr(str) {
try {
return syntaxHighlight(JSON.stringify(JSON.parse(str.replace(/'/g, '"')), null, 2));
} catch (e) {
return str;
}
}
var storage = (function () {
var uid = new Date;
var storage;
var result;
try {
(storage = window.localStorage).setItem(uid, uid);
result = storage.getItem(uid) == uid;
storage.removeItem(uid);
return result && storage;
} catch (exception) {
}
}());
$.fn.serializeObject = function ()
{
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (!this.value) {
return;
}
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
$(document).ready(function () {
if (storage) {
storage.getItem('token') && $('#token').val(storage.getItem('token'));
storage.getItem('apiUrl') && $('#apiUrl').val(storage.getItem('apiUrl'));
}
$('[data-toggle="tooltip"]').tooltip({
placement: 'bottom'
});
$(window).on("resize", function(){
$("#sidebar").css("max-height", $(window).height()-80);
});
$(window).trigger("resize");
$(document).on("click", "#sidebar .list-group > .list-group-item", function(){
$("#sidebar .list-group > .list-group-item").removeClass("current");
$(this).addClass("current");
});
$(document).on("click", "#sidebar .child a", function(){
var heading = $("#heading-"+$(this).data("id"));
if(!heading.next().hasClass("in")){
$("a", heading).trigger("click");
}
$("html,body").animate({scrollTop:heading.offset().top-70});
});
$('code[id^=response]').hide();
$.each($('pre[id^=sample_response],pre[id^=sample_post_body]'), function () {
if ($(this).html() == 'NA') {
return;
}
var str = prepareStr($(this).html());
$(this).html(str);
});
$("[data-toggle=popover]").popover({placement: 'right'});
$('[data-toggle=popover]').on('shown.bs.popover', function () {
var $sample = $(this).parent().find(".popover-content"),
str = $(this).data('content');
if (typeof str == "undefined" || str === "") {
return;
}
var str = prepareStr(str);
$sample.html('<pre>' + str + '</pre>');
});
$(document).on('click', '#save_data', function (e) {
if (storage) {
storage.setItem('token', $('#token').val());
storage.setItem('apiUrl', $('#apiUrl').val());
} else {
alert('Your browser does not support local storage');
}
});
$(document).on('click', '.btn-append', function (e) {
$($("#appendtpl").html()).insertBefore($(this).closest(".panel").find(".form-group-submit"));
return false;
});
$(document).on('click', '.btn-remove', function (e) {
$(this).closest(".form-group").remove();
return false;
});
$(document).on('keyup', '.input-custom-name', function (e) {
$(this).closest(".row").find(".input-custom-value").attr("name", $(this).val());
return false;
});
$(document).on('click', '.send', function (e) {
e.preventDefault();
var form = $(this).closest('form');
//added /g to get all the matched params instead of only first
var matchedParamsInRoute = $(form).attr('action').match(/[^{]+(?=\})/g);
var theId = $(this).attr('rel');
//keep a copy of action attribute in order to modify the copy
//instead of the initial attribute
var url = $(form).attr('action');
var method = $(form).prop('method').toLowerCase() || 'get';
var formData = new FormData();
$(form).find('input').each(function (i, input) {
if ($(input).attr('type').toLowerCase() == 'file') {
formData.append($(input).attr('name'), $(input)[0].files[0]);
method = 'post';
} else {
formData.append($(input).attr('name'), $(input).val())
}
});
var index, key, value;
if (matchedParamsInRoute) {
var params = {};
formData.forEach(function(value, key){
params[key] = value;
});
for (index = 0; index < matchedParamsInRoute.length; ++index) {
try {
key = matchedParamsInRoute[index];
value = params[key];
if (typeof value == "undefined")
value = "";
url = url.replace("\{" + key + "\}", value);
formData.delete(key);
} catch (err) {
console.log(err);
}
}
}
var headers = {};
var token = $('#token').val();
if (token.length > 0) {
headers['token'] = token;
}
$("#sandbox" + theId + " .headers input[type=text]").each(function () {
val = $(this).val();
if (val.length > 0) {
headers[$(this).prop('name')] = val;
}
});
$.ajax({
url: $('#apiUrl').val() + url,
data: method == 'get' ? $(form).serialize() : formData,
type: method,
dataType: 'json',
contentType: false,
processData: false,
headers: headers,
xhrFields: {
withCredentials: true
},
success: function (data, textStatus, xhr) {
if (typeof data === 'object') {
var str = JSON.stringify(data, null, 2);
$('#response' + theId).html(syntaxHighlight(str));
} else {
$('#response' + theId).html(data || '');
}
$('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
$('#response' + theId).show();
},
error: function (xhr, textStatus, error) {
try {
var str = JSON.stringify($.parseJSON(xhr.responseText), null, 2);
} catch (e) {
var str = xhr.responseText;
}
$('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
$('#response' + theId).html(syntaxHighlight(str));
$('#response' + theId).show();
}
});
return false;
});
});
</script>
<script type="text/html" id="appendtpl">
<div class="form-group">
<label class="control-label">自定义</label>
<div class="row">
<div class="col-xs-4">
<input type="text" class="form-control input-sm input-custom-name" placeholder="名称">
</div>
<div class="col-xs-6">
<input type="text" class="form-control input-sm input-custom-value" placeholder="值">
</div>
<div class="col-xs-2 text-center">
<a href="javascript:" class="btn btn-sm btn-danger btn-remove">删除</a>
</div>
</div>
</div>
</script>
</body>
</html>
... ...