浏览代码

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

新增自定义插件API文档生成
新增登录和鉴权状态显示
新增自定义测试提交参数
Karson 5 年之前
父节点
当前提交
39c6720cfc

+ 41 - 18
application/admin/command/Api.php

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

+ 3 - 0
application/admin/command/Api/lang/zh-cn.php

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

+ 28 - 23
application/admin/command/Api/library/Builder.php

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

+ 46 - 13
application/admin/command/Api/library/Extractor.php

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

+ 96 - 23
application/admin/command/Api/template/index.html

@@ -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-weight: 400;
             }
-            h2        { font-size: 1.6em; }
+            h2        { font-size: 1.2em; }
             hr        { margin-top: 10px; }
             .tab-pane { padding-top: 10px; }
             .mt0      { margin-top: 0px; }
             .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; }
             .number   { color: darkorange; }
             .boolean  { color: blue; }
@@ -65,12 +65,24 @@
             #sidebar > .list-group > a{
                 text-indent:0;
             }
+            #sidebar .child > a .tag{
+                position: absolute;
+                right: 10px;
+                top: 11px;
+            }
+            #sidebar .child > a .pull-right{
+                margin-left:3px;
+            }
             #sidebar .child {
                 border:1px solid #ddd;
                 border-bottom:none;
             }
+            #sidebar .child:last-child {
+                border-bottom:1px solid #ddd;
+            }
             #sidebar .child > a {
                 border:0;
+                min-height: 40px;
             }
             #sidebar .list-group a.current {
                 background:#f5f5f5;
@@ -94,6 +106,9 @@
             .label-primary {
                 background-color: #248aff;
             }
+            .docs-list .panel .panel-body .table {
+                margin-bottom: 0;
+            }
 
         </style>
     </head>
@@ -138,25 +153,34 @@
             <!-- menu -->
             <div id="sidebar">
                 <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>
                     <div class="child collapse" id="{$key}">
                         {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}
                     </div>
                     {/foreach}
                 </div>
             </div>
-            <div class="panel-group" id="accordion">
-                {foreach name="docslist" id="docs"}
+            <div class="panel-group docs-list" id="accordion">
+                {foreach name="docsList" id="docs"}
                 <h2>{$key}</h2>
                 <hr>
                 {foreach name="docs" id="api" }
                 <div class="panel panel-default">
                     <div class="panel-heading" id="heading-{$api.id}">
                         <h4 class="panel-title">
-                            <span class="label {$api.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>
                         </h4>
                     </div>
@@ -178,9 +202,26 @@
                                         {$api.summary}
                                     </div>
                                     <div class="panel panel-default">
+                                        <div class="panel-heading"><strong>{$lang.Authorization}</strong></div>
+                                        <div class="panel-body">
+                                            <table class="table table-hover">
+                                                <tbody>
+                                                <tr>
+                                                    <td>{$lang.NeedLogin}</td>
+                                                    <td>{$api.needLogin?'是':'否'}</td>
+                                                </tr>
+                                                <tr>
+                                                    <td>{$lang.NeedRight}</td>
+                                                    <td>{$api.needRight?'是':'否'}</td>
+                                                </tr>
+                                                </tbody>
+                                            </table>
+                                        </div>
+                                    </div>
+                                    <div class="panel panel-default">
                                         <div class="panel-heading"><strong>{$lang.Headers}</strong></div>
                                         <div class="panel-body">
-                                            {if $api.headerslist}
+                                            {if $api.headersList}
                                             <table class="table table-hover">
                                                 <thead>
                                                     <tr>
@@ -191,7 +232,7 @@
                                                     </tr>
                                                 </thead>
                                                 <tbody>
-                                                    {foreach name="api['headerslist']" id="header"}
+                                                    {foreach name="api['headersList']" id="header"}
                                                     <tr>
                                                         <td>{$header.name}</td>
                                                         <td>{$header.type}</td>
@@ -209,7 +250,7 @@
                                     <div class="panel panel-default">
                                         <div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
                                         <div class="panel-body">
