新增自定义插件API文档生成

新增自定义插件API文档生成
新增登录和鉴权状态显示
新增自定义测试提交参数
pull/276/head
Karson 2020-12-21 11:43:16 +08:00
parent dd3d6f3172
commit 39c6720cfc
6 changed files with 1078 additions and 175 deletions

View File

@ -23,9 +23,10 @@ class Api extends Command
->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html')
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false)
->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'])
->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name'])
->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null)
->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn')
->addOption('addon', 'a', Option::VALUE_OPTIONAL, 'addon name', null)
->addOption('controller', 'r', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name', null)
->setDescription('Build Api document from controller'); ->setDescription('Build Api document from controller');
} }
@ -58,12 +59,21 @@ class Api extends Command
$classes = $input->getOption('class'); $classes = $input->getOption('class');
// 标题 // 标题
$title = $input->getOption('title'); $title = $input->getOption('title');
// 作者
$author = $input->getOption('author');
// 模块 // 模块
$module = $input->getOption('module'); $module = $input->getOption('module');
// 插件
$addon = $input->getOption('addon');
$moduleDir = APP_PATH . $module . DS; $moduleDir = $addonDir = '';
if ($addon) {
$addonInfo = get_addon_info($addon);
if (!$addonInfo) {
throw new Exception('addon not found');
}
$moduleDir = ADDON_PATH . $addon . DS;
} else {
$moduleDir = APP_PATH . $module . DS;
}
if (!is_dir($moduleDir)) { if (!is_dir($moduleDir)) {
throw new Exception('module not found'); throw new Exception('module not found');
} }
@ -81,31 +91,44 @@ class Api extends Command
} }
} }
$controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; //控制器名
$files = new \RecursiveIteratorIterator( $controller = $input->getOption('controller') ?: [];
new \RecursiveDirectoryIterator($controllerDir), if (!$controller) {
\RecursiveIteratorIterator::LEAVES_ONLY $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS;
); $files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($controllerDir),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) { foreach ($files as $name => $file) {
if (!$file->isDir() && $file->getExtension() == 'php') { if (!$file->isDir() && $file->getExtension() == 'php') {
$filePath = $file->getRealPath(); $filePath = $file->getRealPath();
$classes[] = $this->get_class_from_file($filePath);
}
}
} else {
foreach ($controller as $index => $item) {
$filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php';
$classes[] = $this->get_class_from_file($filePath); $classes[] = $this->get_class_from_file($filePath);
} }
} }
$classes = array_unique(array_filter($classes)); $classes = array_unique(array_filter($classes));
$config = [ $config = [
'sitename' => config('site.name'), 'sitename' => config('site.name'),
'title' => $title, 'title' => $title,
'author' => $author, 'author' => config('site.name'),
'description' => '', 'description' => '',
'apiurl' => $url, 'apiurl' => $url,
'language' => $language, 'language' => $language,
]; ];
$builder = new Builder($classes); try {
$content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); $builder = new Builder($classes);
$content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]);
} catch (\Exception $e) {
print_r($e);
}
if (!file_put_contents($output_file, $content)) { if (!file_put_contents($output_file, $content)) {
throw new Exception('Cannot save the content to ' . $output_file); throw new Exception('Cannot save the content to ' . $output_file);
} }

View File

