代码功能更新
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\apidoc\controller;
|
||||
|
||||
use laytp\controller\Backend;
|
||||
use laytp\library\CommonFun;
|
||||
use laytp\library\UploadDomain;
|
||||
|
||||
class Index extends Backend
|
||||
{
|
||||
/**
|
||||
* apidoc模型对象
|
||||
* @var \plugin\devtool\model\Apidoc
|
||||
*/
|
||||
protected $model;
|
||||
public $hasSoftDel = 1;//是否拥有软删除功能
|
||||
protected $noNeedLogin = ['getMenu'];
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
$this->model = new \plugin\apidoc\model\Apidoc();
|
||||
}
|
||||
|
||||
//添加
|
||||
public function add()
|
||||
{
|
||||
$post = CommonFun::filterPostData($this->request->post());
|
||||
$post['des'] = UploadDomain::delUploadDomain($post['des']);
|
||||
if ($this->model->create($post)) {
|
||||
return $this->success('添加成功', $post);
|
||||
} else {
|
||||
return $this->error('操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
//编辑
|
||||
public function edit()
|
||||
{
|
||||
$id = $this->request->param('id');
|
||||
$info = $this->model->find($id);
|
||||
$post = CommonFun::filterPostData($this->request->post());
|
||||
$post['des'] = UploadDomain::delUploadDomain($post['des']);
|
||||
foreach ($post as $k => $v) {
|
||||
$info->$k = $v;
|
||||
}
|
||||
try {
|
||||
$updateRes = $info->save();
|
||||
if ($updateRes) {
|
||||
return $this->success('编辑成功');
|
||||
} else if ($updateRes === 0) {
|
||||
return $this->success('未做修改');
|
||||
} else if ($updateRes === null) {
|
||||
return $this->error('操作失败');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->error('数据库异常,操作失败');
|
||||
}
|
||||
}
|
||||
|
||||
//生成常规CURD
|
||||
public function create()
|
||||
{
|
||||
$api = new \plugin\apidoc\library\Apidoc();
|
||||
if ($api->execute('api文档')) {
|
||||
return $this->success('生成成功');
|
||||
} else {
|
||||
return $this->error($api->getError());
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文档菜单接口
|
||||
public function getMenu()
|
||||
{
|
||||
$menuOutputFile = app()->getRootPath() . 'public' . DS . 'static' . DS . 'admin' . DS . 'data' . DS . 'apidocMenu.json';
|
||||
$menuJson = file_get_contents($menuOutputFile);
|
||||
$menu = json_decode($menuJson, true);
|
||||
return $this->success('生成成功', $menu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
|
||||
class PluginApiDoc extends Migrator
|
||||
{
|
||||
public function change()
|
||||
{
|
||||
$table = $this->table('plugin_apidoc', [
|
||||
'engine' => 'InnoDB',
|
||||
'comment' => 'Api文档',
|
||||
'collation' => 'utf8mb4_general_ci',
|
||||
]);
|
||||
|
||||
//删除表
|
||||
if ($table->exists()) {
|
||||
$table->drop();
|
||||
}
|
||||
|
||||
$table
|
||||
->addColumn('title', 'string', ['limit' => 255, 'default' => '', 'comment' => '标题'])
|
||||
->addColumn('des', 'text', ['comment' => '描述'])
|
||||
->addColumn('create_time', 'datetime', ['null' => 1, 'comment' => '创建时间'])
|
||||
->addColumn('update_time', 'datetime', ['null' => 1, 'comment' => '更新时间'])
|
||||
->addColumn('delete_time', 'datetime', ['null' => 1, 'comment' => '删除时间'])
|
||||
;
|
||||
|
||||
$data = [
|
||||
[
|
||||
'title' => '文档更新',
|
||||
'des' => '本文档分为两个部分,`全局说明文档`和`其他文档`。
|
||||
|
||||
# 全局说明文档
|
||||
全局说明下的文档在后台生成Api文档菜单中进行添加,添加完成后点击生成Api文档按钮即可在本文档中看见
|
||||
|
||||
# 其他文档
|
||||
其他文档使用PHP程序,通过PHP的类反射机制,获取到`app/controller/api/`目录下所有文件的注解来进行生成。
|
||||
|
||||
注解规则请查阅`laytp.com`官网手册或者框架`app/controller/api/Demo.php`文件。
|
||||
|
||||
如果后端程序员更新了`api`接口程序,并且使用了相应的注解规则,在后台点击`生成Api文档`按钮即可在本文档看到最新的Api文档
|
||||
|
||||
# 注意点
|
||||
由于文档使用的是静态html展示,而浏览器对html页面是有缓存的。所以如果文档进行了更新。可能需要强制刷新页面才能看到最新的文档。',
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'title' => '在线测试功能使用',
|
||||
'des' => '本文档使用`Laytp极速后台开发框架 - 生成Api文档插件`进行生成。
|
||||
如果需要使用本文档的在线测试功能,需要先进行Api文档配置。
|
||||
|
||||
### Api文档配置
|
||||
点击本文档右上角配置图标会弹出配置层。
|
||||
配置项包括:Api请求域名、签名开关、Header参数。
|
||||
|
||||
#### Api请求域名
|
||||
后台使用单域名部署模式,则无需修改,使用默认值即可。
|
||||
后台使用多域名部署模式,则后端程序员需要提供请求Api的域名地址。使用者不要以/结尾,将请求Api的域名地址填入此处
|
||||
|
||||
#### 签名开关
|
||||
请求Api有签名中间件,签名中间件是否启用要根据后台[系统配置 - 基础配置 - Api签名开关]是否开启的配置来决定。如果后台配置[Api签名开关]开启了,此处也要开启。此处开启后,使用本文档的在线测试功能时,在ajax请求头部会自动添加request-time和sign两个参数。至于签名具体如何生成,请查阅[签名相关]章节说明
|
||||
|
||||
#### Header参数
|
||||
此处填入后端程序员自定义的请求头部参数。框架自定义的请求头部参数有用户登录凭证token,请求时间戳request-time,签名sign三个。一个复杂的系统,一般还会在Header头部定义一些公用参数。比如平台标识,代理标识等等。这些公用参数根据需求不同由后端程序员定义,在使用本文档时,自行在此处进行添加
|
||||
|
||||
### 配置保存
|
||||
点击保存按钮,或者点击页面阴影部分遮罩层,配置都会进行保存
|
||||
|
||||
### 配置持久化
|
||||
Api文档配置使用的是localStorage存储在浏览器端进行持久化的。无需担心页面关闭后,配置不存在的问题',
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'title' => '统一说明',
|
||||
'des' => '# 请求方式
|
||||
默认使用POST方式进行请求
|
||||
|
||||
# 请求头Content-Type
|
||||
请求头的Content-Type定义请求参数的数据类型。ThinkPHP6兼容多种常用请求头方式。比如`application/json`、`application/form-data`和 `application/x-www-form-urlencoded`。
|
||||
|
||||
一般的接口,客户端可以使用`application/json`,自行将参数定义成`json`格式进行参数传递。
|
||||
|
||||
文件上传不支持`json`方式上传文件`Base64`内容,需要使用提交表单方式上传文件。
|
||||
|
||||
# 接口域名
|
||||
- 正式环境
|
||||
由后端程序员提供
|
||||
|
||||
- 测试环境
|
||||
由后端程序员提供
|
||||
|
||||
# 后台地址
|
||||
- 正式环境
|
||||
- 访问地址
|
||||
由后端程序员提供
|
||||
- 账号
|
||||
admin
|
||||
- 密码
|
||||
123456
|
||||
- 测试环境
|
||||
- 访问地址
|
||||
由后端程序员提供
|
||||
- 账号
|
||||
admin
|
||||
- 密码
|
||||
123456',
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'title' => '签名相关',
|
||||
'des' => '客户端请求Api接口程序,后端有签名中间件对请求进行拦截。
|
||||
|
||||
签名中间件是否启用要根据后台[系统配置 - 基础配置 - Api签名开关]是否开启的配置来决定。
|
||||
|
||||
如果后台配置[Api签名开关]开启了,那么客户端在请求Api接口时,需要在Header部分传递两个参数`request-time`和`sign`。
|
||||
|
||||
`request-time`的值由客户端自行定义为当前Unix时间戳。
|
||||
|
||||
`sign`的值由签名生成规则计算生成。
|
||||
|
||||
# 签名生成规则
|
||||
- 客户端自行定义request-time的值为当前Unix时间戳,并经过`md5`运算得到`stringA`
|
||||
|
||||
- 后台系统配置 - 基础配置 - Api签名Key的值为生成签名的Key,并经过`md5`运算得到`stringB`
|
||||
|
||||
- 连接`stringA`和`stringB`后经过`md5`运算得到`stringC`
|
||||
|
||||
- 最后将`stringC`全部转成大写即是客户端需要在请求头部分传递的`sign`值',
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'title' => '接口返回',
|
||||
'des' => '接口统一返回`json`格式的数据。
|
||||
|
||||
# 返回说明
|
||||
|
||||
|参数名|必然存在|类型|说明|
|
||||
|:---- |:---|:----- |----- |
|
||||
|code |是 |integer |接口返回码.0=常规正确码,表示常规操作成功;1=常规错误码,客户端仅需提示message;其他返回码与具体业务相关。框架实现了的唯一其他返回码:10401,前端需要跳转至登录界面。在一个复杂的交互过程中,你可能需要自行定义其他返回码|
|
||||
|msg |是 |string | 接口返回文字描述 |
|
||||
|time |是 |integer | 接口返回时间戳,单位秒 |
|
||||
|data |是 |object/array | 附加数据。单条数据是对象,多条数据是数组。当为空时,会返回一个空对象{}|
|
||||
|
||||
# 返回示例
|
||||
```
|
||||
{
|
||||
"code":1,
|
||||
"msg":"签名错误",
|
||||
"time":1613628412,
|
||||
"data":{}
|
||||
}
|
||||
```',
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
],
|
||||
];
|
||||
|
||||
$table->setData($data)->create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
name = apidoc
|
||||
title = 生成Api文档
|
||||
description = 根据Api接口注释,生成Api文档。支持签名验证,在线测试。
|
||||
version = 1.0.3
|
||||
author = Laytp官方
|
||||
lt_version = 3.0.0
|
||||
parent_menu = first
|
||||
menu_ids = 102,103,104,105,106,107,108,109,110,111
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\apidoc\library;
|
||||
|
||||
use app\service\ConfServiceFacade;
|
||||
use laytp\traits\Error;
|
||||
use plugin\apidoc\library\apidoc\library\Builder;
|
||||
use plugin\apidoc\library\apidoc\library\Extractor;
|
||||
use laytp\library\DirFile;
|
||||
use think\facade\Env;
|
||||
use think\facade\View;
|
||||
|
||||
class Apidoc
|
||||
{
|
||||
use Error;
|
||||
protected $template = 'index.html';
|
||||
protected $output = 'api.html';
|
||||
protected $force = true;
|
||||
|
||||
/**
|
||||
* 执行生成程序
|
||||
* @param $title
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function execute($title)
|
||||
{
|
||||
// $addon = $input->getOption('addon');
|
||||
// if($addon){
|
||||
// $addon_service = new Addons();
|
||||
// $addon_info = $addon_service->_info->getPluginInfo($addon);
|
||||
// $controllerDir = Env::get('root_path') . DS . 'addons' . DS . $addon . DS . $addon_info['api_module'] . DS;
|
||||
// }else{
|
||||
$controllerDir = app()->getRootPath() . DS . 'app' . DS . 'controller' . DS . 'api' . DS;
|
||||
// }
|
||||
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
$weighs = [];
|
||||
$k = 0;
|
||||
foreach ($files as $name => $file) {
|
||||
if (!$file->isDir()) {
|
||||
$filePath = $file->getRealPath();
|
||||
$className = $this->getClassFromFile($filePath);
|
||||
$classAnnotations = Extractor::getClassAnnotations($className);
|
||||
if (isset($classAnnotations['ApiInternal'])) {
|
||||
continue;
|
||||
}
|
||||
$weigh = isset($classAnnotations['ApiWeigh']) ? intval($classAnnotations['ApiWeigh'][0]) : $k;
|
||||
$weighs[$this->getClassFromFile($filePath)] = $weigh;
|
||||
$k++;
|
||||
}
|
||||
}
|
||||
uasort($weighs, function ($a, $b) {
|
||||
if ($a == $b) return 0;
|
||||
return ($a > $b) ? -1 : 1;
|
||||
});
|
||||
|
||||
$classes = array_flip($weighs);
|
||||
|
||||
$builder = new Builder($classes);
|
||||
$apiDir = __DIR__ . DS . 'apidoc' . DS;
|
||||
$templateDir = $apiDir . 'template' . DS;
|
||||
$templateFile = $templateDir . $this->template;
|
||||
$var['plugin'] = '';
|
||||
$var['title'] = $title;
|
||||
$var['apiDomain'] = Env::get('domain.api');
|
||||
$var['createSignKey'] = ConfServiceFacade::get('system.basic.signKey');
|
||||
$var['apidocList'] = \plugin\apidoc\model\Apidoc::select()->toArray();
|
||||
// $content = $builder->render($templateFile, $var);
|
||||
$docslist = $builder->render($templateFile, $var);
|
||||
$menu = [];
|
||||
$menu[] = [
|
||||
'id' => 0,
|
||||
'title' => '全局说明',
|
||||
'docType' => 'meditor'
|
||||
];
|
||||
$key = 0;
|
||||
foreach($var['apidocList'] as $k=>$v){
|
||||
$key++;
|
||||
$temp = [];
|
||||
$temp['id'] = $key;
|
||||
$temp['title'] = $v['title'];
|
||||
$temp['docType'] = 'meditor';
|
||||
$menu[0]['children'][] = $temp;
|
||||
}
|
||||
foreach($docslist as $k=>$v){
|
||||
$key++;
|
||||
$temp = [];
|
||||
$temp['id'] = $key;
|
||||
$temp['title'] = $k;
|
||||
$temp['docType'] = 'api';
|
||||
foreach($v as $api){
|
||||
$key++;
|
||||
$children = [];
|
||||
$children['id'] = $key;
|
||||
$children['title'] = $api['title'];
|
||||
$children['docType'] = 'api';
|
||||
$temp['children'][] = $children;
|
||||
}
|
||||
$menu[] = $temp;
|
||||
}
|
||||
$menuOutputFile = app()->getRootPath() . 'public' . DS . 'static' . DS . 'admin' . DS . 'data' . DS . 'apidocMenu.json';
|
||||
file_put_contents($menuOutputFile, json_encode($menu, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
View::engine()->layout(false);
|
||||
$content = View::display(file_get_contents($templateFile), array_merge($var, ['docslist' => $docslist]));
|
||||
$outputDir = app()->getRootPath() . DS . 'public' . DS;
|
||||
$outputFile = $outputDir . $this->output;
|
||||
DirFile::createDir(dirname($outputFile));
|
||||
if (!file_put_contents($outputFile, $content)) {
|
||||
$this->setError('Cannot save the content to ' . $outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get full qualified class name
|
||||
*
|
||||
* @param string $path_to_file
|
||||
* @return string
|
||||
* @author JBYRNE http://jarretbyrne.com/2015/06/197/
|
||||
*/
|
||||
protected function getClassFromFile($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];
|
||||
} else if ($token === ';') {
|
||||
|
||||
//If the token is the semicolon, then we're done with the namespace declaration
|
||||
$getting_namespace = false;
|
||||
}
|
||||
}
|
||||
|
||||
//While we're grabbing the class name...
|
||||
if ($getting_class === true) {
|
||||
|
||||
//If the token is a string, it's the name of the class
|
||||
if (is_array($token) && $token[0] == T_STRING) {
|
||||
|
||||
//Store the token's value as the class name
|
||||
$class = $token[1];
|
||||
|
||||
//Got what we need, stope here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Build the fully-qualified class name and return it
|
||||
return $namespace ? $namespace . '\\' . $class : $class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\apidoc\library\apidoc\library;
|
||||
|
||||
use think\facade\View;
|
||||
|
||||
/**
|
||||
* @website https://github.com/calinrada/php-apidoc
|
||||
* @author Calin Rada <rada.calin@gmail.com>
|
||||
* @author Karson <karsonzhang@163.com>
|
||||
*/
|
||||
class Builder
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \think\View
|
||||
*/
|
||||
public $view = null;
|
||||
|
||||
/**
|
||||
* parse classes
|
||||
* @var array
|
||||
*/
|
||||
protected $classes = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $classes
|
||||
*/
|
||||
public function __construct($classes = [])
|
||||
{
|
||||
$this->classes = array_merge($this->classes, $classes);
|
||||
}
|
||||
|
||||
protected function extractAnnotations()
|
||||
{
|
||||
$st_output = [];
|
||||
foreach ($this->classes as $class) {
|
||||
$st_output[] = Extractor::getAllClassAnnotations($class);
|
||||
}
|
||||
return end($st_output);
|
||||
}
|
||||
|
||||
protected function generateHeadersTemplate($docs)
|
||||
{
|
||||
if (!isset($docs['ApiHeaders'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$headerslist = [];
|
||||
foreach ($docs['ApiHeaders'] as $params) {
|
||||
$tr = [
|
||||
'name' => $params['name'],
|
||||
'type' => $params['type'],
|
||||
'sample' => isset($params['sample']) ? $params['sample'] : '',
|
||||
'required' => isset($params['required']) ? $params['required'] : false,
|
||||
'description' => isset($params['description']) ? $params['description'] : '',
|
||||
];
|
||||
$headerslist[] = $tr;
|
||||
}
|
||||
|
||||
return $headerslist;
|
||||
}
|
||||
|
||||
protected function generateParamsTemplate($docs)
|
||||
{
|
||||
if (!isset($docs['ApiParams'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$paramslist = [];
|
||||
foreach ($docs['ApiParams'] as $params) {
|
||||
$tr = [
|
||||
'name' => $params['name'],
|
||||
'type' => isset($params['type']) ? $params['type'] : 'string',
|
||||
'sample' => isset($params['sample']) ? $params['sample'] : '',
|
||||
'required' => isset($params['required']) ? $params['required'] : true,
|
||||
'description' => isset($params['description']) ? $params['description'] : '',
|
||||
];
|
||||
$paramslist[] = $tr;
|
||||
}
|
||||
|
||||
return $paramslist;
|
||||
}
|
||||
|
||||
protected function generateReturnHeadersTemplate($docs)
|
||||
{
|
||||
if (!isset($docs['ApiReturnHeaders'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$headerslist = [];
|
||||
foreach ($docs['ApiReturnHeaders'] as $params) {
|
||||
$tr = [
|
||||
'name' => $params['name'],
|
||||
'type' => 'string',
|
||||
'sample' => isset($params['sample']) ? $params['sample'] : '',
|
||||
'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No',
|
||||
'description' => isset($params['description']) ? $params['description'] : '',
|
||||
];
|
||||
$headerslist[] = $tr;
|
||||
}
|
||||
|
||||
return $headerslist;
|
||||
}
|
||||
|
||||
protected function generateReturnParamsTemplate($st_params)
|
||||
{
|
||||
if (!isset($st_params['ApiReturnParams'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$paramslist = [];
|
||||
foreach ($st_params['ApiReturnParams'] as $params) {
|
||||
$tr = [
|
||||
'name' => $params['name'],
|
||||
'type' => isset($params['type']) ? $params['type'] : 'string',
|
||||
'sample' => isset($params['sample']) ? $params['sample'] : '',
|
||||
'description' => isset($params['description']) ? $params['description'] : '',
|
||||
];
|
||||
$paramslist[] = $tr;
|
||||
}
|
||||
|
||||
return $paramslist;
|
||||
}
|
||||
|
||||
protected function generateBadgeForMethod($data)
|
||||
{
|
||||
$method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]);
|
||||
$labes = [
|
||||
'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()
|
||||
{
|
||||
$annotations = $this->extractAnnotations();
|
||||
|
||||
$counter = 0;
|
||||
$section = null;
|
||||
$docslist = [];
|
||||
foreach ($annotations 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;
|
||||
}
|
||||
|
||||
$docslist[$section][] = [
|
||||
'id' => $counter,
|
||||
'title' => $docs['ApiTitle'][0],
|
||||
'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0],
|
||||
'method_label' => $this->generateBadgeForMethod($docs),
|
||||
'section' => $section,
|
||||
'route' => is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][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),
|
||||
'return' => isset($docs['ApiReturn']) ? is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0] : '',
|
||||
];
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
return $docslist;
|
||||
}
|
||||
|
||||
public function getView()
|
||||
{
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
* @param $template
|
||||
* @param array $vars
|
||||
* @return array|string
|
||||
*/
|
||||
public function render($template, $vars = [])
|
||||
{
|
||||
$docslist = $this->parse();
|
||||
$ids = [];
|
||||
foreach ($docslist as $v) {
|
||||
foreach ($v as $api) {
|
||||
$ids[] = $api['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $docslist;
|
||||
|
||||
return View::display(file_get_contents($template), array_merge($vars, ['docslist' => $docslist, 'ids' => json_encode($ids)]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\apidoc\library\apidoc\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;
|
||||
|
||||
/**
|
||||
* 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::$annotationCache all annotated elements
|
||||
*/
|
||||
public static function getClassAnnotations($className)
|
||||
{
|
||||
if (!isset(self::$annotationCache[$className])) {
|
||||
$class = new \ReflectionClass($className);
|
||||
self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
|
||||
}
|
||||
|
||||
return self::$annotationCache[$className];
|
||||
}
|
||||
|
||||
public static function getAllClassAnnotations($className)
|
||||
{
|
||||
$class = new \ReflectionClass($className);
|
||||
|
||||
foreach ($class->getMethods() as $object) {
|
||||
self::$annotationCache['annotations'][$className][$object->name] = self::getMethodAnnotations($className, $object->name);
|
||||
}
|
||||
|
||||
return self::$annotationCache['annotations'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ($className == $method->class) {
|
||||
if (!$method->isPublic() || $method->isConstructor()) {
|
||||
$annotations = [];
|
||||
} else {
|
||||
$annotations = self::consolidateAnnotations($method, $class);
|
||||
}
|
||||
} else {
|
||||
$annotations = [];
|
||||
}
|
||||
} catch (\ReflectionException $e) {
|
||||
$annotations = [];
|
||||
}
|
||||
|
||||
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 = [];
|
||||
|
||||
$i = 0;
|
||||
|
||||
foreach ($annotations as $annotationClass => $listParams) {
|
||||
$annotationClass = ucfirst($annotationClass);
|
||||
$class = $this->defaultNamespace . $annotationClass . 'Annotation';
|
||||
|
||||
// verify is the annotation class exists, depending if Annotations::strict is true
|
||||
// if not, just skip the annotation instance creation.
|
||||
if (!class_exists($class)) {
|
||||
if ($this->strict) {
|
||||
throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class));
|
||||
} else {
|
||||
// silent skip & continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($objects[$annotationClass])) {
|
||||
$objects[$annotationClass] = new $class();
|
||||
}
|
||||
|
||||
foreach ($listParams as $params) {
|
||||
if (is_array($params)) {
|
||||
foreach ($params as $key => $value) {
|
||||
$objects[$annotationClass]->set($key, $value);
|
||||
}
|
||||
} else {
|
||||
$objects[$annotationClass]->set($i++, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
private static function consolidateAnnotations($method, $class)
|
||||
{
|
||||
$dockblockClass = $class->getDocComment();
|
||||
$docblockMethod = $method->getDocComment();
|
||||
$methodName = $method->getName();
|
||||
|
||||
$methodAnnotations = self::parseAnnotations($docblockMethod);
|
||||
$classAnnotations = self::parseAnnotations($dockblockClass);
|
||||
if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$properties = $class->getDefaultProperties();
|
||||
$noNeedLogin = isset($properties['noNeedLogin']) ? is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']] : [];
|
||||
$noNeedRight = isset($properties['noNeedRight']) ? is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']] : [];
|
||||
|
||||
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr);
|
||||
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr);
|
||||
|
||||
$methodTitle = isset($methodArr[1]) && isset($methodArr[1][0]) ? $methodArr[1][0] : '';
|
||||
$classTitle = isset($classArr[1]) && isset($classArr[1][0]) ? $classArr[1][0] : '';
|
||||
|
||||
if (!isset($methodAnnotations['ApiMethod'])) {
|
||||
$methodAnnotations['ApiMethod'] = ['get'];
|
||||
}
|
||||
if (!isset($methodAnnotations['ApiSummary'])) {
|
||||
$methodAnnotations['ApiSummary'] = [$methodTitle];
|
||||
}
|
||||
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['ApiTitle'])) {
|
||||
$methodAnnotations['ApiTitle'] = [$methodTitle];
|
||||
}
|
||||
if (!isset($methodAnnotations['ApiRoute'])) {
|
||||
$urlArr = [];
|
||||
$className = $class->getName();
|
||||
|
||||
list($prefix, $suffix) = explode('\\controller\\', $className);
|
||||
$prefixArr = explode('\\', $prefix);
|
||||
$suffixArr = explode('\\', $suffix);
|
||||
if ($prefixArr[0] == 'app') {
|
||||
$prefixArr[0] = '';
|
||||
} else if ($prefixArr[0] == 'plugin') {
|
||||
$tempPrefixArr = [];
|
||||
$tempPrefixArr[0] = '';
|
||||
$tempPrefixArr[1] = $prefixArr[2];
|
||||
$prefixArr = $tempPrefixArr;
|
||||
}
|
||||
$urlArr = array_merge($urlArr, $prefixArr);
|
||||
$urlArr[] = implode('.', array_map(function ($item) {
|
||||
return self::parseName($item);
|
||||
}, $suffixArr));
|
||||
$urlArr[] = $method->getName();
|
||||
$methodAnnotations['ApiRoute'] = [implode('/', $urlArr)];
|
||||
}
|
||||
if (!isset($methodAnnotations['ApiSector'])) {
|
||||
$methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : [$classTitle];
|
||||
}
|
||||
if (!isset($methodAnnotations['ApiParams'])) {
|
||||
$params = self::parseCustomAnnotations($docblockMethod, 'param');
|
||||
foreach ($params as $k => $v) {
|
||||
$arr = explode(' ', preg_replace("/[\s]+/", " ", $v));
|
||||
$methodAnnotations['ApiParams'][] = [
|
||||
'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '',
|
||||
'nullable' => false,
|
||||
'type' => isset($arr[0]) ? $arr[0] : 'string',
|
||||
'description' => isset($arr[2]) ? $arr[2] : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
$methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)];
|
||||
$methodAnnotations['ApiPermissionRight'] = [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)];
|
||||
return $methodAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串命名风格转换
|
||||
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
|
||||
* @access public
|
||||
* @param string $name 字符串
|
||||
* @param integer $type 转换类型
|
||||
* @param bool $ucfirst 首字母是否大写(驼峰规则)
|
||||
* @return string
|
||||
*/
|
||||
private static function parseName($name, $type = 0, $ucfirst = true)
|
||||
{
|
||||
if ($type) {
|
||||
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
|
||||
return strtoupper($match[1]);
|
||||
}, $name);
|
||||
return $ucfirst ? ucfirst($name) : lcfirst($name);
|
||||
}
|
||||
|
||||
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse annotations
|
||||
*
|
||||
* @param string $docblock
|
||||
* @param string $name
|
||||
* @return array parsed annotations params
|
||||
*/
|
||||
private static function parseCustomAnnotations($docblock, $name = 'param')
|
||||
{
|
||||
$annotations = [];
|
||||
|
||||
$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 = [];
|
||||
|
||||
// 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) {
|
||||
// annotations has arguments
|
||||
if (isset($matches['args'][$i])) {
|
||||
$argsParts = trim($matches['args'][$i]);
|
||||
$name = $matches['name'][$i];
|
||||
if ($name == 'ApiReturn') {
|
||||
$value = $argsParts;
|
||||
} else {
|
||||
$argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts);
|
||||
$value = self::parseArgs($argsParts);
|
||||
if (is_string($value)) {
|
||||
$value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$value = [];
|
||||
}
|
||||
|
||||
$annotations[$name][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
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 = [];
|
||||
$len = strlen($content);
|
||||
$i = 0;
|
||||
$var = '';
|
||||
$val = '';
|
||||
$level = 1;
|
||||
|
||||
$prevDelimiter = '';
|
||||
$nextDelimiter = '';
|
||||
$nextToken = '';
|
||||
$composing = false;
|
||||
$type = 'plain';
|
||||
$delimiter = null;
|
||||
$quoted = false;
|
||||
$tokens = ['"', '"', '{', '}', ',', '='];
|
||||
|
||||
while ($i <= $len) {
|
||||
$prev_c = substr($content, $i - 1, 1);
|
||||
$c = substr($content, $i++, 1);
|
||||
|
||||
if ($c === '"' && $prev_c !== "\\") {
|
||||
$delimiter = $c;
|
||||
//open delimiter
|
||||
if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) {
|
||||
$prevDelimiter = $nextDelimiter = $delimiter;
|
||||
$val = '';
|
||||
$composing = true;
|
||||
$quoted = true;
|
||||
} else {
|
||||
// close delimiter
|
||||
if ($c !== $nextDelimiter) {
|
||||
throw new Exception(sprintf(
|
||||
"Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c
|
||||
));
|
||||
}
|
||||
|
||||
// validating syntax
|
||||
if ($i < $len) {
|
||||
if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) {
|
||||
throw new Exception(sprintf(
|
||||
"Parse Error: missing comma separator near: ...%s<--", substr($content, ($i - 10), $i)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$prevDelimiter = $nextDelimiter = '';
|
||||
$composing = false;
|
||||
$delimiter = null;
|
||||
}
|
||||
} elseif (!$composing && in_array($c, $tokens)) {
|
||||
switch ($c) {
|
||||
case '=':
|
||||
$prevDelimiter = $nextDelimiter = '';
|
||||
$level = 2;
|
||||
$composing = false;
|
||||
$type = 'assoc';
|
||||
$quoted = false;
|
||||
break;
|
||||
case ',':
|
||||
$level = 3;
|
||||
|
||||
// If composing flag is true yet,
|
||||
// it means that the string was not enclosed, so it is parsing error.
|
||||
if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) {
|
||||
throw new Exception(sprintf(
|
||||
"Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c
|
||||
));
|
||||
}
|
||||
|
||||
$prevDelimiter = $nextDelimiter = '';
|
||||
break;
|
||||
case '{':
|
||||
$subc = '';
|
||||
$subComposing = true;
|
||||
|
||||
while ($i <= $len) {
|
||||
$c = substr($content, $i++, 1);
|
||||
|
||||
if (isset($delimiter) && $c === $delimiter) {
|
||||
throw new Exception(sprintf(
|
||||
"Parse Error: Composite variable is not enclosed correctly."
|
||||
));
|
||||
}
|
||||
|
||||
if ($c === '}') {
|
||||
$subComposing = false;
|
||||
break;
|
||||
}
|
||||
$subc .= $c;
|
||||
}
|
||||
|
||||
// if the string is composing yet means that the structure of var. never was enclosed with '}'
|
||||
if ($subComposing) {
|
||||
throw new Exception(sprintf(
|
||||
"Parse Error: Composite variable is not enclosed correctly. near: ...%s'", $subc
|
||||
));
|
||||
}
|
||||
|
||||
$val = self::parseArgs($subc);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ($level == 1) {
|
||||
$var .= $c;
|
||||
} elseif ($level == 2) {
|
||||
$val .= $c;
|
||||
}
|
||||
}
|
||||
|
||||
if ($level === 3 || $i === $len) {
|
||||
if ($type == 'plain' && $i === $len) {
|
||||
$data = self::castValue($var);
|
||||
} else {
|
||||
$data[trim($var)] = self::castValue($val, !$quoted);
|
||||
}
|
||||
|
||||
$level = 1;
|
||||
$var = $val = '';
|
||||
$composing = false;
|
||||
$quoted = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try determinate the original type variable of a string
|
||||
*
|
||||
* @param string $val string containing possibles variables that can be cast to bool or int
|
||||
* @param boolean $trim indicate if the value passed should be trimmed after to try cast
|
||||
* @return mixed returns the value converted to original type if was possible
|
||||
*/
|
||||
private static function castValue($val, $trim = false)
|
||||
{
|
||||
if (is_array($val)) {
|
||||
foreach ($val as $key => $value) {
|
||||
$val[$key] = self::castValue($value);
|
||||
}
|
||||
} elseif (is_string($val)) {
|
||||
if ($trim) {
|
||||
$val = trim($val);
|
||||
}
|
||||
$val = stripslashes($val);
|
||||
$tmp = strtolower($val);
|
||||
|
||||
if ($tmp === 'false' || $tmp === 'true') {
|
||||
$val = $tmp === 'true';
|
||||
} elseif (is_numeric($val)) {
|
||||
return $val + 0;
|
||||
}
|
||||
|
||||
unset($tmp);
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Cache" content="no-cache">
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>Api文档</title>
|
||||
<!-- 依 赖 样 式 -->
|
||||
<link rel="stylesheet" href="/static/component/laytp/css/laytp.css" />
|
||||
<!-- 加 载 样 式-->
|
||||
<link rel="stylesheet" href="/static/admin/css/load.css" />
|
||||
<!-- 布 局 样 式 -->
|
||||
<link rel="stylesheet" href="/static/admin/css/apidoc.css" />
|
||||
</head>
|
||||
<style>
|
||||
.api-page-tab-content {
|
||||
background-color: #fff;
|
||||
overflow: auto;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.api-url {
|
||||
/*position: relative;*/
|
||||
color: rgb(221, 17, 68);
|
||||
background: #f6f6f6;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.layui-form-pane .layui-form-label {
|
||||
width: 280px;
|
||||
/*height: 34px;*/
|
||||
padding: 6px 15px;
|
||||
}
|
||||
|
||||
.layui-form-pane .layui-input-block {
|
||||
margin-left: 280px;
|
||||
}
|
||||
|
||||
.editormd-preview-container, .editormd-html-preview {
|
||||
width: 98% !important;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown-body li {
|
||||
/* 使md编辑器中的li取消样式none */
|
||||
list-style: unset;
|
||||
}
|
||||
|
||||
.api-tab-title {
|
||||
position: fixed;
|
||||
left: 230px;
|
||||
right: 0;
|
||||
z-index: 999999;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.layui-tab-bar{
|
||||
display:none;
|
||||
}
|
||||
</style>
|
||||
<!-- 结 构 代 码 -->
|
||||
<body class="layui-layout-body laytp-admin">
|
||||
<!-- 布 局 框 架 -->
|
||||
<div class="layui-layout layui-layout-admin">
|
||||
<div class="layui-header">
|
||||
<!-- 顶 部 左 侧 功 能 -->
|
||||
<ul class="layui-nav layui-layout-left">
|
||||
<li class="collaspe layui-nav-item"><a href="javascript:void(0);" class="layui-icon layui-icon-shrink-right"></a></li>
|
||||
</ul>
|
||||
<!-- 顶 部 右 侧 菜 单 -->
|
||||
<div id="control" class="layui-layout-control"></div>
|
||||
<ul class="layui-nav layui-layout-right">
|
||||
<li class="layui-nav-item layui-hide-xs"><a href="javascript:void(0);" class="fullScreen layui-icon layui-icon-screen-full"></a></li>
|
||||
<!-- 主 题 配 置 -->
|
||||
<li class="layui-nav-item apiSet"><a href="javascript:void(0);" class="layui-icon layui-icon-set"></a></li>
|
||||
<li class="layui-nav-item setting"><a href="javascript:void(0);" class="layui-icon layui-icon-more-vertical"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 侧 边 区 域 -->
|
||||
<div class="layui-side layui-bg-black">
|
||||
<!-- 菜 单 顶 部 -->
|
||||
<div class="layui-logo">
|
||||
<!-- 图 标 -->
|
||||
<img class="logo" />
|
||||
<!-- 标 题 -->
|
||||
<span class="title"></span>
|
||||
</div>
|
||||
<!-- 菜 单 内 容 -->
|
||||
<div>
|
||||
<ul class="layui-nav arrow layui-nav-tree laytp-nav-tree" style="display: block;">
|
||||
<li class="layui-nav-item">
|
||||
<div class="search" style="padding: 5px;">
|
||||
<input type="text" class="layui-input search-menu" placeholder="搜索文档">
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="layui-side-scroll">
|
||||
<div id="sideMenu"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 视 图 页 面 -->
|
||||
<div class="layui-body">
|
||||
{php}$key = 0;{/php}
|
||||
{foreach $apidocList as $k=>$v}
|
||||
{php}$key++;{/php}
|
||||
<div class="api-page-tab-content" id="meditor_{$key}" style="display:none;">
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title api-tab-title">
|
||||
<li class="layui-this">
|
||||
{$v.title}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content meditorContent api-content" id="meditor_{$v.id}_content">
|
||||
<textarea style="display:none;">{$v.des}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
{foreach $docslist as $k=>$v}
|
||||
{php}$key++;{/php}
|
||||
{foreach $v as $ak=>$api}
|
||||
{php}$key++;{/php}
|
||||
<div class="api-page-tab-content" id="api_{$key}" style="display:none;">
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title api-tab-title">
|
||||
<li class="layui-this">接口说明</li>
|
||||
<li>在线测试</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content markdown-body api-content">
|
||||
<div class="layui-tab-item layui-show editormd-html-preview">
|
||||
<p><strong>简要说明</strong></p>
|
||||
<ul>
|
||||
<li>{$api.summary}</li>
|
||||
</ul>
|
||||
<p><strong>请求方式</strong></p>
|
||||
<ul>
|
||||
<li><code class="api-url">{$api.method}</code></li>
|
||||
</ul>
|
||||
<p><strong>请求地址</strong></p>
|
||||
<ul>
|
||||
<li><code class="api-url" route="{if $apiDomain && substr($api['route'],0,5) === '/api.'}/{:substr($api['route'],5)}{else /}{$api.route}{/if}">{if $apiDomain && substr($api['route'],0,5) === '/api.'}/{:substr($api['route'],5)}{else /}{$api.route}{/if}</code></li>
|
||||
</ul>
|
||||
<p><strong>Headers参数</strong></p>
|
||||
{if $api.headerslist}
|
||||
<div style="width: 100%;overflow-x: auto;">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: left;">名称</th>
|
||||
<th style="text-align: left;">类型</th>
|
||||
<th style="text-align: left;">必选</th>
|
||||
<th style="text-align: left;">描述</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $api.headerslist as $hk=>$hv}
|
||||
<tr>
|
||||
<td style="text-align:left">{$hv.name}</td>
|
||||
<td style="text-align:left">{$hv.type}</td>
|
||||
<td style="text-align:left">{if $hv.required}是{else}否{/if}</td>
|
||||
<td>{$hv.description}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{else}
|
||||
<ul>
|
||||
<li>无</li>
|
||||
</ul>
|
||||
{/if}
|
||||
<p><strong>Body参数</strong></p>
|
||||
{if $api.paramslist}
|
||||
<div style="width: 100%;overflow-x: auto;">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: left;">名称</th>
|
||||
<th style="text-align: left;">类型</th>
|
||||
<th style="text-align: left;">必选</th>
|
||||
<th style="text-align: left;">描述</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $api.paramslist as $pk=>$pv}
|
||||
<tr>
|
||||
<td style="text-align:left">{$pv.name}</td>
|
||||
<td style="text-align:left">{$pv.type}</td>
|
||||
<td style="text-align:left">{if $pv.required}是{else}否{/if}</td>
|
||||
<td>{if $pv.sample}{$pv.description} -
|
||||
例:{$pv.sample}{else}{$pv.description}{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{else}
|
||||
<ul>
|
||||
<li>无</li>
|
||||
</ul>
|
||||
{/if}
|
||||
<p><strong>返回示例</strong></p>
|
||||
{if $api.return}
|
||||
<pre><code class="json">{$api.return}</code></pre>
|
||||
{else}
|
||||
<ul>
|
||||
<li>无</li>
|
||||
</ul>
|
||||
{/if}
|
||||
<p><strong>返回说明</strong></p>
|
||||
{if $api.returnparamslist}
|
||||
<div style="width: 100%;overflow-x: auto;">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: left;">名称</th>
|
||||
<th style="text-align: left;">类型</th>
|
||||
<th style="text-align: left;">描述</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $api.returnparamslist as $pk=>$pv}
|
||||
<tr>
|
||||
<td style="text-align:left">{$pv.name}</td>
|
||||
<td style="text-align:left">{$pv.type}</td>
|
||||
<td style="text-align:left">{$pv.description}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{else}
|
||||
<ul>
|
||||
<li>无</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="layui-tab-item editormd-html-preview">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header" style="background-color: #F2F2F2;">Body传参</div>
|
||||
<div class="layui-card-body main-container">
|
||||
<form class="layui-form layui-form-pane" method="{$api.method}"
|
||||
route="{if $apiDomain && substr($api['route'],0,5) === '/api.'}/{:substr($api['route'],5)}{else /}{$api.route}{/if}"
|
||||
lay-filter="test_inline_{$key}" id="test_inline_{$key}">
|
||||
{foreach $api.paramslist as $pk=>$pv}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">{$pv.name}</label>
|
||||
<div class="layui-input-block">
|
||||
{if($pv.type == 'array')}
|
||||
<table class="layui-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="right" style="width:400px">
|
||||
<input type="text" class="layui-input" name="{$pv.name}[]" autocomplete="off" placeholder="{if $pv.sample}{$pv.description} - 例:{$pv.sample}{else}请输入{$pv.description}{/if}" />
|
||||
</td>
|
||||
<td>
|
||||
<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-delete del-array-param"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-add-1 add-array-param" data-field="{$pv.name}">追加参数</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{else}
|
||||
<input type="{if ($pv.type == 'file')}file{else}text{/if}"
|
||||
name="{$pv.name}" autocomplete="off"
|
||||
placeholder="{if $pv.sample}{$pv.description} - 例:{$pv.sample}{else}请输入{$pv.description}{/if}"
|
||||
class="layui-input">
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button type="button" class="laytp-btn laytp-btn-primary"
|
||||
href="javascript:void(0);"
|
||||
onclick="api.testInline({$key})">提 交
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card" id="response_card_{$key}">
|
||||
<div class="layui-card-header" style="background-color: #F2F2F2;">响应输出</div>
|
||||
<div class="layui-card-body">
|
||||
<pre><code class="json" id="response_{$key}"></code></pre>
|
||||
<pre class="layui-code" id="response_headers_{$key}"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
{/foreach}
|
||||
</div>
|
||||
<!-- 遮 盖 层 -->
|
||||
<div class="laytp-cover"></div>
|
||||
<!-- 加 载 动 画-->
|
||||
<div class="loader-main">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 移 动 端 便 捷 操 作 -->
|
||||
<div class="laytp-collasped-pe collaspe">
|
||||
<a href="javascript:void(0);" class="layui-icon layui-icon-shrink-right"></a>
|
||||
</div>
|
||||
|
||||
<!-- 请求配置弹出层内容开始 -->
|
||||
<script type="text/html" id="set">
|
||||
<div class="popup-content">
|
||||
<div class="api-page-tab-content">
|
||||
<form class="layui-form" lay-filter="setDataForm" id="setDataForm">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">Api请求域名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="apiUrl"
|
||||
placeholder="完整的请求前缀,以http://或者https://开头,不以/结尾" autocomplete="off" class="layui-input"
|
||||
value="{{d.apiUrl}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" title="开关">签名验证</label>
|
||||
<div class="layui-input-block">
|
||||
<!-- 隐藏域设置开关未选中时需要传递的参数值 -->
|
||||
<input type="hidden" name="createSign" id="createSign-2" value="2"/>
|
||||
<input type="checkbox" name="createSign" id="createSign-1" lay-skin="switch" lay-text="验证|不验证"
|
||||
{{# if(d.createSign== 1){ }}checked="checked" {{# } }} value="1"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" title="Header参数">Header参数</label>
|
||||
<div class="layui-input-block">
|
||||
<table class="layui-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>参数Key</th>
|
||||
<th>参数Value</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="right">
|
||||
<input type="text" class="layui-input" value="token" readonly="readonly"/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="layui-input" name="token" value="{{d.headerJson.token}}"
|
||||
autocomplete="off"/>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{{# let k; }}
|
||||
{{# for(k in d.headerJson){ }}
|
||||
{{# if(k !== "token" && k !== "sign" && k !== "request-time"){ }}
|
||||
<tr>
|
||||
<td align="right">
|
||||
<input type="text" class="layui-input" name="header[key][]" value="{{k}}"
|
||||
autocomplete="off"/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="layui-input" name="header[value][]"
|
||||
value="{{d.headerJson[k]}}" autocomplete="off"/>
|
||||
</td>
|
||||
<td>
|
||||
<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-delete del-header-param"></a>
|
||||
</td>
|
||||
</tr>
|
||||
{{# } }}
|
||||
{{# } }}
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-add-1 add-header-param">追加参数</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 请求配置弹出层内容结束 -->
|
||||
|
||||
<script type="text/html" id="arrayParam">
|
||||
<table class="layui-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="right">
|
||||
<input type="text" class="layui-input" name="{{d.field}}[]" autocomplete="off"/>
|
||||
</td>
|
||||
<td>
|
||||
<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-delete del-array-param"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-add-1 add-array-param">追加参数</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<!-- 依 赖 脚 本 -->
|
||||
<script type="text/javascript" src="/static/component/jquery_3.3.1.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="/static/component/jquery.serializejson.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="/static/component/highlight/highlight.pack.js" charset="utf-8"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/component/highlight/styles/docco.css"/>
|
||||
|
||||
<link rel="stylesheet" href="/static/plugin/meditor/css/editormd.css"/>
|
||||
<script src="/static/plugin/meditor/lib/marked.min.js"></script>
|
||||
<script src="/static/plugin/meditor/lib/prettify.min.js"></script>
|
||||
<script src="/static/plugin/meditor/lib/flowchart.min.js"></script>
|
||||
<script src="/static/plugin/meditor/lib/raphael.min.js"></script>
|
||||
<script src="/static/plugin/meditor/lib/underscore.min.js"></script>
|
||||
<script src="/static/plugin/meditor/lib/sequence-diagram.min.js"></script>
|
||||
<script src="/static/plugin/meditor/lib/jquery.flowchart.min.js"></script>
|
||||
<script src="/static/plugin/meditor/editormd.min.js"></script>
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.18.0/js/md5.js"></script>
|
||||
|
||||
<script>
|
||||
// 初始化静态文件版本号
|
||||
localStorage.setItem("version","1.0.2.Release");
|
||||
document.write("<script src='/static/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
</script>
|
||||
<!-- 框 架 初 始 化 -->
|
||||
<script>
|
||||
//定义storage
|
||||
let storage = (function () {
|
||||
let storage;
|
||||
try {
|
||||
storage = window.localStorage;
|
||||
return storage;
|
||||
} catch (exception) {
|
||||
return false;
|
||||
}
|
||||
}());
|
||||
layui.use(['laytp','apidoc','popup'], function() {
|
||||
let createSignKey = "{$createSignKey}";
|
||||
let apiDomain = "{$apiDomain}";
|
||||
let apiUrl = (apiDomain.length > 0) ? apiDomain : window.location.protocol + "//" + window.location.host;
|
||||
|
||||
//请求配置
|
||||
let nowSetJson = storage.getItem("setJson") ? JSON.parse(storage.getItem("setJson")) : {
|
||||
apiUrl: apiUrl,
|
||||
createSign: 2,
|
||||
headerJson: {token: ""}
|
||||
};
|
||||
|
||||
//渲染所有的meditor编辑器内容
|
||||
layui.each($(".meditorContent"), function (key, item) {
|
||||
editormd.markdownToHTML($(item).attr('id'), {
|
||||
htmlDecode: "style,script,iframe", // you can filter tags decode
|
||||
emoji: true,
|
||||
taskList: true,
|
||||
tex: true, // 默认不解析
|
||||
flowChart: true, // 默认不解析
|
||||
sequenceDiagram: true, // 默认不解析
|
||||
});
|
||||
});
|
||||
//当表格列数过长时将自动出现滚动条
|
||||
var themeColorContext = layui.context.get('theme-color-context');
|
||||
$.each($('table'), function (i, item) {
|
||||
if($(this).attr('class') !== 'layui-table'){
|
||||
$('thead > tr', this).attr('style', 'background-color: '+themeColorContext+';color:white;');
|
||||
$(this).prop('outerHTML', '<div style="width: 95%;overflow-x: auto;">' + $(this).prop('outerHTML') + '</div>');
|
||||
}
|
||||
});
|
||||
|
||||
//弹窗展示请求配置
|
||||
$(document).off("click", ".apiSet").on("click", ".apiSet", function () {
|
||||
let layerDiv = layui.layer.open({
|
||||
type: 1,
|
||||
offset: 'r',
|
||||
area: ['600px', '100%'],
|
||||
title: "请求配置",
|
||||
content: layui.laytpl($("#set").html()).render(nowSetJson),
|
||||
shade: 0.1,
|
||||
closeBtn: 0,
|
||||
shadeClose: false,
|
||||
anim: -1,
|
||||
skin: 'layer-anim-right',
|
||||
move: false,
|
||||
success: function (layero, index) {
|
||||
$(document).off("click", '#layui-layer-shade' + index).on("click", '#layui-layer-shade' + index, function () {
|
||||
var $layero = $('#layui-layer' + index);
|
||||
$layero.animate({
|
||||
left: $layero.offset().left + $layero.width()
|
||||
}, 200, function() {
|
||||
let formJson = $('#setDataForm').serializeJSON();
|
||||
let headerJson = {
|
||||
token: formJson.token
|
||||
};
|
||||
let keyJson = {};
|
||||
if (formJson.hasOwnProperty("header") && formJson.header.hasOwnProperty("key")) {
|
||||
keyJson = formJson.header.key;
|
||||
}
|
||||
let valueJson = {};
|
||||
if (formJson.hasOwnProperty("header") && formJson.header.hasOwnProperty("value")) {
|
||||
valueJson = formJson.header.value;
|
||||
}
|
||||
|
||||
$.each(keyJson, function (key, item) {
|
||||
if(keyJson[key]){
|
||||
headerJson[keyJson[key]] = valueJson[key];
|
||||
}
|
||||
});
|
||||
|
||||
let setJson = {
|
||||
apiUrl: formJson.apiUrl,
|
||||
createSign: formJson.createSign,
|
||||
headerJson: headerJson
|
||||
};
|
||||
|
||||
nowSetJson = setJson;
|
||||
|
||||
storage.setItem("setJson", JSON.stringify(setJson));
|
||||
|
||||
facade.success("保存成功");
|
||||
|
||||
layer.close(index);
|
||||
});
|
||||
});
|
||||
},
|
||||
btn: ['保 存', '取 消'],
|
||||
yes: function (index, layero) {
|
||||
let formJson = $('#setDataForm').serializeJSON();
|
||||
let headerJson = {
|
||||
token: formJson.token
|
||||
};
|
||||
let keyJson = {};
|
||||
if (formJson.hasOwnProperty("header") && formJson.header.hasOwnProperty("key")) {
|
||||
keyJson = formJson.header.key;
|
||||
}
|
||||
let valueJson = {};
|
||||
if (formJson.hasOwnProperty("header") && formJson.header.hasOwnProperty("value")) {
|
||||
valueJson = formJson.header.value;
|
||||
}
|
||||
|
||||
$.each(keyJson, function (key, item) {
|
||||
if(keyJson[key]){
|
||||
headerJson[keyJson[key]] = valueJson[key];
|
||||
}
|
||||
});
|
||||
|
||||
let setJson = {
|
||||
apiUrl: formJson.apiUrl,
|
||||
createSign: formJson.createSign,
|
||||
headerJson: headerJson
|
||||
};
|
||||
|
||||
nowSetJson = setJson;
|
||||
|
||||
storage.setItem("setJson", JSON.stringify(setJson));
|
||||
|
||||
facade.success("保存成功");
|
||||
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
layui.laytpForm.render("#layui-layer" + layerDiv);
|
||||
});
|
||||
|
||||
//追加Header参数
|
||||
$(document).off("click", ".add-header-param").on("click", ".add-header-param", function () {
|
||||
let clickObj = $(this);
|
||||
let template = '<tr>' +
|
||||
'<td align="right">' +
|
||||
'<input type="text" class="layui-input" name="header[key][]" autocomplete="off" />' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<input type="text" class="layui-input" name="header[value][]" autocomplete="off" />' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-delete del-header-param"></a>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
clickObj.parent().parent().before(template);
|
||||
});
|
||||
|
||||
//追加数组参数
|
||||
$(document).off("click", ".add-array-param").on("click", ".add-array-param", function () {
|
||||
let clickObj = $(this);
|
||||
let field = clickObj.data('field');
|
||||
let template = '<tr>' +
|
||||
'<td align="right">' +
|
||||
'<input type="text" class="layui-input" name="' + field + '[]" autocomplete="off" />' +
|
||||
'</td>' +
|
||||
'<td>' +
|
||||
'<a class="layui-btn layui-btn-primary layui-btn-sm layui-icon layui-icon-delete del-array-param"></a>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
clickObj.parent().parent().before(template);
|
||||
});
|
||||
|
||||
//删除header参数
|
||||
$(document).off("click", ".del-header-param").on("click", ".del-header-param", function () {
|
||||
let clickObj = $(this);
|
||||
clickObj.parent().parent().remove();
|
||||
});
|
||||
|
||||
//删除array参数
|
||||
$(document).off("click", ".del-array-param").on("click", ".del-array-param", function () {
|
||||
let clickObj = $(this);
|
||||
clickObj.parent().parent().remove();
|
||||
});
|
||||
|
||||
layui.apidoc.setConfigType("yml");
|
||||
layui.apidoc.setConfigPath("/static/component/laytp/config/api.config.yml?v=" + localStorage.getItem("version"));
|
||||
layui.apidoc.render();
|
||||
// 搜索菜单功能添加在这里,不要添加到js组件里面,因为会在子页面进行调用,这里只会在父页面执行
|
||||
// 这里被注释的代码,触发搜索菜单要按下回车键
|
||||
$(".search-menu").focus(
|
||||
function(){
|
||||
$(document).on("keydown",
|
||||
function(event){
|
||||
if( event.keyCode === 13 ){
|
||||
window.searchMenuData = [];
|
||||
var searchKey = $(".search-menu").val();
|
||||
if(searchKey){
|
||||
searchMenu(window.menuData, searchKey);
|
||||
layui.apidoc.setConfigType("yml");
|
||||
layui.apidoc.setConfigPath("/static/component/laytp/config/api.config.yml?v=" + localStorage.getItem("version"));
|
||||
var param = layui.apidoc.readConfig();
|
||||
param.menu.async = false;
|
||||
param.menu.data = window.searchMenuData;
|
||||
param.isSearch = true;
|
||||
layui.apidoc.render(param);
|
||||
}else{
|
||||
$("#sideMenu").show();
|
||||
$("#searchSideMenu").hide();
|
||||
renderMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
$(".search-menu").unbind();
|
||||
}
|
||||
);
|
||||
|
||||
// 递归搜索菜单
|
||||
window.searchMenu = function(menuData, searchKey){
|
||||
searchKey = searchKey.toLowerCase();
|
||||
$.each(menuData, function(i, item) {
|
||||
var oldTitle = item.title;
|
||||
item.title = item.title.toLowerCase();
|
||||
if(item.title.indexOf(searchKey) > -1){
|
||||
item.title = oldTitle;
|
||||
window.searchMenuData.push(item);
|
||||
}else{
|
||||
item.title = oldTitle;
|
||||
if(item.children && item.children.length > 0){
|
||||
searchMenu(item.children, searchKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 重新渲染菜单
|
||||
window.renderMenu = function(){
|
||||
$(".search-menu").val('');
|
||||
layui.apidoc.setConfigType("yml");
|
||||
layui.apidoc.setConfigPath("/static/component/laytp/config/api.config.yml?v=" + localStorage.getItem("version"));
|
||||
var config = layui.apidoc.readConfig();
|
||||
layui.apidoc.menuRender(config);
|
||||
};
|
||||
|
||||
window.api = {
|
||||
testInline: function (id) {
|
||||
let route = $("#test_inline_" + id).attr('route');
|
||||
let url = nowSetJson.apiUrl + route;
|
||||
let method = $("#test_inline_" + id).attr('method');
|
||||
|
||||
if (nowSetJson.createSign === "1") {
|
||||
nowSetJson.headerJson['request-time'] = Date.now();
|
||||
nowSetJson.headerJson.sign = api.createSign(nowSetJson.headerJson['request-time']);
|
||||
}
|
||||
|
||||
let formData = new FormData();
|
||||
$('#test_inline_'+id).find('input').each(function (i, input) {
|
||||
if ($(input).attr('type') == 'file') {
|
||||
formData.append($(input).attr('name'), $(input)[0].files[0]);
|
||||
} else {
|
||||
formData.append($(input).attr('name'), $(input).val())
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
headers: nowSetJson.headerJson,
|
||||
data: $('#test_inline_'+id).prop('method').toLowerCase() == 'get' ? $('#test_inline_'+id).serialize() : formData,
|
||||
type: method,
|
||||
dataType: 'json',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function (data, textStatus, xhr) {
|
||||
if (typeof data === 'object') {
|
||||
var str = JSON.stringify(data, null, 2);
|
||||
if(str) {
|
||||
$('#response_' + id).html(hljs.highlightAuto(str).value);
|
||||
}
|
||||
} else {
|
||||
$('#response_' + id).html(data || '');
|
||||
}
|
||||
$('#response_headers_' + id).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
|
||||
$('#response_card_' + id).show();
|
||||
facade.success('请求成功');
|
||||
},
|
||||
error: function (xhr) {
|
||||
try {
|
||||
var str = JSON.stringify($.parseJSON(xhr.responseText), null, 2);
|
||||
} catch (e) {
|
||||
var str = xhr.responseText;
|
||||
}
|
||||
$('#response_headers_' + id).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
|
||||
if(str){
|
||||
$('#response_' + id).html(hljs.highlightAuto(str).value);
|
||||
}
|
||||
$('#response_card_' + id).show();
|
||||
facade.error('请求失败');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
createSign: function (requestTime) {
|
||||
return md5(md5(requestTime) + md5(createSignKey)).toUpperCase();
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
return[[
|
||||
'name'=>'生成Api文档',
|
||||
'href'=>'/admin/plugin/apidoc/index.html',
|
||||
'is_menu'=>1,
|
||||
'icon'=>'layui-icon layui-icon-read',
|
||||
'children'=>[
|
||||
['name'=>'查看和搜索列表', 'rule'=>'/plugin/apidoc/index/index', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'查看单条数据详情', 'rule'=>'/plugin/apidoc/index/info', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'添加', 'rule'=>'/plugin/apidoc/index/add', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'编辑', 'rule'=>'/plugin/apidoc/index/edit', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'生成文档', 'rule'=>'/plugin/apidoc/index/create', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'删除', 'rule'=>'/plugin/apidoc/index/del', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'回收站', 'rule'=>'/plugin/apidoc/index/recycle', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'还原', 'rule'=>'/plugin/apidoc/index/restore', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
['name'=>'真实删除', 'rule'=>'/plugin/apidoc/index/trueDel', 'is_menu'=>2, 'icon'=>'layui-icon layui-icon-fire'],
|
||||
]
|
||||
]];
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* 后台系统配置模型
|
||||
*/
|
||||
|
||||
namespace plugin\apidoc\model;
|
||||
|
||||
use laytp\BaseModel;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
class Apidoc extends BaseModel
|
||||
{
|
||||
use SoftDelete;
|
||||
protected $name = 'plugin_apidoc';
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>添加Api文档</title>
|
||||
</head>
|
||||
<body>
|
||||
<form class="layui-form" lay-filter="layui-form">
|
||||
<div class="mainBox">
|
||||
<div class="main-container">
|
||||
<div class="main-container">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" title="标题">标题</label>
|
||||
<div class="layui-input-block">
|
||||
<input autocomplete="off" type="text" name="title" id="title" placeholder="请输入标题" class="layui-input"
|
||||
lay-verify="required">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" title="描述">描述</label>
|
||||
<div class="layui-input-block">
|
||||
<iframe src="/admin/meditor.html" class="editor" data-type="meditor" data-id="des"
|
||||
style="width:100%;height:560px;border: 0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="button-container">
|
||||
<button type="button" class="laytp-btn laytp-btn-primary laytp-btn-sm" lay-submit="" lay-filter="add">
|
||||
<i class="layui-icon layui-icon-ok"></i>
|
||||
提交
|
||||
</button>
|
||||
<button type="reset" class="laytp-btn laytp-btn-sm">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
if(localStorage.getItem("staticDomain")){
|
||||
document.write("<link rel='stylesheet' href='" + localStorage.getItem("staticDomain") + "/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}else{
|
||||
document.write("<link rel='stylesheet' href='/static/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='/static/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
layui.use(['laytp'],function(){
|
||||
layui.form.on('submit(add)', function(data){
|
||||
data = facade.setEditorField(data);
|
||||
facade.ajax({
|
||||
route:'/plugin/apidoc/index/add',
|
||||
data : data.field
|
||||
}).done(function(res){
|
||||
if(res.code === 0){
|
||||
parent.layui.layer.close(parent.layui.layer.getFrameIndex(window.name));//关闭当前页
|
||||
parent.layui.table.reload("laytp-table");
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>编辑Api文档</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/html" id="form">
|
||||
<input type="hidden" name="id" value="{{ d.id }}" />
|
||||
<div class="mainBox">
|
||||
<div class="main-container">
|
||||
<div class="main-container">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" title="标题">标题</label>
|
||||
<div class="layui-input-block">
|
||||
<input autocomplete="off" type="text" name="title" id="title" placeholder="请输入标题" class="layui-input"
|
||||
lay-verify="required" value="{{=d.title}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" title="描述">描述</label>
|
||||
<div class="layui-input-block">
|
||||
<iframe src="/admin/meditor.html?id=des" class="editor" data-type="meditor" data-id="des"
|
||||
style="width:100%;height:560px;border: 0"></iframe>
|
||||
<textarea class="editorContent" data-id="des" style="display:none;">{{=d.des}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="button-container">
|
||||
<button type="button" class="laytp-btn laytp-btn-primary laytp-btn-sm" lay-submit="" lay-filter="edit">
|
||||
<i class="layui-icon layui-icon-ok"></i>
|
||||
提交
|
||||
</button>
|
||||
<button type="reset" class="laytp-btn laytp-btn-sm">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<form class="layui-form" lay-filter="layui-form"></form>
|
||||
<script>
|
||||
if(localStorage.getItem("staticDomain")){
|
||||
document.write("<link rel='stylesheet' href='" + localStorage.getItem("staticDomain") + "/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}else{
|
||||
document.write("<link rel='stylesheet' href='/static/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='/static/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
layui.use(['laytp'],function(){
|
||||
let form = layui.form;
|
||||
let $ = layui.jquery;
|
||||
//获取参数ID
|
||||
var id = facade.getUrlParam('id');
|
||||
if(!id){
|
||||
facade.error('参数ID不能为空');
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取数据,渲染到对应的节点上
|
||||
facade.ajax({
|
||||
route: "/plugin/apidoc/index/info",
|
||||
data: {id: id},
|
||||
successAlert: false,
|
||||
showLoading: true
|
||||
}).done(function(res){
|
||||
if(res.code === 0){
|
||||
layui.laytpl($("#form").html()).render(res.data,function(string){
|
||||
$("form").html(string);
|
||||
layui.laytpForm.render();
|
||||
form.render();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
form.on('submit(edit)', function(data){
|
||||
data = facade.setEditorField(data);
|
||||
facade.ajax({
|
||||
route:'/plugin/apidoc/index/edit',
|
||||
data:data.field
|
||||
}).done(function(res){
|
||||
if(res.code === 0){
|
||||
parent.layui.layer.close(parent.layui.layer.getFrameIndex(window.name));//关闭当前页
|
||||
parent.layui.table.reload("laytp-table");
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>用户管理</title>
|
||||
</head>
|
||||
<body class="laytp-container">
|
||||
<div id="search-form" style="display:none;">
|
||||
<div class="layui-layer-title">搜索</div>
|
||||
<form class="layui-form search-form-body" lay-filter="layui-form">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="ID">ID</label>
|
||||
<div class="layui-input-inline">
|
||||
<input autocomplete="off" type="text" id="id" name="search_param[id][value]" id="id"
|
||||
placeholder="请输入ID" class="layui-input">
|
||||
<input type="hidden" name="search_param[id][condition]" value="=">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="标题">标题</label>
|
||||
<div class="layui-input-inline">
|
||||
<input autocomplete="off" type="text" id="title" name="search_param[title][value]"
|
||||
placeholder="请输入标题" class="layui-input">
|
||||
<input type="hidden" name="search_param[title][condition]" value="LIKE">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="描述">描述</label>
|
||||
<div class="layui-input-inline">
|
||||
<input autocomplete="off" type="text" id="des" name="search_param[des][value]" placeholder="请输入描述"
|
||||
class="layui-input">
|
||||
<input type="hidden" name="search_param[des][condition]" value="LIKE">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="创建时间">创建时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" class="layui-input laydate"
|
||||
id="create_time" name="search_param[create_time][value]"
|
||||
data-type="datetime" data-isRange="true" placeholder="请选择创建时间">
|
||||
<input type="hidden" name="search_param[create_time][condition]" value="BETWEEN">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bottom">
|
||||
<div class="button-container">
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-sm" lay-submit="" lay-filter="laytp-recycle-search-form">
|
||||
<i class="layui-icon layui-icon-ok"></i>
|
||||
提交
|
||||
</button>
|
||||
<button type="button" class="laytp-btn laytp-btn-sm laytp-recycle-search-form-reset">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<table id="laytp-table" lay-filter="laytp-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="default-toolbar">
|
||||
<div class="dropdown-menu">
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-md">
|
||||
<i class="layui-icon layui-icon-triangle-d"></i>
|
||||
批量操作
|
||||
</button>
|
||||
<ul class="dropdown-menu-nav layui-anim-upbit dropdown-bottom-left layui-anim">
|
||||
<div class="dropdown-anchor"></div>
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/edit")){ }}
|
||||
<li><a lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a></li>
|
||||
{{# } }}
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/del")){ }}
|
||||
<li><a lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a></li>
|
||||
{{# } }}
|
||||
</ul>
|
||||
</div>
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/add")){ }}
|
||||
<button class="laytp-btn laytp-btn-danger laytp-btn-md" lay-event="add">
|
||||
<i class="layui-icon layui-icon-add-1"></i>
|
||||
新增
|
||||
</button>
|
||||
{{# } }}
|
||||
<button class="laytp-btn laytp-btn-warming laytp-btn-md" lay-event="search">
|
||||
<i class="layui-icon layui-icon-search"></i>
|
||||
搜索
|
||||
</button>
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/recycle")){ }}
|
||||
<button class="laytp-btn laytp-btn-success laytp-btn-md" lay-event="recycle">
|
||||
<i class="layui-icon layui-icon-delete"></i>
|
||||
回收站
|
||||
</button>
|
||||
{{# } }}
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/create")){ }}
|
||||
<button class="laytp-btn laytp-btn-default laytp-btn-md"lay-event="create">
|
||||
<i class="layui-icon layui-icon-tabs"></i>
|
||||
生成Api文档
|
||||
</button>
|
||||
{{# } }}
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/create")){ }}
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-md fa fa-binoculars"
|
||||
lay-event="open"> 查看Api文档
|
||||
</button>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="default-bar">
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/edit")){ }}
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</button>
|
||||
{{# } }}
|
||||
{{# if(facade.hasAuth("/plugin/apidoc/index/del")){ }}
|
||||
<button class="laytp-btn laytp-btn-danger laytp-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</button>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if(localStorage.getItem("staticDomain")){
|
||||
document.write("<link rel='stylesheet' href='" + localStorage.getItem("staticDomain") + "/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/plugin/apidoc/js/index.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}else{
|
||||
document.write("<link rel='stylesheet' href='/static/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='/static/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/plugin/apidoc/js/index.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Api文档回收站</title>
|
||||
</head>
|
||||
<body class="laytp-container-recycle">
|
||||
<div id="recycle-search-form" style="display:none;">
|
||||
<div class="layui-layer-title">搜索</div>
|
||||
<form class="layui-form search-form-body" lay-filter="layui-form">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="ID">ID</label>
|
||||
<div class="layui-input-inline">
|
||||
<input autocomplete="off" type="text" id="id" name="search_param[id][value]" id="id"
|
||||
placeholder="请输入ID" class="layui-input">
|
||||
<input type="hidden" name="search_param[id][condition]" value="=">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="标题">标题</label>
|
||||
<div class="layui-input-inline">
|
||||
<input autocomplete="off" type="text" id="title" name="search_param[title][value]"
|
||||
placeholder="请输入标题" class="layui-input">
|
||||
<input type="hidden" name="search_param[title][condition]" value="LIKE">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="描述">描述</label>
|
||||
<div class="layui-input-inline">
|
||||
<input autocomplete="off" type="text" id="des" name="search_param[des][value]" placeholder="请输入描述"
|
||||
class="layui-input">
|
||||
<input type="hidden" name="search_param[des][condition]" value="LIKE">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label" title="创建时间">创建时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" class="layui-input laydate"
|
||||
id="create_time" name="search_param[create_time][value]"
|
||||
data-type="datetime" data-isRange="true" placeholder="请选择创建时间">
|
||||
<input type="hidden" name="search_param[create_time][condition]" value="BETWEEN">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bottom">
|
||||
<div class="button-container">
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-sm" lay-submit="" lay-filter="laytp-recycle-search-form">
|
||||
<i class="layui-icon layui-icon-ok"></i>
|
||||
提交
|
||||
</button>
|
||||
<button type="button" class="laytp-btn laytp-btn-sm laytp-recycle-search-form-reset">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<table id="laytp-recycle-table" lay-filter="laytp-recycle-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="recycle-default-toolbar">
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-md" lay-event="restore">
|
||||
<i class="laytp-icon laytp-icon-back"></i>
|
||||
还原
|
||||
</button>
|
||||
<button class="laytp-btn laytp-btn-danger laytp-btn-md" lay-event="true-del">
|
||||
<i class="layui-icon layui-icon-delete"></i>
|
||||
删除
|
||||
</button>
|
||||
<button class="laytp-btn laytp-btn-warming laytp-btn-md" lay-event="recycle-search">
|
||||
<i class="layui-icon layui-icon-search"></i>
|
||||
搜索
|
||||
</button>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="recycle-default-bar">
|
||||
<button class="laytp-btn laytp-btn-primary laytp-btn-xs" lay-event="restore"><i class="laytp-icon laytp-icon-back"></i>还原</button>
|
||||
<button class="laytp-btn laytp-btn-danger laytp-btn-xs" lay-event="true-del"><i class="layui-icon layui-icon-delete"></i>删除</button>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if(localStorage.getItem("staticDomain")){
|
||||
document.write("<link rel='stylesheet' href='" + localStorage.getItem("staticDomain") + "/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='" + localStorage.getItem("staticDomain") + "/plugin/apidoc/js/recycle.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}else{
|
||||
document.write("<link rel='stylesheet' href='/static/component/laytp/css/laytp.css?v=" + localStorage.getItem("version") + "'>");
|
||||
document.write("<script src='/static/component/layui/layui.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/component/laytp/layuiConfig.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
document.write("<script src='/static/plugin/apidoc/js/recycle.js?v="+localStorage.getItem("version")+"'><\/script>");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
## 网站配置
|
||||
logo:
|
||||
## 网站名称
|
||||
title: "Api文档"
|
||||
## 网站图标
|
||||
image: "/static/admin/images/logo.png"
|
||||
## 菜单配置
|
||||
menu:
|
||||
## 菜单数据来源
|
||||
data: "/plugin/apidoc/index/getMenu"
|
||||
## 菜单接口的请求方式 GET / POST
|
||||
method: "GET"
|
||||
## 是否同时只打开一个菜单目录
|
||||
accordion: true
|
||||
## 是否开启多系统菜单模式
|
||||
control: true
|
||||
## 当control为true时,顶部最多显示多少菜单,其他顶部菜单需要点击最后的顶部下拉菜单进行展示
|
||||
maxTopMenuNum: 8
|
||||
## 默认选中的菜单项
|
||||
select: "1"
|
||||
## 是否开启异步菜单,false 时 data 属性设置为菜单数据,true 时为 json 文件或后端接口
|
||||
async: true
|
||||
## 视图内容配置
|
||||
tab:
|
||||
## 是否开启多选项卡
|
||||
muiltTab: true
|
||||
## 保持视图状态
|
||||
keepState: false
|
||||
## 开启选项卡记忆
|
||||
session: true
|
||||
## 最大可打开的选项卡数量
|
||||
tabMax: "30"
|
||||
## 首页
|
||||
index:
|
||||
id: "2" ## 标识 ID , 建议与菜单项中的 ID 一致
|
||||
href: "/admin/console.html" ## 页面地址
|
||||
title: "控制面板" ## 标题
|
||||
## 主题配置
|
||||
theme:
|
||||
## 默认主题色,对应 colors 配置中的 ID 标识
|
||||
defaultColor: "2"
|
||||
## 默认的菜单主题 dark-theme 黑 / light-theme 白
|
||||
defaultMenu: "dark-theme"
|
||||
## 是否允许用户切换主题,false 时关闭自定义主题面板
|
||||
allowCustom: true
|
||||
## 主题色配置列表
|
||||
colors:
|
||||
- id: "1"
|
||||
color: "#2d8cf0"
|
||||
- id: "2"
|
||||
color: "#36b368"
|
||||
- id: "3"
|
||||
color: "#f6ad55"
|
||||
- id: "4"
|
||||
color: "#f56c6c"
|
||||
- id: "5"
|
||||
color: "#3963bc"
|
||||
## 主题面板的链接列表
|
||||
links:
|
||||
- icon: "layui-icon layui-icon-website"
|
||||
title: "官方网站"
|
||||
href: "http://www.laytp.com"
|
||||
- icon: "layui-icon layui-icon-read"
|
||||
title: "开发文档"
|
||||
href: "https://www.laytp.com/doc.html"
|
||||
- icon: "layui-icon layui-icon-fonts-code"
|
||||
title: "开源地址"
|
||||
href: "https://gitee.com/junstar/laytp"
|
||||
## 其他配置
|
||||
other:
|
||||
## 主页动画时长
|
||||
keepLoad: "500"
|
||||
## 布局顶部主题
|
||||
autoHead: false
|
||||
## 头部配置
|
||||
header:
|
||||
## 站内消息,数据来源,通过 false 设置关闭
|
||||
message: false
|
||||
@@ -0,0 +1,522 @@
|
||||
layui.define([
|
||||
'message',
|
||||
'table',
|
||||
'jquery',
|
||||
'element',
|
||||
'yaml',
|
||||
'form',
|
||||
'laytpTab',
|
||||
'apimenu',
|
||||
'frame',
|
||||
'theme',
|
||||
'convert'
|
||||
],
|
||||
function(exports) {
|
||||
"use strict";
|
||||
|
||||
var $ = layui.jquery,
|
||||
form = layui.form,
|
||||
element = layui.element,
|
||||
yaml = layui.yaml,
|
||||
laytpTab = layui.laytpTab,
|
||||
convert = layui.convert,
|
||||
laytpApiMenu = layui.apimenu,
|
||||
laytpFrame = layui.frame,
|
||||
laytpTheme = layui.theme,
|
||||
message = layui.message;
|
||||
|
||||
var bodyFrame;
|
||||
var sideMenu;
|
||||
var bodyTab;
|
||||
var config;
|
||||
var logout = function() {};
|
||||
var msgInstance;
|
||||
|
||||
var body = layui.$('body');
|
||||
|
||||
var laytpApidoc = new function() {
|
||||
|
||||
// 默认配置
|
||||
var configType = 'yml';
|
||||
var configPath = 'api.config.yml';
|
||||
|
||||
this.setConfigPath = function(path) {
|
||||
configPath = path;
|
||||
};
|
||||
|
||||
this.setConfigType = function(type) {
|
||||
configType = type;
|
||||
};
|
||||
|
||||
this.setAvatar = function(url, username) {
|
||||
var image = new Image();
|
||||
image.src = url || "/static/admin/images/avatar.jpg";
|
||||
image.onload = function() {
|
||||
layui.$(".layui-nav-img").attr("src", convert.imageToBase64(image));
|
||||
};
|
||||
layui.$(".layui-nav-img").parent().append(username);
|
||||
};
|
||||
|
||||
this.render = function(initConfig) {
|
||||
if (initConfig !== undefined) {
|
||||
applyConfig(initConfig);
|
||||
} else {
|
||||
applyConfig(laytpApidoc.readConfig());
|
||||
}
|
||||
};
|
||||
|
||||
this.readConfig = function() {
|
||||
if (configType === "yml") {
|
||||
return yaml.load(configPath);
|
||||
} else {
|
||||
var data;
|
||||
$.ajax({
|
||||
url: configPath,
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
async: false,
|
||||
success: function(result) {
|
||||
data = result;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
this.messageRender = function(option) {
|
||||
var option = {
|
||||
elem: '.message',
|
||||
url: option.header.message,
|
||||
height: '250px'
|
||||
};
|
||||
msgInstance = message.render(option);
|
||||
};
|
||||
|
||||
this.logoRender = function(param) {
|
||||
layui.$(".layui-logo .logo").attr("src", param.logo.image);
|
||||
layui.$(".layui-logo .title").html(param.logo.title);
|
||||
};
|
||||
|
||||
this.menuRender = function(param) {
|
||||
sideMenu = laytpApiMenu.render({
|
||||
elem: 'sideMenu',
|
||||
async: param.menu.async !== undefined ? param.menu.async : true,
|
||||
theme: "dark-theme",
|
||||
// height: '100%',
|
||||
method: param.menu.method,
|
||||
control: param.menu.control ? 'control' : false, // control
|
||||
defaultMenu: 0,
|
||||
accordion: param.menu.accordion,
|
||||
url: param.menu.data,
|
||||
data: param.menu.data, //async为false时,传入菜单数组
|
||||
parseData: function(res){
|
||||
if(param.isSearch){
|
||||
var result = {
|
||||
"id" : -1,
|
||||
"is_menu" : 1,
|
||||
"is_show" : 1,
|
||||
"title" : "搜索结果",
|
||||
"type" : 0,
|
||||
"children" : res,
|
||||
"href" : "",
|
||||
"icon" : ""
|
||||
};
|
||||
var resArr = [];
|
||||
resArr.push(result);
|
||||
return resArr;
|
||||
}else{
|
||||
window.menuData = laytpApidoc.parseData(res.data);
|
||||
return window.menuData;
|
||||
}
|
||||
},
|
||||
change: function() {
|
||||
compatible();
|
||||
},
|
||||
done: function() {
|
||||
let firstMenuObj = layui.$("#sideMenu a[menu-id='" + param.menu.select + "']").parent();
|
||||
let menuType = $(firstMenuObj.html()).attr("doc-type");
|
||||
let id = $(firstMenuObj.html()).attr("menu-id");
|
||||
$('.api-page-tab-content').hide();
|
||||
$('#' + menuType + '_' + id).show();
|
||||
sideMenu.selectItem(param.menu.select);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.parseData = function(data){
|
||||
$.each(data, function(i, item) {
|
||||
var tempItem = item;
|
||||
// tempItem.id = item.id;
|
||||
// tempItem.href = item.href;
|
||||
// tempItem.icon = item.icon;
|
||||
tempItem.type = 0;
|
||||
// tempItem.title = item.name;
|
||||
if(typeof item.children != null && typeof item.children !== "undefined" && item.children.length > 0){
|
||||
tempItem.children = laytpApidoc.parseData(item.children);
|
||||
}else{
|
||||
tempItem.type = 1;
|
||||
}
|
||||
data[i] = tempItem;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
this.bodyRender = function(param) {
|
||||
if (param.tab.muiltTab) {
|
||||
sideMenu.click(function(dom, data) {
|
||||
let menuType = data.docType;
|
||||
let id = data.menuId;
|
||||
$('.api-page-tab-content').hide();
|
||||
$('#' + menuType + '_' + id).show();
|
||||
compatible();
|
||||
});
|
||||
} else {
|
||||
sideMenu.click(function(dom, data) {
|
||||
let menuType = data.docType;
|
||||
let id = data.menuId;
|
||||
$('.api-page-tab-content').hide();
|
||||
$('#' + menuType + '_' + id).show();
|
||||
compatible();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.keepLoad = function(param) {
|
||||
compatible();
|
||||
setTimeout(function() {
|
||||
layui.$(".loader-main").fadeOut(200);
|
||||
}, param.other.keepLoad)
|
||||
};
|
||||
|
||||
this.themeRender = function(option) {
|
||||
if (option.theme.allowCustom === false) {
|
||||
layui.$(".setting").remove();
|
||||
}
|
||||
var colorId = localStorage.getItem("theme-color");
|
||||
var currentColor = getColorById(colorId);
|
||||
localStorage.setItem("theme-color", currentColor.id);
|
||||
localStorage.setItem("theme-color-context", currentColor.color);
|
||||
laytpTheme.changeTheme(window, option.other.autoHead);
|
||||
var menu = localStorage.getItem("theme-menu");
|
||||
if (menu == null) {
|
||||
menu = option.theme.defaultMenu;
|
||||
} else {
|
||||
if (option.theme.allowCustom === false) {
|
||||
menu = option.theme.defaultMenu;
|
||||
}
|
||||
}
|
||||
localStorage.setItem("theme-menu", menu);
|
||||
this.menuSkin(menu);
|
||||
}
|
||||
|
||||
this.menuSkin = function(theme) {
|
||||
var laytpApidoc = layui.$(".laytp-admin");
|
||||
laytpApidoc.removeClass("light-theme");
|
||||
laytpApidoc.removeClass("dark-theme");
|
||||
laytpApidoc.addClass(theme);
|
||||
}
|
||||
|
||||
this.logout = function(callback) {
|
||||
logout = callback;
|
||||
}
|
||||
|
||||
this.message = function(callback) {
|
||||
if (callback != null) {
|
||||
msgInstance.click(callback);
|
||||
} else {
|
||||
msgInstance.click(messageTip);
|
||||
}
|
||||
}
|
||||
|
||||
this.jump = function(id, title, url) {
|
||||
if (config.tab.muiltTab) {
|
||||
bodyTab.addTabOnly({
|
||||
id: id,
|
||||
title: title,
|
||||
url: url,
|
||||
icon: null,
|
||||
close: true
|
||||
}, 300);
|
||||
} else {
|
||||
sideMenu.selectItem(id);
|
||||
bodyFrame.changePage(url, title, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var messageTip = function(id, title, context, form) {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '消息', //标题
|
||||
area: ['390px', '330px'], //宽高
|
||||
shade: 0.4, //遮罩透明度
|
||||
content: "<div style='background-color:whitesmoke;'><div class='layui-card'><div class='layui-card-body'>来源 : " +
|
||||
form + "</div><div class='layui-card-header' >标题 : " + title +
|
||||
"</div><div class='layui-card-body' >内容 : " + context + "</div></div></div>", //支持获取DOM元素
|
||||
btn: ['确认'], //按钮组
|
||||
scrollbar: false, //屏蔽浏览器滚动条
|
||||
yes: function(index) { //layer.msg('yes'); //点击确定回调
|
||||
layer.close(index);
|
||||
showToast();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function collaspe() {
|
||||
sideMenu.collaspe();
|
||||
var admin = layui.$(".laytp-admin");
|
||||
var left = layui.$(".layui-icon-spread-left")
|
||||
var right = layui.$(".layui-icon-shrink-right")
|
||||
if (admin.is(".laytp-mini")) {
|
||||
$(".search").show();
|
||||
left.addClass("layui-icon-shrink-right")
|
||||
left.removeClass("layui-icon-spread-left")
|
||||
admin.removeClass("laytp-mini");
|
||||
} else {
|
||||
$(".search").hide();
|
||||
right.addClass("layui-icon-spread-left")
|
||||
right.removeClass("layui-icon-shrink-right")
|
||||
admin.addClass("laytp-mini");
|
||||
}
|
||||
}
|
||||
|
||||
body.on("click", ".logout", function() {
|
||||
// 回调
|
||||
var result = logout();
|
||||
|
||||
if (result) {
|
||||
// 清空缓存
|
||||
bodyTab.clear();
|
||||
}
|
||||
})
|
||||
|
||||
body.on("click", ".collaspe,.laytp-cover", function() {
|
||||
collaspe();
|
||||
});
|
||||
|
||||
body.on("click", ".fullScreen", function() {
|
||||
if (layui.$(this).hasClass("layui-icon-screen-restore")) {
|
||||
screenFun(2).then(function() {
|
||||
layui.$(".fullScreen").eq(0).removeClass("layui-icon-screen-restore");
|
||||
});
|
||||
} else {
|
||||
screenFun(1).then(function() {
|
||||
layui.$(".fullScreen").eq(0).addClass("layui-icon-screen-restore");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
body.on("click", '[user-menu-id]', function() {
|
||||
if (config.tab.muiltTab) {
|
||||
bodyTab.addTabOnly({
|
||||
id: layui.$(this).attr("user-menu-id"),
|
||||
title: layui.$(this).attr("user-menu-title"),
|
||||
url: layui.$(this).attr("user-menu-url"),
|
||||
icon: "",
|
||||
close: true
|
||||
}, 300);
|
||||
} else {
|
||||
bodyFrame.changePage(layui.$(this).attr("user-menu-url"), "", true);
|
||||
}
|
||||
});
|
||||
|
||||
body.on("click", ".setting", function() {
|
||||
|
||||
var bgColorHtml =
|
||||
'<li class="layui-this" data-select-bgcolor="dark-theme" >' +
|
||||
'<a href="javascript:;" data-skin="skin-blue" style="" class="clearfix full-opacity-hover">' +
|
||||
'<div><span style="display:block; width: 20%; float: left; height: 12px; background: #28333E;"></span><span style="display:block; width: 80%; float: left; height: 12px; background: white;"></span></div>' +
|
||||
'<div><span style="display:block; width: 20%; float: left; height: 40px; background: #28333E;"></span><span style="display:block; width: 80%; float: left; height: 40px; background: #f4f5f7;"></span></div>' +
|
||||
'</a>' +
|
||||
'</li>';
|
||||
|
||||
bgColorHtml +=
|
||||
'<li data-select-bgcolor="light-theme" >' +
|
||||
'<a href="javascript:;" data-skin="skin-blue" style="" class="clearfix full-opacity-hover">' +
|
||||
'<div><span style="display:block; width: 20%; float: left; height: 12px; background: white;"></span><span style="display:block; width: 80%; float: left; height: 12px; background: white;"></span></div>' +
|
||||
'<div><span style="display:block; width: 20%; float: left; height: 40px; background: white;"></span><span style="display:block; width: 80%; float: left; height: 40px; background: #f4f5f7;"></span></div>' +
|
||||
'</a>' +
|
||||
'</li>';
|
||||
|
||||
var html =
|
||||
'<div class="laytpone-color">\n' +
|
||||
'<div class="color-title">整体风格</div>\n' +
|
||||
'<div class="color-content">\n' +
|
||||
'<ul>\n' + bgColorHtml + '</ul>\n' +
|
||||
'</div>\n' +
|
||||
'</div>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
offset: 'r',
|
||||
area: ['320px', '100%'],
|
||||
title: false,
|
||||
shade: 0.1,
|
||||
closeBtn: 0,
|
||||
shadeClose: false,
|
||||
anim: -1,
|
||||
skin: 'layer-anim-right',
|
||||
move: false,
|
||||
content: html + buildColorHtml() + buildLinkHtml() + bottomTool(),
|
||||
success: function(layero, index) {
|
||||
|
||||
var color = localStorage.getItem("theme-color");
|
||||
var menu = localStorage.getItem("theme-menu");
|
||||
|
||||
if (color !== "null") {
|
||||
layui.$(".select-color-item").removeClass("layui-icon").removeClass("layui-icon-ok");
|
||||
layui.$("*[color-id='" + color + "']").addClass("layui-icon").addClass("layui-icon-ok");
|
||||
}
|
||||
if (menu !== "null") {
|
||||
layui.$("*[data-select-bgcolor]").removeClass("layui-this");
|
||||
layui.$("[data-select-bgcolor='" + menu + "']").addClass("layui-this");
|
||||
}
|
||||
layui.$('#layui-layer-shade' + index).click(function() {
|
||||
var $layero = layui.$('#layui-layer' + index);
|
||||
$layero.animate({
|
||||
left: $layero.offset().left + $layero.width()
|
||||
}, 200, function() {
|
||||
layer.close(index);
|
||||
});
|
||||
})
|
||||
|
||||
layui.$('#closeTheme').click(function() {
|
||||
var $layero = layui.$('#layui-layer' + index);
|
||||
$layero.animate({
|
||||
left: $layero.offset().left + $layero.width()
|
||||
}, 200, function() {
|
||||
layer.close(index);
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function bottomTool() {
|
||||
return "<button id='closeTheme' style='position: absolute;bottom: 20px;left: 20px;' class='laytp-btn'>关闭</button>"
|
||||
}
|
||||
|
||||
body.on('click', '[data-select-bgcolor]', function() {
|
||||
var theme = layui.$(this).attr('data-select-bgcolor');
|
||||
layui.$('[data-select-bgcolor]').removeClass("layui-this");
|
||||
layui.$(this).addClass("layui-this");
|
||||
localStorage.setItem("theme-menu", theme);
|
||||
laytpApidoc.menuSkin(theme);
|
||||
});
|
||||
|
||||
body.on('click', '.select-color-item', function() {
|
||||
layui.$(".select-color-item").removeClass("layui-icon").removeClass("layui-icon-ok");
|
||||
layui.$(this).addClass("layui-icon").addClass("layui-icon-ok");
|
||||
var colorId = layui.$(".select-color-item.layui-icon-ok").attr("color-id");
|
||||
var currentColor = getColorById(colorId);
|
||||
localStorage.setItem("theme-color", currentColor.id);
|
||||
localStorage.setItem("theme-color-context", currentColor.color);
|
||||
laytpTheme.changeTheme(window, config.other.autoHead);
|
||||
});
|
||||
|
||||
function applyConfig(param) {
|
||||
config = param;
|
||||
laytpApidoc.logoRender(param);
|
||||
laytpApidoc.menuRender(param);
|
||||
laytpApidoc.bodyRender(param);
|
||||
laytpApidoc.themeRender(param);
|
||||
laytpApidoc.keepLoad(param);
|
||||
if (param.header.message !== false) {
|
||||
laytpApidoc.messageRender(param);
|
||||
}
|
||||
}
|
||||
|
||||
function getColorById(id) {
|
||||
var color;
|
||||
var flag = false;
|
||||
$.each(config.colors, function(i, value) {
|
||||
if (value.id === id) {
|
||||
color = value;
|
||||
flag = true;
|
||||
}
|
||||
})
|
||||
if (flag === false || config.theme.allowCustom === false) {
|
||||
$.each(config.colors, function(i, value) {
|
||||
if (value.id === config.theme.defaultColor) {
|
||||
color = value;
|
||||
}
|
||||
})
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
function buildLinkHtml() {
|
||||
var links = "";
|
||||
$.each(config.links, function(i, value) {
|
||||
links += '<a class="more-menu-item" href="' + value.href + '">' +
|
||||
'<i class="' + value.icon + '" style="font-size: 19px;"></i> ' + value.title +
|
||||
'</a>'
|
||||
})
|
||||
return '<div class="more-menu-list">' + links + '</div>';
|
||||
}
|
||||
|
||||
function buildColorHtml() {
|
||||
return "";
|
||||
var colors = "";
|
||||
$.each(config.colors, function(i, value) {
|
||||
colors += "<span class='select-color-item' color-id='" + value.id + "' style='background-color:" + value.color +
|
||||
";'></span>";
|
||||
})
|
||||
return "<div class='select-color'><div class='select-color-title'>主题配色</div><div class='select-color-content'>" +
|
||||
colors + "</div></div>"
|
||||
}
|
||||
|
||||
function compatible() {
|
||||
if (layui.$(window).width() <= 768) {
|
||||
collaspe()
|
||||
}
|
||||
}
|
||||
|
||||
function screenFun(num) {
|
||||
num = num || 1;
|
||||
num = num * 1;
|
||||
var docElm = document.documentElement;
|
||||
switch (num) {
|
||||
case 1:
|
||||
if (docElm.requestFullscreen) {
|
||||
docElm.requestFullscreen();
|
||||
} else if (docElm.mozRequestFullScreen) {
|
||||
docElm.mozRequestFullScreen();
|
||||
} else if (docElm.webkitRequestFullScreen) {
|
||||
docElm.webkitRequestFullScreen();
|
||||
} else if (docElm.msRequestFullscreen) {
|
||||
docElm.msRequestFullscreen();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return new Promise(function(res, rej) {
|
||||
res("返回值");
|
||||
});
|
||||
}
|
||||
|
||||
function isFullscreen() {
|
||||
return document.fullscreenElement ||
|
||||
document.msFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.webkitFullscreenElement || false;
|
||||
}
|
||||
|
||||
window.onresize = function() {
|
||||
if (!isFullscreen()) {
|
||||
layui.$(".fullScreen").eq(0).removeClass("layui-icon-screen-restore");
|
||||
}
|
||||
}
|
||||
|
||||
exports('apidoc', laytpApidoc);
|
||||
})
|
||||
@@ -0,0 +1,491 @@
|
||||
layui.define(['table', 'jquery', 'element'], function(exports) {
|
||||
"use strict";
|
||||
|
||||
var MOD_NAME = 'apimenu',
|
||||
$ = layui.jquery,
|
||||
element = layui.element;
|
||||
|
||||
var laytpApiMenu = function(opt) {
|
||||
this.option = opt;
|
||||
};
|
||||
|
||||
// 供外部调用的,渲染菜单方法
|
||||
laytpApiMenu.prototype.render = function(opt) {
|
||||
var option = {
|
||||
elem: opt.elem,
|
||||
async: opt.async,
|
||||
parseData: opt.parseData,
|
||||
url: opt.url,
|
||||
method: opt.method ? opt.method : "GET",
|
||||
defaultOpen: opt.defaultOpen,
|
||||
defaultSelect: opt.defaultSelect,
|
||||
control: opt.control,
|
||||
defaultMenu: opt.defaultMenu,
|
||||
accordion: opt.accordion,
|
||||
height: opt.height,
|
||||
theme: opt.theme,
|
||||
data: opt.data ? opt.data : [],
|
||||
change: opt.change ? opt.change : function() {},
|
||||
done: opt.done ? opt.done : function() {}
|
||||
};
|
||||
if (option.async) {
|
||||
if (option.method === "GET") {
|
||||
getData(option.url).then(function(data) {
|
||||
option.data = data;
|
||||
renderMenu(option);
|
||||
});
|
||||
} else {
|
||||
postData(option.url).then(function(data) {
|
||||
option.data = data;
|
||||
renderMenu(option);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
//renderMenu中需要调用done事件,done事件中需要menu对象,但是此时还未返回menu对象,做个延时提前返回对象
|
||||
window.setTimeout(function() { renderMenu(option);}, 500);
|
||||
}
|
||||
|
||||
// 处理高度
|
||||
layui.$("#"+opt.elem).height(option.height);
|
||||
return new laytpApiMenu(opt);
|
||||
};
|
||||
|
||||
// 任意左侧菜单绑定点击事件
|
||||
laytpApiMenu.prototype.click = function(clickEvent) {
|
||||
var _this = this;
|
||||
layui.$(document).off("click", "#" + _this.option.elem + " .site-demo-active").on("click", "#" + _this.option.elem + " .site-demo-active", function() {
|
||||
// layui.$("body").on("click", "#" + _this.option.elem + " .site-demo-active", function() {
|
||||
var dom = layui.$(this);
|
||||
var data = {
|
||||
menuId: dom.attr("menu-id"),
|
||||
docType: dom.attr("doc-type"),
|
||||
menuTitle: dom.attr("menu-title"),
|
||||
};
|
||||
var doms = hash(dom);
|
||||
if (doms != null) {
|
||||
if (doms.text() != '') {
|
||||
data['menuPath'] = doms.find("span").text() + " / " + data['menuPath'];
|
||||
}
|
||||
}
|
||||
if (doms != null) {
|
||||
var domss = hash(doms);
|
||||
if(domss!=null){
|
||||
if (domss.text() != '') {
|
||||
data['menuPath'] = domss.find("span").text() + " / " + data['menuPath'];
|
||||
}}
|
||||
}
|
||||
if (domss != null) {
|
||||
|
||||
var domsss = hash(domss);
|
||||
if(domsss!=null){
|
||||
if (domsss.text() != '') {
|
||||
data['menuPath'] = domsss.find("span").text() + " / " + data['menuPath'];
|
||||
}}
|
||||
}
|
||||
if (layui.$("#" + _this.option.elem).is(".laytp-nav-mini")) {
|
||||
if (_this.option.accordion) {
|
||||
activeMenus = layui.$(this).parent().parent().parent().children("a");
|
||||
} else {
|
||||
activeMenus.push(layui.$(this).parent().parent().parent().children("a"));
|
||||
}
|
||||
}
|
||||
clickEvent(dom, data);
|
||||
})
|
||||
};
|
||||
|
||||
function hash(dom) {
|
||||
var d = dom.parent().parent().prev();
|
||||
if (d.prop("tagName") === "UL") {
|
||||
return null;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
// 样式选择
|
||||
laytpApiMenu.prototype.skin = function(skin) {
|
||||
var menu = layui.$(".laytp-nav-tree[lay-filter='" + this.option.elem + "']").parent();
|
||||
menu.removeClass("dark-theme");
|
||||
menu.removeClass("light-theme");
|
||||
menu.addClass(skin);
|
||||
};
|
||||
|
||||
// 选择没有子级的菜单节点
|
||||
laytpApiMenu.prototype.selectItem = function(laytpId) {
|
||||
if (this.option.control != false) {
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents(".layui-side-scroll ").find("ul").css({
|
||||
display: "none"
|
||||
});
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents(".layui-side-scroll ").find(".layui-this").removeClass(
|
||||
"layui-this");
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents("ul").css({
|
||||
display: "block"
|
||||
});
|
||||
var controlId = layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents("ul").attr("menu-id");
|
||||
if (controlId != undefined) {
|
||||
layui.$("#" + this.option.control).find(".layui-this").removeClass("layui-this");
|
||||
layui.$("#" + this.option.control).find("[menu-id='" + controlId + "']").addClass("layui-this");
|
||||
}
|
||||
}
|
||||
if (this.option.accordion === true) {
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents(".laytp-nav-tree").find(".layui-nav-itemed").removeClass(
|
||||
"layui-nav-itemed");
|
||||
}
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents(".laytp-nav-tree").find(".layui-this").removeClass(
|
||||
"layui-this");
|
||||
if (!layui.$("#" + this.option.elem).is(".laytp-nav-mini")) {
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents(".layui-nav-item").addClass("layui-nav-itemed");
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parents("dd").addClass("layui-nav-itemed");
|
||||
}
|
||||
layui.$("#" + this.option.elem + " a[menu-id='" + laytpId + "']").parent().addClass("layui-this");
|
||||
}
|
||||
|
||||
var activeMenus;
|
||||
// 手机模式下,右下角按钮点击展开收缩左侧菜单事件
|
||||
laytpApiMenu.prototype.collaspe = function(time) {
|
||||
var elem = this.option.elem;
|
||||
var config = this.option;
|
||||
if (layui.$("#" + this.option.elem).is(".laytp-nav-mini")) {
|
||||
$.each(activeMenus, function(i, item) {
|
||||
layui.$("#" + elem + " a[menu-id='" + layui.$(this).attr("menu-id") + "']").parent().addClass("layui-nav-itemed");
|
||||
});
|
||||
layui.$("#" + this.option.elem).removeClass("laytp-nav-mini");
|
||||
layui.$("#" + this.option.elem).animate({
|
||||
width: "220px"
|
||||
}, 150);
|
||||
isHoverMenu(false, config);
|
||||
} else {
|
||||
activeMenus = layui.$("#" + this.option.elem).find(".layui-nav-itemed>a");
|
||||
layui.$("#" + this.option.elem).find(".layui-nav-itemed").removeClass("layui-nav-itemed");
|
||||
layui.$("#" + this.option.elem).addClass("laytp-nav-mini");
|
||||
layui.$("#" + this.option.elem).animate({
|
||||
width: "60px"
|
||||
}, 400);
|
||||
isHoverMenu(true, config);
|
||||
}
|
||||
};
|
||||
|
||||
function getData(url) {
|
||||
var defer = $.Deferred();
|
||||
var contact = (url.indexOf('?') > -1) ? "&" : "?";
|
||||
$.get(url + contact + "fresh=" + Math.random(), function(result) {
|
||||
defer.resolve(result)
|
||||
});
|
||||
return defer.promise();
|
||||
}
|
||||
|
||||
function postData(url) {
|
||||
var defer = $.Deferred();
|
||||
$.post(url + "?fresh=" + Math.random(), function(result) {
|
||||
defer.resolve(result)
|
||||
});
|
||||
return defer.promise();
|
||||
}
|
||||
|
||||
// 内部使用,渲染菜单函数
|
||||
function renderMenu(option) {
|
||||
if (option.parseData != false) {
|
||||
option.data = option.parseData(option.data);
|
||||
}
|
||||
if (option.data.length > 0) {
|
||||
if (option.control != false) {
|
||||
createMenuAndControl(option);
|
||||
} else {
|
||||
createMenu(option);
|
||||
}
|
||||
}
|
||||
element.init();
|
||||
downShow(option);
|
||||
option.done();
|
||||
}
|
||||
|
||||
// 创建菜单
|
||||
function createMenu(option) {
|
||||
var menuHtml = '<div style="height:100%!important;" class="laytp-side-scroll layui-side-scroll ' + option.theme + '"><ul lay-filter="' + option.elem +
|
||||
'" class="layui-nav arrow laytp-menu layui-nav-tree laytp-nav-tree">';
|
||||
$.each(option.data, function(i, item) {
|
||||
var content = '<li class="layui-nav-item">';
|
||||
if (i === option.defaultOpen) {
|
||||
content = '<li class="layui-nav-item layui-nav-itemed">';
|
||||
}
|
||||
var href = "javascript:void(0);";
|
||||
var className = "site-demo-active";
|
||||
if (item.openType === "_blank" && item.type === 1) {
|
||||
className = "";
|
||||
}
|
||||
if (item.type === 0) {
|
||||
// 创 建 目 录 结 构
|
||||
content += '<a href="javascript:;" menu-type="' + item.type + '" doc-type="' + item.docType + '" menu-id="' + item.id + '" href="' + href + '">' +
|
||||
'<span>' + item.title + '</span></a>';
|
||||
} else if (item.type === 1) {
|
||||
content += '<a class="' + className + '" menu-type="' + item.type + '" doc-type="' + item.docType + '" menu-id="' +
|
||||
item.id +
|
||||
'" menu-title="' + item.title + '" href="' + href + '"><span>' + item.title + '</span></a>';
|
||||
}
|
||||
// 调 用 递 归 方 法 加 载 无 限 层 级 的 子 菜 单
|
||||
content += loadchild(item);
|
||||
// 结 束 一 个 根 菜 单 项
|
||||
content += '</li>';
|
||||
menuHtml += content;
|
||||
});
|
||||
// 结 束 菜 单 结 构 的 初 始 化
|
||||
menuHtml += "</ul></div>";
|
||||
// 将 菜 单 拼 接 到 初 始 化 容 器 中
|
||||
layui.$("#" + option.elem).html(menuHtml);
|
||||
}
|
||||
|
||||
// 创建多系统菜单, 包括渲染静态html和绑定顶部菜单点击事件
|
||||
function createMenuAndControl(option) {
|
||||
var control = '<ul class="layui-nav laytp-nav-control pc layui-hide-xs">';
|
||||
var controlPe = '<ul class="layui-nav laytp-nav-control layui-hide-sm">';
|
||||
// 声 明 头 部
|
||||
var menu = '<div class="layui-side-scroll ' + option.theme + '">';
|
||||
// 开 启 同 步 操 作
|
||||
var index = 0;
|
||||
var controlItemPe = '<dl class="layui-nav-child">';
|
||||
var config = layui.apidoc.readConfig();
|
||||
$.each(option.data, function(i, item) {
|
||||
var menuItem = '';
|
||||
var controlItem = '';
|
||||
if(index < config.menu['maxTopMenuNum']){
|
||||
if (i === option.defaultMenu) {
|
||||
controlItem = '<li menu-title="' + item.title + '" doc-type="' + item.docType + '" menu-id="' + item.id +
|
||||
'" class="layui-this layui-nav-item"><a href="javascript:void(0);">' + item.title + '</a></li>';
|
||||
menuItem = '<ul menu-id="' + item.id + '" lay-filter="' + option.elem +
|
||||
'" class="layui-nav arrow laytp-menu layui-nav-tree laytp-nav-tree">';
|
||||
// 兼容移动端
|
||||
controlPe += '<li class="layui-nav-item"><a class="pe-title" href="javascript:void(0);" >' + item.title + '</a>';
|
||||
controlItemPe += '<dd menu-title="' + item.title + '" menu-id="' + item.id +
|
||||
'"><a href="javascript:void(0);">' + item.title + '</a></dd>';
|
||||
} else {
|
||||
controlItem = '<li menu-title="' + item.title + '" doc-type="' + item.docType + '" menu-id="' + item.id +
|
||||
'" class="layui-nav-item"><a href="javascript:void(0);">' + item.title + '</a></li>';
|
||||
menuItem = '<ul style="display:none" menu-id="' + item.id + '" lay-filter="' + option.elem +
|
||||
'" class="layui-nav arrow layui-nav-tree laytp-nav-tree">';
|
||||
controlItemPe += '<dd menu-title="' + item.title + '" doc-type="' + item.docType + '" menu-id="' + item.id +
|
||||
'"><a href="javascript:void(0);">' + item.title + '</a></dd>';
|
||||
}
|
||||
index++;
|
||||
}else{
|
||||
menuItem = '<ul style="display:none" menu-id="' + item.id + '" lay-filter="' + option.elem +
|
||||
'" class="layui-nav arrow layui-nav-tree laytp-nav-tree">';
|
||||
controlItemPe += '<dd menu-title="' + item.title + '" doc-type="' + item.docType + '" menu-id="' + item.id +
|
||||
'"><a href="javascript:void(0);">' + item.title + '</a></dd>';
|
||||
index++;
|
||||
}
|
||||
|
||||
$.each(item.children, function(i, note) {
|
||||
// 创 建 每 一 个 菜 单 项
|
||||
var content = '<li class="layui-nav-item" >';
|
||||
var href = "javascript:void(0);";
|
||||
var target = "";
|
||||
var className = "site-demo-active";
|
||||
if (note.openType == "_blank" && note.type == 1) {
|
||||
href = note.href;
|
||||
target = "target='_blank'";
|
||||
className = "";
|
||||
}
|
||||
// 判 断 菜 单 类 型 0 是 不可跳转的目录 1 是 可 点 击 跳 转 的 菜 单
|
||||
if (note.type == 0) {
|
||||
// 创 建 目 录 结 构
|
||||
content += '<a href="javascript:void(0);" menu-type="' + item.type + '" doc-type="' + note.docType + '" menu-id="' + note.id +
|
||||
'" ><span>' + note.title +
|
||||
'</span></a>';
|
||||
} else if (note.type == 1) {
|
||||
// 创 建 菜 单 结 构
|
||||
content += '<a class="' + className + '" menu-type="' + item.type + '" doc-type="' + note.docType + '" menu-id="' + note.id +
|
||||
'" menu-title="' + note.title + '" href="' + href + '"><span>' + note.title + '</span></a>';
|
||||
}
|
||||
content += loadchild(note);
|
||||
content += '</li>';
|
||||
menuItem += content;
|
||||
})
|
||||
menu += menuItem + '</ul>';
|
||||
control += controlItem;
|
||||
})
|
||||
|
||||
if(option.data.length > config.menu['maxTopMenuNum']){
|
||||
control += '<li class="layui-nav-item">\n' +
|
||||
'<a href="javascript:void(0);" class="laytp-icon laytp-icon-elipsis"></a>' +
|
||||
'<!-- 功 能 菜 单 -->\n' +
|
||||
'<dl class="layui-nav-child">';
|
||||
var moreTopMenuIndex = 0;
|
||||
$.each(option.data, function(i, item) {
|
||||
var moreTopMenuItem = '';
|
||||
if(moreTopMenuIndex >= config.menu['maxTopMenuNum']){
|
||||
moreTopMenuItem = '<dd><a menu-title="' + item.title + '" menu-id="' + item.id +'">' + item.title + '</a></dd>';
|
||||
}
|
||||
moreTopMenuIndex++;
|
||||
control += moreTopMenuItem;
|
||||
});
|
||||
control += '</dl></li></ul>';
|
||||
}
|
||||
|
||||
controlItemPe += "</li></dl></ul>";
|
||||
controlPe += controlItemPe;
|
||||
layui.$("#" + option.control).html(control);
|
||||
layui.$("#" + option.control).append(controlPe);
|
||||
layui.$("#" + option.elem).html(menu);
|
||||
// 绑定顶部菜单点击事件
|
||||
layui.$(document).off("click", "#" + option.control + " .laytp-nav-control [menu-id]").on("click", "#" + option.control + " .laytp-nav-control [menu-id]", function() {
|
||||
// layui.$("#" + option.control + " .laytp-nav-control").on("click", "[menu-id]", function() {
|
||||
layui.$("#" + option.elem).find(".laytp-nav-tree").css({
|
||||
display: 'none'
|
||||
});
|
||||
layui.$("#" + option.elem).find(".laytp-nav-tree[menu-id='" + layui.$(this).attr("menu-id") + "']").css({
|
||||
display: 'block'
|
||||
});
|
||||
layui.$("#" + option.control).find(".pe-title").html(layui.$(this).attr("menu-title"));
|
||||
layui.$("#" + option.control).find("");
|
||||
option.change(layui.$(this).attr("menu-id"), layui.$(this).attr("menu-title"), layui.$(this).attr("menu-href"));
|
||||
// 自动点击左侧的第一个菜单
|
||||
var s = layui.$("a:first",layui.$("#" + option.elem).find("ul[style='display: block;']")).parent();
|
||||
if(s.hasClass("layui-nav-itemed")){
|
||||
recursionFindA(s);
|
||||
}else{
|
||||
layui.$("a:first",layui.$("#" + option.elem).find("ul[style='display: block;']")).click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 点击顶部菜单,自动点击左侧菜单,当菜单已经是展开状态时,要递归找到最低级别的A标签进行点击
|
||||
function recursionFindA(obj){
|
||||
var dlObj = layui.$("dl:first", obj);
|
||||
if(dlObj.length === 0){
|
||||
layui.$("a:first", obj).click();
|
||||
}else{
|
||||
recursionFindA(layui.$("dd:first",dlObj));
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载子菜单 (递归)*/
|
||||
function loadchild(obj) {
|
||||
// 判 单 是 否 是 菜 单, 如 果 是 菜 单 直 接 返 回
|
||||
if (obj.type == 1) {
|
||||
return "";
|
||||
}
|
||||
// 创 建 子 菜 单 结 构
|
||||
var content = '<dl class="layui-nav-child">';
|
||||
// 如 果 嵌 套 不 等 于 空
|
||||
if (obj.children != null && obj.children.length > 0) {
|
||||
// 遍 历 子 项 目
|
||||
$.each(obj.children, function(i, note) {
|
||||
// 创 建 子 项 结 构
|
||||
content += '<dd>';
|
||||
var href = "javascript:;";
|
||||
var target = "";
|
||||
var className = "site-demo-active";
|
||||
if (note.openType == "_blank" && note.type == 1) {
|
||||
href = note.href;
|
||||
target = "target='_blank'";
|
||||
className = "";
|
||||
}
|
||||
// 判 断 子 项 类 型
|
||||
if (note.type == 0) {
|
||||
// 创 建 目 录 结 构
|
||||
content += '<a href="' + href + '" doc-type="' + note.docType + '" menu-id="' + note.id +
|
||||
'"><i class="' + note.icon + '"></i><span>' + note.title + '</span></a>';
|
||||
} else if (note.type == 1) {
|
||||
// 创 建 菜 单 结 构
|
||||
content += '<a class="' + className + '" doc-type="' + note.docType + '" menu-id="' + note.id + '" menu-title="' + note.title + '" href="' + href +
|
||||
'" ><i class="' + note.icon + '"></i><span>' + note.title + '</span></a>';
|
||||
}
|
||||
// 加 载 子 项 目 录
|
||||
content += loadchild(note);
|
||||
// 结 束 当 前 子 菜 单
|
||||
content += '</dd>';
|
||||
});
|
||||
// 封 装
|
||||
} else {
|
||||
content += '<div class="toast"> 无 内 容 </div>';
|
||||
}
|
||||
content += '</dl>';
|
||||
return content;
|
||||
}
|
||||
|
||||
// 左侧菜单点击事件
|
||||
function downShow(option) {
|
||||
layui.$(document).off("click", "#" + option.elem + " a[menu-type='0']").on("click", "#" + option.elem + " a[menu-type='0']", function() {
|
||||
// layui.$("body #" + option.elem).on("click", "a[menu-type='0']", function() {
|
||||
if (!layui.$("#" + option.elem).is(".laytp-nav-mini")) {
|
||||
var superEle = layui.$(this).parent();
|
||||
var ele = layui.$(this).next('.layui-nav-child');
|
||||
var heights = ele.children("dd").length * 48;
|
||||
|
||||
if (superEle.is(".layui-nav-itemed")) {
|
||||
if (option.accordion) {
|
||||
superEle.parent().find(".layui-nav-itemed").removeClass("layui-nav-itemed");
|
||||
superEle.addClass("layui-nav-itemed");
|
||||
//自动点击第一个子菜单
|
||||
layui.$("a:first",ele).click();
|
||||
}
|
||||
ele.height(0);
|
||||
ele.animate({
|
||||
height: heights + "px"
|
||||
}, 200, function() {
|
||||
ele.css({
|
||||
height: "auto"
|
||||
});
|
||||
});
|
||||
} else {
|
||||
superEle.addClass("layui-nav-itemed");
|
||||
ele.animate({
|
||||
height: "0px"
|
||||
}, 200, function() {
|
||||
ele.css({
|
||||
height: "auto"
|
||||
});
|
||||
superEle.removeClass("layui-nav-itemed");
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 二 级 悬 浮 菜 单*/
|
||||
function isHoverMenu(b, option) {
|
||||
if (b) {
|
||||
layui.$("#" + option.elem + ".laytp-nav-mini .layui-nav-item,#" + option.elem + ".laytp-nav-mini dd").hover(function(e) {
|
||||
e.stopPropagation();
|
||||
var _this = layui.$(this);
|
||||
_this.siblings().find(".layui-nav-child")
|
||||
.removeClass("layui-nav-hover").css({
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
_this.children(".layui-nav-child").addClass("layui-nav-hover");
|
||||
_this.closest('.layui-nav-item').data('time') && clearTimeout(_this.closest('.layui-nav-item').data('time'));
|
||||
var height = layui.$(window).height();
|
||||
var topLength = _this.offset().top;
|
||||
var thisHeight = _this.children(".layui-nav-child").height();
|
||||
if ((thisHeight + topLength) > height) {
|
||||
topLength = height - thisHeight - 10;
|
||||
}
|
||||
var left = _this.offset().left + 60;
|
||||
if (!_this.hasClass("layui-nav-item")) {
|
||||
left = _this.offset().left + _this.width();
|
||||
}
|
||||
_this.children(".layui-nav-child").offset({
|
||||
top: topLength,
|
||||
left: left + 3
|
||||
});
|
||||
}, function(e) {
|
||||
e.stopPropagation();
|
||||
var _this = layui.$(this);
|
||||
_this.closest('.layui-nav-item').data('time', setTimeout(function() {
|
||||
_this.closest('.layui-nav-item')
|
||||
.find(".layui-nav-child")
|
||||
.removeClass("layui-nav-hover")
|
||||
.css({
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
}, 50));
|
||||
})
|
||||
} else {
|
||||
layui.$("#" + option.elem + " .layui-nav-item").off('mouseenter').unbind('mouseleave');
|
||||
layui.$("#" + option.elem + " dd").off('mouseenter').unbind('mouseleave');
|
||||
}
|
||||
}
|
||||
|
||||
exports(MOD_NAME, new laytpApiMenu());
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
layui.use(["laytp"], function () {
|
||||
const funController = {};
|
||||
//静态页面地址前缀
|
||||
window.htmlPrefix = facade.compatibleHtmlPath("/plugin/apidoc/");
|
||||
//后端接口地址前缀
|
||||
window.apiPrefix = facade.compatibleApiRoute("/plugin/apidoc/index/");
|
||||
|
||||
//表格渲染
|
||||
funController.tableRender = function (where, page) {
|
||||
layui.table.render({
|
||||
elem: "#laytp-table"
|
||||
, id: "laytp-table"
|
||||
, url: facade.url("/plugin/apidoc/index/index",{paging:1})
|
||||
, toolbar: "#default-toolbar"
|
||||
, defaultToolbar: [{
|
||||
title: '刷新',
|
||||
layEvent: 'refresh',
|
||||
icon: 'layui-icon-refresh',
|
||||
}, 'filter', 'print', 'exports']
|
||||
, where: where
|
||||
, method: "GET"
|
||||
, cellMinWidth: 120
|
||||
, skin: 'line'
|
||||
, loading: false
|
||||
, page: {
|
||||
curr: page
|
||||
}
|
||||
, parseData: function (res) { //res 即为原始返回的数据
|
||||
return facade.parseTableData(res, true);
|
||||
}
|
||||
, done: function(){
|
||||
layui.laytpTable.done();
|
||||
}
|
||||
, cols: [[
|
||||
{type: 'checkbox', fixed: 'left'}
|
||||
, {field: 'id', title: 'ID', align: 'center', width: 80, fixed: 'left'}
|
||||
, {field: 'title', title: '标题', align: 'center'}
|
||||
, {field: 'des', title: '描述', align: 'center'}
|
||||
, {field: 'create_time', title: '创建时间', align: 'center'}
|
||||
, {field:'operation',title:'操作',align:'center',toolbar:'#default-bar',width:150,fixed:'right'}
|
||||
]]
|
||||
});
|
||||
|
||||
//监听数据表格顶部左侧按钮点击事件
|
||||
layui.table.on("toolbar(laytp-table)", function (obj) {
|
||||
//默认按钮点击事件,包括添加按钮和回收站按钮
|
||||
var defaultTableToolbar = layui.context.get("defaultTableToolbar");
|
||||
if (defaultTableToolbar.indexOf(obj.event) !== -1) {
|
||||
laytp.tableToolbar(obj);
|
||||
//其他自定义按钮点击事件
|
||||
} else {
|
||||
//自定义按钮点击事件
|
||||
switch (obj.event) {
|
||||
//生成Api文档
|
||||
case "create":
|
||||
facade.ajax({
|
||||
route: "/plugin/apidoc/index/create",
|
||||
showLoading: true
|
||||
});
|
||||
break;
|
||||
//查看Api文档
|
||||
case "open":
|
||||
window.open(facade.getAdminApiDomain() + "/api.html");
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//监听数据表格[操作列]按钮点击事件
|
||||
layui.table.on("tool(laytp-table)", function (obj) {
|
||||
var defaultTableTool = layui.context.get("defaultTableTool");
|
||||
if (defaultTableTool.indexOf(obj.event) !== -1) {
|
||||
laytp.tableTool(obj);
|
||||
} else {
|
||||
// //自定义按钮点击事件
|
||||
// switch(obj.event){
|
||||
// //自定义按钮点击事件
|
||||
// case "":
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
funController.tableRender();
|
||||
|
||||
window.funController = funController;
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
layui.use(['laytp'], function () {
|
||||
const funRecycleController = {};
|
||||
//静态页面地址前缀
|
||||
window.htmlPrefix = facade.compatibleHtmlPath("/plugin/apidoc/");
|
||||
//后端接口地址前缀
|
||||
window.apiPrefix = facade.compatibleApiRoute("/plugin/apidoc/index/");
|
||||
|
||||
//表格渲染
|
||||
funRecycleController.tableRender = function (where, page) {
|
||||
layui.table.render({
|
||||
elem: "#laytp-recycle-table"
|
||||
, id: "laytp-recycle-table"
|
||||
, url: facade.url("/plugin/apidoc/index/recycle")
|
||||
, toolbar: "#recycle-default-toolbar"
|
||||
, defaultToolbar: [{
|
||||
title: '刷新',
|
||||
layEvent: 'recycle-refresh',
|
||||
icon: 'layui-icon-refresh',
|
||||
}, 'filter', 'print', 'exports']
|
||||
, where: where
|
||||
, method: "GET"
|
||||
, cellMinWidth: 80
|
||||
, skin: 'line'
|
||||
, loading: false
|
||||
, page: {
|
||||
curr: page
|
||||
}
|
||||
, parseData: function (res) { //res 即为原始返回的数据
|
||||
return facade.parseTableData(res, true);
|
||||
}
|
||||
, cols: [[ //表头
|
||||
{type: 'checkbox', fixed: 'left'}
|
||||
, {field: 'id', title: 'ID', align: 'center', width: 80, fixed: 'left'}
|
||||
, {field: 'title', title: '标题', align: 'center'}
|
||||
, {field: 'des', title: '描述', align: 'center'}
|
||||
, {field: 'create_time', title: '创建时间', align: 'center'}
|
||||
, {field:'operation',title:'操作',align:'center',toolbar:'#recycle-default-bar',width:150,fixed:'right'}
|
||||
]]
|
||||
});
|
||||
|
||||
//监听数据表格顶部左侧按钮点击事件
|
||||
layui.table.on("toolbar(laytp-recycle-table)", function (obj) {
|
||||
var defaultTableToolbar = layui.context.get("defaultTableToolbar");
|
||||
if (defaultTableToolbar.indexOf(obj.event) !== -1) {
|
||||
//默认按钮点击事件
|
||||
laytp.tableToolbar(obj);
|
||||
} else {
|
||||
// //自定义按钮点击事件
|
||||
// switch(obj.event){
|
||||
// //自定义按钮点击事件
|
||||
// case "":
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
//监听数据表格[操作列]按钮点击事件
|
||||
layui.table.on('tool(laytp-recycle-table)', function (obj) {
|
||||
var defaultTableTool = layui.context.get("defaultTableTool");
|
||||
if (defaultTableTool.indexOf(obj.event) !== -1) {
|
||||
laytp.tableTool(obj);
|
||||
} else {
|
||||
// //自定义按钮
|
||||
// switch(obj.event){
|
||||
// //自定义按钮点击事件
|
||||
// case '':
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
funRecycleController.tableRender();
|
||||
|
||||
window.funRecycleController = funRecycleController;
|
||||
});
|
||||
Reference in New Issue
Block a user