-                                            {if $api.paramslist}
+                                            {if $api.paramsList}
                                             <table class="table table-hover">
                                                 <thead>
                                                     <tr>
@@ -220,7 +261,7 @@
                                                     </tr>
                                                 </thead>
                                                 <tbody>
-                                                    {foreach name="api['paramslist']" id="param"}
+                                                    {foreach name="api['paramsList']" id="param"}
                                                     <tr>
                                                         <td>{$param.name}</td>
                                                         <td>{$param.type}</td>
@@ -246,12 +287,12 @@
                                 <div class="tab-pane" id="sandbox{$api.id}">
                                     <div class="row">
                                         <div class="col-md-12">
-                                            {if $api.headerslist}
+                                            {if $api.headersList}
                                             <div class="panel panel-default">
                                                 <div class="panel-heading"><strong>{$lang.Headers}</strong></div>
                                                 <div class="panel-body">
                                                     <div class="headers">
-                                                        {foreach name="api['headerslist']" id="param"}
+                                                        {foreach name="api['headersList']" id="param"}
                                                         <div class="form-group">
                                                             <label class="control-label" for="{$param.name}">{$param.name}</label>
                                                             <input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}">
@@ -262,11 +303,15 @@
                                             </div>
                                             {/if}
                                             <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">
                                                     <form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}">
-                                                        {if $api.paramslist}
-                                                        {foreach name="api['paramslist']" id="param"}
+                                                        {if $api.paramsList}
+                                                        {foreach name="api['paramsList']" id="param"}
                                                         <div class="form-group">
                                                             <label class="control-label" for="{$param.name}">{$param.name}</label>
                                                             <input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}">
@@ -277,7 +322,7 @@
                                                         </div>
                                                         {/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="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button>
                                                         </div>
@@ -298,7 +343,7 @@
                                             <div class="panel panel-default">
                                                 <div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div>
                                                 <div class="panel-body">
-                                                    {if $api.returnparamslist}
+                                                    {if $api.returnParamsList}
                                                     <table class="table table-hover">
                                                         <thead>
                                                             <tr>
@@ -308,7 +353,7 @@
                                                             </tr>
                                                         </thead>
                                                         <tbody>
-                                                            {foreach name="api['returnparamslist']" id="param"}
+                                                            {foreach name="api['returnParamsList']" id="param"}
                                                             <tr>
                                                                 <td>{$param.name}</td>
                                                                 <td>{$param.type}</td>
@@ -346,10 +391,10 @@
 
             <div class="row mt0 footer">
                 <div class="col-md-6" align="left">
-                    Generated on {:date('Y-m-d H:i:s')}
+
                 </div>
                 <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>
 
@@ -475,7 +520,7 @@
                     $sample.html('<pre>' + str + '</pre>');
                 });
 
-                $('body').on('click', '#save_data', function (e) {
+                $(document).on('click', '#save_data', function (e) {
                     if (storage) {
                         storage.setItem('token', $('#token').val());
                         storage.setItem('apiUrl', $('#apiUrl').val());
@@ -483,8 +528,20 @@
                         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();
                     var form = $(this).closest('form');
                     //added /g to get all the matched params instead of only first
@@ -577,5 +634,21 @@
                 });
             });
         </script>
+        <script type="text/html" id="appendtpl">
+            <div class="form-group">
+                <label class="control-label">自定义</label>
+                <div class="row">
+                    <div class="col-xs-4">
+                        <input type="text" class="form-control input-sm input-custom-name" placeholder="名称">
+                    </div>
+                    <div class="col-xs-6">
+                        <input type="text" class="form-control input-sm input-custom-value" placeholder="值">
+                    </div>
+                    <div class="col-xs-2 text-center">
+                        <a href="javascript:" class="btn btn-sm btn-danger btn-remove">删除</a>
+                    </div>
+                </div>
+            </div>
+        </script>
     </body>
 </html>

文件差异内容过多而无法显示
+ 866 - 100
public/api.html