@ -16,6 +16,9 @@ return [
'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中', 'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中',
'Apiurltips' => 'API接口URL', 'Apiurltips' => 'API接口URL',
'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中', 'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中',
'Authorization' => '权限',
'NeedLogin' => '登录',
'NeedRight' => '鉴权',
'ReturnHeaders' => '响应头', 'ReturnHeaders' => '响应头',
'ReturnParameters' => '返回参数', 'ReturnParameters' => '返回参数',
'Response' => '响应输出', 'Response' => '响应输出',

View File

@ -43,9 +43,11 @@ class Builder
continue; continue;
} }
Extractor::getClassMethodAnnotations($class); Extractor::getClassMethodAnnotations($class);
//Extractor::getClassPropertyValues($class);
} }
$allClassAnnotation = Extractor::getAllClassAnnotations(); $allClassAnnotation = Extractor::getAllClassAnnotations();
$allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations(); $allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations();
//$allClassPropertyValue = Extractor::getAllClassPropertyValues();
// foreach ($allClassMethodAnnotation as $className => &$methods) { // foreach ($allClassMethodAnnotation as $className => &$methods) {
// foreach ($methods as &$method) { // foreach ($methods as &$method) {
@ -162,10 +164,12 @@ class Builder
list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations(); list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations();
$sectorArr = []; $sectorArr = [];
foreach ($allClassAnnotations as $index => $allClassAnnotation) { foreach ($allClassAnnotations as $index => &$allClassAnnotation) {
$sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0]; $sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0];
$sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0; $sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0;
} }
unset($allClassAnnotation);
arsort($sectorArr); arsort($sectorArr);
$routes = include_once CONF_PATH . 'route.php'; $routes = include_once CONF_PATH . 'route.php';
$subdomain = false; $subdomain = false;
@ -175,7 +179,7 @@ class Builder
$counter = 0; $counter = 0;
$section = null; $section = null;
$weigh = 0; $weigh = 0;
$docslist = []; $docsList = [];
foreach ($allClassMethodAnnotations as $class => $methods) { foreach ($allClassMethodAnnotations as $class => $methods) {
foreach ($methods as $name => $docs) { foreach ($methods as $name => $docs) {
if (isset($docs['ApiSector'][0])) { if (isset($docs['ApiSector'][0])) {
@ -190,28 +194,30 @@ class Builder
if ($subdomain) { if ($subdomain) {
$route = substr($route, 4); $route = substr($route, 4);
} }
$docslist[$section][$class . $name] = [ $docsList[$section][$name] = [
'id' => $counter, 'id' => $counter,
'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0], 'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0],
'method_label' => $this->generateBadgeForMethod($docs), 'methodLabel' => $this->generateBadgeForMethod($docs),
'section' => $section, 'section' => $section,
'route' => $route, 'route' => $route,
'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0], 'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0],
'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0], '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] : '', 'body' => isset($docs['ApiBody'][0]) ? is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0] : '',
'headerslist' => $this->generateHeadersTemplate($docs), 'headersList' => $this->generateHeadersTemplate($docs),
'paramslist' => $this->generateParamsTemplate($docs), 'paramsList' => $this->generateParamsTemplate($docs),
'returnheaderslist' => $this->generateReturnHeadersTemplate($docs), 'returnHeadersList' => $this->generateReturnHeadersTemplate($docs),
'returnparamslist' => $this->generateReturnParamsTemplate($docs), 'returnParamsList' => $this->generateReturnParamsTemplate($docs),
'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0], 'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0],
'return' => isset($docs['ApiReturn']) ? is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0] : '', 'return' => isset($docs['ApiReturn']) ? is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0] : '',
'needLogin' => $docs['ApiPermissionLogin'][0],
'needRight' => $docs['ApiPermissionRight'][0],
]; ];
$counter++; $counter++;
} }
} }
//重建排序 //重建排序
foreach ($docslist as $index => &$methods) { foreach ($docsList as $index => &$methods) {
$methodSectorArr = []; $methodSectorArr = [];
foreach ($methods as $name => $method) { foreach ($methods as $name => $method) {
$methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0; $methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0;
@ -219,9 +225,8 @@ class Builder
arsort($methodSectorArr); arsort($methodSectorArr);
$methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods); $methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods);
} }
$docslist = array_merge(array_flip(array_keys($sectorArr)), $docslist); $docsList = array_merge(array_flip(array_keys($sectorArr)), $docsList);
$docslist = array_filter($docslist , function($v) {return is_array($v) ; }) ; return $docsList;
return $docslist;
} }
public function getView() public function getView()
@ -237,8 +242,8 @@ class Builder
*/ */
public function render($template, $vars = []) public function render($template, $vars = [])
{ {
$docslist = $this->parse(); $docsList = $this->parse();
return $this->view->display(file_get_contents($template), array_merge($vars, ['docslist' => $docslist])); return $this->view->display(file_get_contents($template), array_merge($vars, ['docsList' => $docsList]));
} }
} }

View File

@ -24,6 +24,8 @@ class Extractor
private static $classMethodAnnotationCache; private static $classMethodAnnotationCache;
private static $classPropertyValueCache;
/** /**
* Indicates that annotations should has strict behavior, 'false' by default * Indicates that annotations should has strict behavior, 'false' by default
* @var boolean * @var boolean
@ -66,14 +68,16 @@ class Extractor
/** /**
* Gets all anotations with pattern @SomeAnnotation() from a given class * Gets all anotations with pattern @SomeAnnotation() from a given class
* *
* @param string $className class name to get annotations * @param string $className class name to get annotations
* @return array self::$classAnnotationCache all annotated elements * @return array self::$classAnnotationCache all annotated elements
*/ */
public static function getClassAnnotations($className) public static function getClassAnnotations($className)
{ {
if (!isset(self::$classAnnotationCache[$className])) { if (!isset(self::$classAnnotationCache[$className])) {
$class = new \ReflectionClass($className); $class = new \ReflectionClass($className);
self::$classAnnotationCache[$className] = self::parseAnnotations($class->getDocComment()); $annotationArr = self::parseAnnotations($class->getDocComment());
$annotationArr['ApiTitle'] = !isset($annotationArr['ApiTitle'][0]) || !trim($annotationArr['ApiTitle'][0]) ? [$class->getShortName()] : $annotationArr['ApiTitle'];
self::$classAnnotationCache[$className] = $annotationArr;
} }
return self::$classAnnotationCache[$className]; return self::$classAnnotationCache[$className];
@ -96,6 +100,17 @@ class Extractor
return self::$classMethodAnnotationCache[$className]; return self::$classMethodAnnotationCache[$className];
} }
public static function getClassPropertyValues($className)
{
$class = new \ReflectionClass($className);
foreach ($class->getProperties() as $object) {
self::$classPropertyValueCache[$className][$object->name] = self::getClassPropertyValue($className, $object->name);
}
return self::$classMethodAnnotationCache[$className];
}
public static function getAllClassAnnotations() public static function getAllClassAnnotations()
{ {
return self::$classAnnotationCache; return self::$classAnnotationCache;
@ -106,11 +121,25 @@ class Extractor
return self::$classMethodAnnotationCache; return self::$classMethodAnnotationCache;
} }
public static function getAllClassPropertyValues()
{
return self::$classPropertyValueCache;
}
public static function getClassPropertyValue($className, $property)
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$reflectionClass = new \ReflectionClass($className);
$reflectionProperty = $reflectionClass->getProperty($property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($reflectionClass->newInstanceWithoutConstructor());
}
/** /**
* Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class
* *
* @param string $className class name * @param string $className class name
* @param string $methodName method name to get annotations * @param string $methodName method name to get annotations
* @return array self::$annotationCache all annotated elements of a method given * @return array self::$annotationCache all annotated elements of a method given
*/ */
public static function getMethodAnnotations($className, $methodName) public static function getMethodAnnotations($className, $methodName)
@ -138,8 +167,8 @@ class Extractor
* Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class
* and instance its abcAnnotation class * and instance its abcAnnotation class
* *
* @param string $className class name * @param string $className class name
* @param string $methodName method name to get annotations * @param string $methodName method name to get annotations
* @return array self::$annotationCache all annotated objects of a method given * @return array self::$annotationCache all annotated objects of a method given
*/ */
public function getMethodAnnotationsObjects($className, $methodName) public function getMethodAnnotationsObjects($className, $methodName)
@ -189,7 +218,11 @@ class Extractor
$methodName = $method->getName(); $methodName = $method->getName();
$methodAnnotations = self::parseAnnotations($docblockMethod); $methodAnnotations = self::parseAnnotations($docblockMethod);
$methodAnnotations['ApiTitle'] = !isset($methodAnnotations['ApiTitle'][0]) || !trim($methodAnnotations['ApiTitle'][0]) ? [$method->getName()] : $methodAnnotations['ApiTitle'];
$classAnnotations = self::parseAnnotations($dockblockClass); $classAnnotations = self::parseAnnotations($dockblockClass);
$classAnnotations['ApiTitle'] = !isset($classAnnotations['ApiTitle'][0]) || !trim($classAnnotations['ApiTitle'][0]) ? [$class->getShortName()] : $classAnnotations['ApiTitle'];
if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') { if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') {
return []; return [];
} }
@ -264,15 +297,15 @@ class Extractor
} }
} }
$methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)]; $methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)];
$methodAnnotations['ApiPermissionRight'] = [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)]; $methodAnnotations['ApiPermissionRight'] = !$methodAnnotations['ApiPermissionLogin'][0] ? false : [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)];
return $methodAnnotations; return $methodAnnotations;
} }
/** /**
* Parse annotations * Parse annotations
* *
* @param string $docblock * @param string $docblock
* @param string $name * @param string $name
* @return array parsed annotations params * @return array parsed annotations params
*/ */
private static function parseCustomAnnotations($docblock, $name = 'param') private static function parseCustomAnnotations($docblock, $name = 'param')
@ -291,7 +324,7 @@ class Extractor
/** /**
* Parse annotations * Parse annotations
* *
* @param string $docblock * @param string $docblock
* @return array parsed annotations params * @return array parsed annotations params
*/ */
private static function parseAnnotations($docblock) private static function parseAnnotations($docblock)
@ -337,7 +370,7 @@ class Extractor
/** /**
* Parse individual annotation arguments * Parse individual annotation arguments
* *
* @param string $content arguments string * @param string $content arguments string
* @return array annotated arguments * @return array annotated arguments
*/ */
private static function parseArgs($content) private static function parseArgs($content)
@ -480,8 +513,8 @@ class Extractor
/** /**
* Try determinate the original type variable of a string * 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 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 * @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 * @return mixed returns the value converted to original type if was possible
*/ */
private static function castValue($val, $trim = false) private static function castValue($val, $trim = false)

View File

@ -27,12 +27,12 @@
font-family: "Roboto", "SF Pro SC", "SF Pro Display", "SF Pro Icons", "PingFang SC", BlinkMacSystemFont, -apple-system, "Segoe UI", "Microsoft Yahei", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif; font-family: "Roboto", "SF Pro SC", "SF Pro Display", "SF Pro Icons", "PingFang SC", BlinkMacSystemFont, -apple-system, "Segoe UI", "Microsoft Yahei", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-weight: 400; font-weight: 400;
} }
h2 { font-size: 1.6em; } h2 { font-size: 1.2em; }
hr { margin-top: 10px; } hr { margin-top: 10px; }
.tab-pane { padding-top: 10px; } .tab-pane { padding-top: 10px; }
.mt0 { margin-top: 0px; } .mt0 { margin-top: 0px; }
.footer { font-size: 12px; color: #666; } .footer { font-size: 12px; color: #666; }
.label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; } .docs-list .label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; }
.string { color: green; } .string { color: green; }
.number { color: darkorange; } .number { color: darkorange; }
.boolean { color: blue; } .boolean { color: blue; }
@ -65,12 +65,24 @@
#sidebar > .list-group > a{ #sidebar > .list-group > a{
text-indent:0; text-indent:0;
} }
#sidebar .child > a .tag{
position: absolute;
right: 10px;
top: 11px;
}
#sidebar .child > a .pull-right{
margin-left:3px;
}
#sidebar .child { #sidebar .child {
border:1px solid #ddd; border:1px solid #ddd;
border-bottom:none; border-bottom:none;
} }
#sidebar .child:last-child {
border-bottom:1px solid #ddd;
}
#sidebar .child > a { #sidebar .child > a {
border:0; border:0;
min-height: 40px;
} }
#sidebar .list-group a.current { #sidebar .list-group a.current {
background:#f5f5f5; background:#f5f5f5;
@ -94,6 +106,9 @@
.label-primary { .label-primary {
background-color: #248aff; background-color: #248aff;
} }
.docs-list .panel .panel-body .table {
margin-bottom: 0;
}
</style> </style>
</head> </head>
@ -138,25 +153,34 @@
<!-- menu --> <!-- menu -->
<div id="sidebar"> <div id="sidebar">
<div class="list-group panel"> <div class="list-group panel">
{foreach name="docslist" id="docs"} {foreach name="docsList" id="docs"}
<a href="#{$key}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a> <a href="#{$key}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a>
<div class="child collapse" id="{$key}"> <div class="child collapse" id="{$key}">
{foreach name="docs" id="api" } {foreach name="docs" id="api" }
<a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title}</a> <a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title}
<span class="tag">
{if $api.needRight}
<span class="label label-danger pull-right"></span>
{/if}
{if $api.needLogin}
<span class="label label-success pull-right noneedlogin"></span>
{/if}
</span>
</a>
{/foreach} {/foreach}
</div> </div>
{/foreach} {/foreach}
</div> </div>
</div> </div>
<div class="panel-group" id="accordion"> <div class="panel-group docs-list" id="accordion">
{foreach name="docslist" id="docs"} {foreach name="docsList" id="docs"}
<h2>{$key}</h2> <h2>{$key}</h2>
<hr> <hr>
{foreach name="docs" id="api" } {foreach name="docs" id="api" }
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading" id="heading-{$api.id}"> <div class="panel-heading" id="heading-{$api.id}">
<h4 class="panel-title"> <h4 class="panel-title">
<span class="label {$api.method_label}">{$api.method|strtoupper}</span> <span class="label {$api.methodLabel}">{$api.method|strtoupper}</span>
<a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></a> <a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></a>
</h4> </h4>
</div> </div>
@ -177,10 +201,27 @@
<div class="well"> <div class="well">
{$api.summary} {$api.summary}
</div> </div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Authorization}</strong></div>
<div class="panel-body">
<table class="table table-hover">
<tbody>
<tr>
<td>{$lang.NeedLogin}</td>
<td>{$api.needLogin?'是':'否'}</td>
</tr>
<tr>
<td>{$lang.NeedRight}</td>
<td>{$api.needRight?'是':'否'}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Headers}</strong></div> <div class="panel-heading"><strong>{$lang.Headers}</strong></div>
<div class="panel-body"> <div class="panel-body">
{if $api.headerslist} {if $api.headersList}
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
@ -191,7 +232,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{foreach name="api['headerslist']" id="header"} {foreach name="api['headersList']" id="header"}
<tr> <tr>
<td>{$header.name}</td> <td>{$header.name}</td>
<td>{$header.type}</td> <td>{$header.type}</td>
@ -209,7 +250,7 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div> <div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
<div class="panel-body"> <div class="panel-body">
{if $api.paramslist} {if $api.paramsList}
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
@ -220,7 +261,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{foreach name="api['paramslist']" id="param"} {foreach name="api['paramsList']" id="param"}
<tr> <tr>
<td>{$param.name}</td> <td>{$param.name}</td>
<td>{$param.type}</td> <td>{$param.type}</td>
@ -246,12 +287,12 @@
<div class="tab-pane" id="sandbox{$api.id}"> <div class="tab-pane" id="sandbox{$api.id}">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
{if $api.headerslist} {if $api.headersList}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Headers}</strong></div> <div class="panel-heading"><strong>{$lang.Headers}</strong></div>
<div class="panel-body"> <div class="panel-body">
<div class="headers"> <div class="headers">
{foreach name="api['headerslist']" id="param"} {foreach name="api['headersList']" id="param"}
<div class="form-group"> <div class="form-group">
<label class="control-label" for="{$param.name}">{$param.name}</label> <label class="control-label" for="{$param.name}">{$param.name}</label>
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}"> <input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}">
@ -262,11 +303,15 @@
</div> </div>
{/if} {/if}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div> <div class="panel-heading"><strong>{$lang.Parameters}</strong>
<div class="pull-right">
<a href="javascript:" class="btn btn-xs btn-info btn-append">追加</a>
</div>
</div>
<div class="panel-body"> <div class="panel-body">
<form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}"> <form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}">
{if $api.paramslist} {if $api.paramsList}
{foreach name="api['paramslist']" id="param"} {foreach name="api['paramsList']" id="param"}
<div class="form-group"> <div class="form-group">
<label class="control-label" for="{$param.name}">{$param.name}</label> <label class="control-label" for="{$param.name}">{$param.name}</label>
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}"> <input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}">
@ -277,7 +322,7 @@
</div> </div>
{/if} {/if}
<div class="form-group"> <div class="form-group form-group-submit">
<button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button> <button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button>
<button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button> <button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button>
</div> </div>
@ -298,7 +343,7 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div> <div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div>
<div class="panel-body"> <div class="panel-body">
{if $api.returnparamslist} {if $api.returnParamsList}
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
@ -308,7 +353,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{foreach name="api['returnparamslist']" id="param"} {foreach name="api['returnParamsList']" id="param"}
<tr> <tr>
<td>{$param.name}</td> <td>{$param.name}</td>
<td>{$param.type}</td> <td>{$param.type}</td>
@ -346,10 +391,10 @@
<div class="row mt0 footer"> <div class="row mt0 footer">
<div class="col-md-6" align="left"> <div class="col-md-6" align="left">
Generated on {:date('Y-m-d H:i:s')}
</div> </div>
<div class="col-md-6" align="right"> <div class="col-md-6" align="right">
<a href="./" target="_blank">{$config.sitename}</a> Generated on {:date('Y-m-d H:i:s')} <a href="./" target="_blank">{$config.sitename}</a>
</div> </div>
</div> </div>
@ -475,7 +520,7 @@
$sample.html('<pre>' + str + '</pre>'); $sample.html('<pre>' + str + '</pre>');
}); });
$('body').on('click', '#save_data', function (e) { $(document).on('click', '#save_data', function (e) {
if (storage) { if (storage) {
storage.setItem('token', $('#token').val()); storage.setItem('token', $('#token').val());
storage.setItem('apiUrl', $('#apiUrl').val()); storage.setItem('apiUrl', $('#apiUrl').val());
@ -483,8 +528,20 @@
alert('Your browser does not support local storage'); alert('Your browser does not support local storage');
} }
}); });
$(document).on('click', '.btn-append', function (e) {
$($("#appendtpl").html()).insertBefore($(this).closest(".panel").find(".form-group-submit"));
return false;
});
$(document).on('click', '.btn-remove', function (e) {
$(this).closest(".form-group").remove();
return false;
});
$(document).on('keyup', '.input-custom-name', function (e) {
$(this).closest(".row").find(".input-custom-value").attr("name", $(this).val());
return false;
});
$('body').on('click', '.send', function (e) { $(document).on('click', '.send', function (e) {
e.preventDefault(); e.preventDefault();
var form = $(this).closest('form'); var form = $(this).closest('form');
//added /g to get all the matched params instead of only first //added /g to get all the matched params instead of only first
@ -577,5 +634,21 @@
}); });
}); });
</script> </script>
<script type="text/html" id="appendtpl">
<div class="form-group">
<label class="control-label">自定义</label>
<div class="row">
<div class="col-xs-4">
<input type="text" class="form-control input-sm input-custom-name" placeholder="名称">
</div>
<div class="col-xs-6">
<input type="text" class="form-control input-sm input-custom-value" placeholder="值">
</div>
<div class="col-xs-2 text-center">
<a href="javascript:" class="btn btn-sm btn-danger btn-remove">删除</a>
</div>
</div>
</div>
</script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff