From 9753d94bc033f1417fb75f927225e3cfb7a128a5 Mon Sep 17 00:00:00 2001 From: Karson Date: Sun, 5 Aug 2018 21:59:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9ECRUD=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=AD=9B=E9=80=89=E9=80=89=E9=A1=B9=E5=8D=A1?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20=E6=96=B0=E5=A2=9E=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E7=94=9F=E6=88=90API=E6=96=87=E6=A1=A3=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=8A=9F=E8=83=BD=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E9=BB=98=E8=AE=A4=E5=8A=A0=E8=BD=BD=E9=A1=B5?= =?UTF-8?q?=E7=9A=84=E5=93=8D=E5=BA=94=E9=80=9F=E5=BA=A6=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BA=8C=E7=BA=A7=E6=A0=8F=E7=9B=AE=E6=8C=BA=E6=8B=BD?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E5=8A=9F=E8=83=BD=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E9=83=A8=E5=88=86=E5=88=97=E8=A1=A8=E4=BC=9A?= =?UTF-8?q?=E8=A2=AB=E6=88=AA=E6=96=AD=E7=9A=84BUG=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E7=94=9F=E6=88=90API=E6=96=87=E6=A1=A3@ApiIn?= =?UTF-8?q?ternal=E4=B8=8D=E7=94=9F=E6=95=88=E7=9A=84BUG=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=B8=80=E9=94=AE=E7=94=9F=E6=88=90API=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=A9=BA=E7=B1=BB=E7=9A=84BUG=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E7=94=9F=E6=88=90CRUD=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E6=AD=A3=E5=9C=A8=E5=AD=97=E6=AE=B5=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/admin/command/Api.php | 1 + application/admin/command/Api/lang/zh-cn.php | 1 + .../admin/command/Api/library/Builder.php | 55 +- .../admin/command/Api/library/Extractor.php | 243 +-- .../admin/command/Api/template/index.html | 91 +- application/admin/command/Crud.php | 20 + .../command/Crud/stubs/html/heading-html.stub | 10 + .../command/Crud/stubs/html/heading-js.stub | 18 + .../admin/command/Crud/stubs/index.stub | 2 +- .../admin/command/Crud/stubs/javascript.stub | 2 + .../command/Crud/stubs/mixins/checkbox.stub | 2 +- .../command/Crud/stubs/mixins/datetime.stub | 2 +- .../command/Crud/stubs/mixins/multiple.stub | 2 +- .../command/Crud/stubs/mixins/radio.stub | 2 +- .../command/Crud/stubs/mixins/select.stub | 2 +- application/admin/controller/Ajax.php | 60 +- application/admin/controller/Category.php | 3 + application/admin/controller/Index.php | 4 +- application/admin/controller/auth/Admin.php | 3 + .../admin/controller/auth/Adminlog.php | 3 + application/admin/controller/auth/Group.php | 3 + application/admin/controller/auth/Rule.php | 3 + .../admin/controller/general/Attachment.php | 3 + .../admin/controller/general/Config.php | 3 + application/admin/controller/user/Group.php | 2 +- application/admin/controller/user/Rule.php | 3 +- application/admin/controller/user/User.php | 3 +- application/admin/lang/zh-cn.php | 2 + application/admin/library/Auth.php | 18 +- application/admin/library/traits/Backend.php | 6 + application/admin/view/category/index.html | 2 +- application/admin/view/common/header.html | 6 + application/admin/view/index/index.html | 13 +- application/admin/view/user/group/index.html | 2 +- application/api/controller/Demo.php | 32 +- application/common/controller/Backend.php | 2 +- bower.json | 2 +- public/api.html | 1669 ++++++++++------- public/assets/css/backend.css | 11 +- public/assets/css/backend.min.css | 2 +- public/assets/js/adminlte.js | 137 +- public/assets/js/backend/category.js | 2 +- public/assets/js/backend/dashboard.js | 53 +- public/assets/js/backend/index.js | 5 - public/assets/js/require-backend.min.js | 28 +- public/assets/less/backend.less | 12 +- 46 files changed, 1526 insertions(+), 1024 deletions(-) create mode 100644 application/admin/command/Crud/stubs/html/heading-html.stub create mode 100644 application/admin/command/Crud/stubs/html/heading-js.stub diff --git a/application/admin/command/Api.php b/application/admin/command/Api.php index 62cbbf1b..9079558f 100644 --- a/application/admin/command/Api.php +++ b/application/admin/command/Api.php @@ -93,6 +93,7 @@ class Api extends Command $classes[] = $this->get_class_from_file($filePath); } } + $classes = array_unique(array_filter($classes)); $config = [ 'title' => $title, diff --git a/application/admin/command/Api/lang/zh-cn.php b/application/admin/command/Api/lang/zh-cn.php index 3bec9919..2154aa4d 100644 --- a/application/admin/command/Api/lang/zh-cn.php +++ b/application/admin/command/Api/lang/zh-cn.php @@ -12,6 +12,7 @@ return [ 'Required' => '必选', 'Description' => '描述', 'Send' => '提交', + 'Reset' => '重置', 'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中', 'Apiurltips' => 'API接口URL', 'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中', diff --git a/application/admin/command/Api/library/Builder.php b/application/admin/command/Api/library/Builder.php index 264610aa..d51eeadb 100755 --- a/application/admin/command/Api/library/Builder.php +++ b/application/admin/command/Api/library/Builder.php @@ -14,18 +14,18 @@ class Builder /** * - * @var \think\View + * @var \think\View */ public $view = null; /** * parse classes - * @var array + * @var array */ protected $classes = []; /** - * + * * @param array $classes */ public function __construct($classes = []) @@ -37,8 +37,12 @@ class Builder protected function extractAnnotations() { $st_output = []; - foreach ($this->classes as $class) - { + foreach ($this->classes as $class) { + $classAnnotation = Extractor::getClassAnnotations($class); + // 如果忽略 + if (isset($classAnnotation['ApiInternal'])) { + continue; + } $st_output[] = Extractor::getAllClassAnnotations($class); } return end($st_output); @@ -46,14 +50,12 @@ class Builder protected function generateHeadersTemplate($docs) { - if (!isset($docs['ApiHeaders'])) - { + if (!isset($docs['ApiHeaders'])) { return []; } $headerslist = array(); - foreach ($docs['ApiHeaders'] as $params) - { + foreach ($docs['ApiHeaders'] as $params) { $tr = array( 'name' => $params['name'], 'type' => $params['type'], @@ -69,14 +71,12 @@ class Builder protected function generateParamsTemplate($docs) { - if (!isset($docs['ApiParams'])) - { + if (!isset($docs['ApiParams'])) { return []; } $paramslist = array(); - foreach ($docs['ApiParams'] as $params) - { + foreach ($docs['ApiParams'] as $params) { $tr = array( 'name' => $params['name'], 'type' => isset($params['type']) ? $params['type'] : 'string', @@ -92,14 +92,12 @@ class Builder protected function generateReturnHeadersTemplate($docs) { - if (!isset($docs['ApiReturnHeaders'])) - { + if (!isset($docs['ApiReturnHeaders'])) { return []; } $headerslist = array(); - foreach ($docs['ApiReturnHeaders'] as $params) - { + foreach ($docs['ApiReturnHeaders'] as $params) { $tr = array( 'name' => $params['name'], 'type' => 'string', @@ -115,14 +113,12 @@ class Builder protected function generateReturnParamsTemplate($st_params) { - if (!isset($st_params['ApiReturnParams'])) - { + if (!isset($st_params['ApiReturnParams'])) { return []; } $paramslist = array(); - foreach ($st_params['ApiReturnParams'] as $params) - { + foreach ($st_params['ApiReturnParams'] as $params) { $tr = array( 'name' => $params['name'], 'type' => isset($params['type']) ? $params['type'] : 'string', @@ -157,20 +153,14 @@ class Builder $counter = 0; $section = null; $docslist = []; - foreach ($annotations as $class => $methods) - { - foreach ($methods as $name => $docs) - { - if (isset($docs['ApiSector'][0])) - { + 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 - { + } else { $section = $class; } - if (0 === count($docs)) - { + if (0 === count($docs)) { continue; } @@ -180,6 +170,7 @@ class Builder 'method_label' => $this->generateBadgeForMethod($docs), 'section' => $section, 'route' => is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][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], 'body' => isset($docs['ApiBody'][0]) ? is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0] : '', 'headerslist' => $this->generateHeadersTemplate($docs), diff --git a/application/admin/command/Api/library/Extractor.php b/application/admin/command/Api/library/Extractor.php index 1dc62e34..bbee48fe 100644 --- a/application/admin/command/Api/library/Extractor.php +++ b/application/admin/command/Api/library/Extractor.php @@ -38,7 +38,7 @@ class Extractor */ public function setStrict($value) { - $this->strict = (bool) $value; + $this->strict = (bool)$value; } /** @@ -67,8 +67,7 @@ class Extractor */ public static function getClassAnnotations($className) { - if (!isset(self::$annotationCache[$className])) - { + if (!isset(self::$annotationCache[$className])) { $class = new \ReflectionClass($className); self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment()); } @@ -80,8 +79,7 @@ class Extractor { $class = new \ReflectionClass($className); - foreach ($class->getMethods() as $object) - { + foreach ($class->getMethods() as $object) { self::$annotationCache['annotations'][$className][$object->name] = self::getMethodAnnotations($className, $object->name); } @@ -91,29 +89,22 @@ class Extractor /** * 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 * @return array self::$annotationCache all annotated elements of a method given */ public static function getMethodAnnotations($className, $methodName) { - if (!isset(self::$annotationCache[$className . '::' . $methodName])) - { - try - { + if (!isset(self::$annotationCache[$className . '::' . $methodName])) { + try { $method = new \ReflectionMethod($className, $methodName); $class = new \ReflectionClass($className); - if (!$method->isPublic() || $method->isConstructor()) - { + if (!$method->isPublic() || $method->isConstructor()) { $annotations = array(); - } - else - { + } else { $annotations = self::consolidateAnnotations($method, $class); } - } - catch (\ReflectionException $e) - { + } catch (\ReflectionException $e) { $annotations = array(); } @@ -127,7 +118,7 @@ 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 $className class name * @param string $methodName method name to get annotations * @return array self::$annotationCache all annotated objects of a method given */ @@ -138,42 +129,31 @@ class Extractor $i = 0; - foreach ($annotations as $annotationClass => $listParams) - { + 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) - { + if (!class_exists($class)) { + if ($this->strict) { throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class)); - } - else - { + } else { // silent skip & continue continue; } } - if (empty($objects[$annotationClass])) - { + if (empty($objects[$annotationClass])) { $objects[$annotationClass] = new $class(); } - foreach ($listParams as $params) - { - if (is_array($params)) - { - foreach ($params as $key => $value) - { + foreach ($listParams as $params) { + if (is_array($params)) { + foreach ($params as $key => $value) { $objects[$annotationClass]->set($key, $value); } - } - else - { + } else { $objects[$annotationClass]->set($i++, $params); } } @@ -190,8 +170,7 @@ class Extractor $methodAnnotations = self::parseAnnotations($docblockMethod); $classAnnotations = self::parseAnnotations($dockblockClass); - if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') - { + if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') { return []; } @@ -205,74 +184,58 @@ class Extractor $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'])) - { + if (!isset($methodAnnotations['ApiMethod'])) { $methodAnnotations['ApiMethod'] = ['get']; } - if (!isset($methodAnnotations['ApiSummary'])) - { + if (!isset($methodAnnotations['ApiSummary'])) { $methodAnnotations['ApiSummary'] = [$methodTitle]; } - if ($methodAnnotations) - { - foreach ($classAnnotations as $name => $valueClass) - { - if (count($valueClass) !== 1) - { + if ($methodAnnotations) { + foreach ($classAnnotations as $name => $valueClass) { + if (count($valueClass) !== 1) { continue; } - if ($name === 'ApiRoute') - { - if (isset($methodAnnotations[$name])) - { + if ($name === 'ApiRoute') { + if (isset($methodAnnotations[$name])) { $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]]; - } - else - { + } else { $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()]; } } - if ($name === 'ApiSector') - { + if ($name === 'ApiSector') { $methodAnnotations[$name] = $valueClass; } } } - if (!isset($methodAnnotations['ApiTitle'])) - { + if (!isset($methodAnnotations['ApiTitle'])) { $methodAnnotations['ApiTitle'] = [$methodTitle]; } - if (!isset($methodAnnotations['ApiRoute'])) - { + if (!isset($methodAnnotations['ApiRoute'])) { $urlArr = []; $className = $class->getName(); list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className); $prefixArr = explode('\\', $prefix); $suffixArr = explode('\\', $suffix); - if ($prefixArr[0] == \think\Config::get('app_namespace')) - { + if ($prefixArr[0] == \think\Config::get('app_namespace')) { $prefixArr[0] = ''; } $urlArr = array_merge($urlArr, $prefixArr); - $urlArr[] = implode('.', array_map(function($item) { - return \think\Loader::parseName($item); - }, $suffixArr)); + $urlArr[] = implode('.', array_map(function ($item) { + return \think\Loader::parseName($item); + }, $suffixArr)); $urlArr[] = $method->getName(); $methodAnnotations['ApiRoute'] = [implode('/', $urlArr)]; } - if (!isset($methodAnnotations['ApiSector'])) - { + if (!isset($methodAnnotations['ApiSector'])) { $methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : [$classTitle]; } - if (!isset($methodAnnotations['ApiParams'])) - { + if (!isset($methodAnnotations['ApiParams'])) { $params = self::parseCustomAnnotations($docblockMethod, 'param'); - foreach ($params as $k => $v) - { + foreach ($params as $k => $v) { $arr = explode(' ', preg_replace("/[\s]+/", " ", $v)); $methodAnnotations['ApiParams'][] = [ 'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '', @@ -299,10 +262,8 @@ class Extractor $annotations = array(); $docblock = substr($docblock, 3, -2); - if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) - { - foreach ($matches[1] as $k => $v) - { + if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) { + foreach ($matches[1] as $k => $v) { $annotations[] = $v; } } @@ -321,36 +282,31 @@ class Extractor // Strip away the docblock header and footer to ease parsing of one line annotations $docblock = substr($docblock, 3, -2); - if (preg_match_all('/@(?[A-Za-z_-]+)[\s\t]*\((?(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) - { + if (preg_match_all('/@(?[A-Za-z_-]+)[\s\t]*\((?(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) { $numMatches = count($matches[0]); - for ($i = 0; $i < $numMatches; ++$i) - { + for ($i = 0; $i < $numMatches; ++$i) { + $name = $matches['name'][$i]; + $value = ''; // annotations has arguments - if (isset($matches['args'][$i])) - { + if (isset($matches['args'][$i])) { $argsParts = trim($matches['args'][$i]); - $name = $matches['name'][$i]; - if($name == 'ApiReturn') - { + if ($name == 'ApiReturn') { $value = $argsParts; - } else { + } else if ($matches['args'][$i] != '') { $argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts); $value = self::parseArgs($argsParts); - if(is_string($value)) - { + if (is_string($value)) { $value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts); } } } - else - { - $value = array(); - } $annotations[$name][] = $value; } } + if (stripos($docblock, '@ApiInternal') !== false) { + $annotations['ApiInternal'] = [true]; + } return $annotations; } @@ -382,39 +338,31 @@ class Extractor $quoted = false; $tokens = array('"', '"', '{', '}', ',', '='); - while ($i <= $len) - { + while ($i <= $len) { $prev_c = substr($content, $i - 1, 1); $c = substr($content, $i++, 1); - if ($c === '"' && $prev_c !== "\\") - { + if ($c === '"' && $prev_c !== "\\") { $delimiter = $c; //open delimiter - if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) - { + if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) { $prevDelimiter = $nextDelimiter = $delimiter; $val = ''; $composing = true; $quoted = true; - } - else - { + } else { // close delimiter - if ($c !== $nextDelimiter) - { + if ($c !== $nextDelimiter) { throw new Exception(sprintf( - "Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c + "Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c )); } // validating syntax - if ($i < $len) - { - if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) - { + 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) + "Parse Error: missing comma separator near: ...%s<--", substr($content, ($i - 10), $i) )); } } @@ -423,11 +371,8 @@ class Extractor $composing = false; $delimiter = null; } - } - elseif (!$composing && in_array($c, $tokens)) - { - switch ($c) - { + } elseif (!$composing && in_array($c, $tokens)) { + switch ($c) { case '=': $prevDelimiter = $nextDelimiter = ''; $level = 2; @@ -440,10 +385,9 @@ class Extractor // 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)) - { + if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) { throw new Exception(sprintf( - "Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c + "Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c )); } @@ -453,19 +397,16 @@ class Extractor $subc = ''; $subComposing = true; - while ($i <= $len) - { + while ($i <= $len) { $c = substr($content, $i++, 1); - if (isset($delimiter) && $c === $delimiter) - { + if (isset($delimiter) && $c === $delimiter) { throw new Exception(sprintf( - "Parse Error: Composite variable is not enclosed correctly." + "Parse Error: Composite variable is not enclosed correctly." )); } - if ($c === '}') - { + if ($c === '}') { $subComposing = false; break; } @@ -473,37 +414,27 @@ class Extractor } // if the string is composing yet means that the structure of var. never was enclosed with '}' - if ($subComposing) - { + if ($subComposing) { throw new Exception(sprintf( - "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", $subc + "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", $subc )); } $val = self::parseArgs($subc); break; } - } - else - { - if ($level == 1) - { + } else { + if ($level == 1) { $var .= $c; - } - elseif ($level == 2) - { + } elseif ($level == 2) { $val .= $c; } } - if ($level === 3 || $i === $len) - { - if ($type == 'plain' && $i === $len) - { + if ($level === 3 || $i === $len) { + if ($type == 'plain' && $i === $len) { $data = self::castValue($var); - } - else - { + } else { $data[trim($var)] = self::castValue($val, !$quoted); } @@ -520,34 +451,26 @@ 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 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) - { + if (is_array($val)) { + foreach ($val as $key => $value) { $val[$key] = self::castValue($value); } - } - elseif (is_string($val)) - { - if ($trim) - { + } elseif (is_string($val)) { + if ($trim) { $val = trim($val); } $val = stripslashes($val); $tmp = strtolower($val); - if ($tmp === 'false' || $tmp === 'true') - { + if ($tmp === 'false' || $tmp === 'true') { $val = $tmp === 'true'; - } - elseif (is_numeric($val)) - { + } elseif (is_numeric($val)) { return $val + 0; } diff --git a/application/admin/command/Api/template/index.html b/application/admin/command/Api/template/index.html index 2bb4db90..62389bcb 100755 --- a/application/admin/command/Api/template/index.html +++ b/application/admin/command/Api/template/index.html @@ -8,6 +8,7 @@ {$config.title} + @@ -68,15 +122,29 @@
+ +
{foreach name="docslist" id="docs"}

{$key}


{foreach name="docs" id="api" }
-
+

- {$api.method|strtoupper} {$api.route} + {$api.method|strtoupper} + {$api.title} {$api.route}

@@ -198,6 +266,7 @@ {/if}
+
@@ -349,6 +418,24 @@ placement: 'bottom' }); + $(window).on("resize", function(){ + $("#sidebar").css("max-height", $(window).height()-80); + }); + + $(window).trigger("resize"); + + $(document).on("click", "#sidebar .list-group > .list-group-item", function(){ + $("#sidebar .list-group > .list-group-item").removeClass("current"); + $(this).addClass("current"); + }); + $(document).on("click", "#sidebar .child a", function(){ + var heading = $("#heading-"+$(this).data("id")); + if(!heading.next().hasClass("in")){ + $("a", heading).trigger("click"); + } + $("html,body").animate({scrollTop:heading.offset().top-70}); + }); + $('code[id^=response]').hide(); $.each($('pre[id^=sample_response],pre[id^=sample_post_body]'), function () { diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php index d61a97bb..87f737df 100755 --- a/application/admin/command/Crud.php +++ b/application/admin/command/Crud.php @@ -104,6 +104,12 @@ class Crud extends Command */ protected $sortField = 'weigh'; + /** + * 筛选字段 + * @var string + */ + protected $headingFilterField = 'status'; + /** * 编辑器的Class */ @@ -138,6 +144,7 @@ class Crud extends Command ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) + ->addOption('headingfilterfield', null, Option::VALUE_OPTIONAL, 'heading filter field', null) ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) ->setDescription('Build CRUD controller and model from table'); } @@ -198,6 +205,8 @@ class Crud extends Command $ignoreFields = $input->getOption('ignorefields'); //排序字段 $sortfield = $input->getOption('sortfield'); + //顶部筛选过滤字段 + $headingfilterfield = $input->getOption('headingfilterfield'); //编辑器Class $editorclass = $input->getOption('editorclass'); if ($setcheckboxsuffix) @@ -224,6 +233,8 @@ class Crud extends Command $this->editorClass = $editorclass; if ($sortfield) $this->sortField = $sortfield; + if ($headingfilterfield) + $this->headingFilterField = $headingfilterfield; $dbname = Config::get('database.database'); $prefix = Config::get('database.prefix'); @@ -466,6 +477,8 @@ class Crud extends Command $getEnumArr = []; $appendAttrList = []; $controllerAssignList = []; + $headingHtml = '{:build_heading()}'; + $headingJs = ''; //循环所有字段,开始构造视图的HTML和JS信息 foreach ($columnList as $k => $v) { @@ -681,6 +694,10 @@ class Crud extends Command //构造JS列信息 $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr); } + if ($this->headingFilterField && $this->headingFilterField == $field && $itemArr) { + $headingHtml = $this->getReplacedStub('html/heading-html', ['field' => $field]); + $headingJs = $this->getReplacedStub('html/heading-js', ['field' => $field]); + } //排序方式,如果有指定排序字段,否则按主键排序 $order = $field == $this->sortField ? $this->sortField : $order; } @@ -724,6 +741,7 @@ class Crud extends Command $modelInit = $this->getReplacedStub('mixins' . DS . 'modelinit', ['order' => $order]); } + $data = [ 'controllerNamespace' => $controllerNamespace, 'modelNamespace' => $modelNamespace, @@ -753,6 +771,8 @@ class Crud extends Command 'relationWithList' => '', 'relationMethodList' => '', 'controllerIndex' => '', + 'headingHtml' => $headingHtml, + 'headingJs' => $headingJs, 'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '', 'appendAttrList' => implode(",\n", $appendAttrList), 'getEnumList' => implode("\n\n", $getEnumArr), diff --git a/application/admin/command/Crud/stubs/html/heading-html.stub b/application/admin/command/Crud/stubs/html/heading-html.stub new file mode 100644 index 00000000..7972eaec --- /dev/null +++ b/application/admin/command/Crud/stubs/html/heading-html.stub @@ -0,0 +1,10 @@ + +
+ {:build_heading(null,FALSE)} + +
diff --git a/application/admin/command/Crud/stubs/html/heading-js.stub b/application/admin/command/Crud/stubs/html/heading-js.stub new file mode 100644 index 00000000..f40c6fae --- /dev/null +++ b/application/admin/command/Crud/stubs/html/heading-js.stub @@ -0,0 +1,18 @@ + + // 绑定TAB事件 + $('.panel-heading a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + var field = $(this).closest("ul").data("field"); + var value = $(this).data("value"); + var options = table.bootstrapTable('getOptions'); + options.pageNumber = 1; + options.queryParams = function (params) { + var filter = {}; + if (value !== '') { + filter[field] = value; + } + params.filter = JSON.stringify(filter); + return params; + }; + table.bootstrapTable('refresh', {}); + return false; + }); \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/index.stub b/application/admin/command/Crud/stubs/index.stub index 7518a118..6e151029 100644 --- a/application/admin/command/Crud/stubs/index.stub +++ b/application/admin/command/Crud/stubs/index.stub @@ -1,5 +1,5 @@
- {:build_heading()} + {%headingHtml%}
diff --git a/application/admin/command/Crud/stubs/javascript.stub b/application/admin/command/Crud/stubs/javascript.stub index 6b938cf9..6749c56f 100644 --- a/application/admin/command/Crud/stubs/javascript.stub +++ b/application/admin/command/Crud/stubs/javascript.stub @@ -28,6 +28,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin ] }); + {%headingJs%} + // 为表格绑定事件 Table.api.bindevent(table); }, diff --git a/application/admin/command/Crud/stubs/mixins/checkbox.stub b/application/admin/command/Crud/stubs/mixins/checkbox.stub index 909adfd4..d5f7b664 100644 --- a/application/admin/command/Crud/stubs/mixins/checkbox.stub +++ b/application/admin/command/Crud/stubs/mixins/checkbox.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $valueArr = explode(',', $value); $list = $this->{%listMethodName%}(); return implode(',', array_intersect_key($list, array_flip($valueArr))); diff --git a/application/admin/command/Crud/stubs/mixins/datetime.stub b/application/admin/command/Crud/stubs/mixins/datetime.stub index 60da81f3..591dd4dd 100644 --- a/application/admin/command/Crud/stubs/mixins/datetime.stub +++ b/application/admin/command/Crud/stubs/mixins/datetime.stub @@ -1,6 +1,6 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/multiple.stub b/application/admin/command/Crud/stubs/mixins/multiple.stub index 909adfd4..d5f7b664 100644 --- a/application/admin/command/Crud/stubs/mixins/multiple.stub +++ b/application/admin/command/Crud/stubs/mixins/multiple.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $valueArr = explode(',', $value); $list = $this->{%listMethodName%}(); return implode(',', array_intersect_key($list, array_flip($valueArr))); diff --git a/application/admin/command/Crud/stubs/mixins/radio.stub b/application/admin/command/Crud/stubs/mixins/radio.stub index f5fa6e0d..527151a9 100644 --- a/application/admin/command/Crud/stubs/mixins/radio.stub +++ b/application/admin/command/Crud/stubs/mixins/radio.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $list = $this->{%listMethodName%}(); return isset($list[$value]) ? $list[$value] : ''; } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/select.stub b/application/admin/command/Crud/stubs/mixins/select.stub index f5fa6e0d..527151a9 100644 --- a/application/admin/command/Crud/stubs/mixins/select.stub +++ b/application/admin/command/Crud/stubs/mixins/select.stub @@ -1,7 +1,7 @@ public function {%methodName%}($value, $data) { - $value = $value ? $value : $data['{%field%}']; + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); $list = $this->{%listMethodName%}(); return isset($list[$value]) ? $list[$value] : ''; } \ No newline at end of file diff --git a/application/admin/controller/Ajax.php b/application/admin/controller/Ajax.php index d378e298..216d053f 100644 --- a/application/admin/controller/Ajax.php +++ b/application/admin/controller/Ajax.php @@ -165,44 +165,30 @@ class Ajax extends Backend $ids = array_values(array_intersect($ids, $hasids)); } - //直接修复排序 - $one = Db::name($table)->field("{$field},COUNT(*) AS nums")->group($field)->having('nums > 1')->find(); - if ($one) { - $list = Db::name($table)->field("$prikey,$field")->order($field, $orderway)->select(); - foreach ($list as $k => $v) { - Db::name($table)->where($prikey, $v[$prikey])->update([$field => $k + 1]); - } - $this->success(); - } else { - $list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select(); - foreach ($list as $k => $v) { - $sour[] = $v[$prikey]; - $weighdata[$v[$prikey]] = $v[$field]; - } - $position = array_search($changeid, $ids); - $desc_id = $sour[$position]; //移动到目标的ID值,取出所处改变前位置的值 - $sour_id = $changeid; - $desc_value = $weighdata[$desc_id]; - $sour_value = $weighdata[$sour_id]; - //echo "移动的ID:{$sour_id}\n"; - //echo "替换的ID:{$desc_id}\n"; - $weighids = array(); - $temp = array_values(array_diff_assoc($ids, $sour)); - foreach ($temp as $m => $n) { - if ($n == $sour_id) { - $offset = $desc_id; - } else { - if ($sour_id == $temp[0]) { - $offset = isset($temp[$m + 1]) ? $temp[$m + 1] : $sour_id; - } else { - $offset = isset($temp[$m - 1]) ? $temp[$m - 1] : $sour_id; - } - } - $weighids[$n] = $weighdata[$offset]; - Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]); - } - $this->success(); + $list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select(); + foreach ($list as $k => $v) { + $sour[] = $v[$prikey]; + $weighdata[$v[$prikey]] = $v[$field]; } + $position = array_search($changeid, $ids); + $desc_id = $sour[$position]; //移动到目标的ID值,取出所处改变前位置的值 + $sour_id = $changeid; + $weighids = array(); + $temp = array_values(array_diff_assoc($ids, $sour)); + foreach ($temp as $m => $n) { + if ($n == $sour_id) { + $offset = $desc_id; + } else { + if ($sour_id == $temp[0]) { + $offset = isset($temp[$m + 1]) ? $temp[$m + 1] : $sour_id; + } else { + $offset = isset($temp[$m - 1]) ? $temp[$m - 1] : $sour_id; + } + } + $weighids[$n] = $weighdata[$offset]; + Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]); + } + $this->success(); } /** diff --git a/application/admin/controller/Category.php b/application/admin/controller/Category.php index b16c4d47..bf84c7a4 100644 --- a/application/admin/controller/Category.php +++ b/application/admin/controller/Category.php @@ -15,6 +15,9 @@ use fast\Tree; class Category extends Backend { + /** + * @var \app\common\model\Category + */ protected $model = null; protected $categorylist = []; protected $noNeedRight = ['selectpage']; diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php index da62b6c7..25e036bf 100644 --- a/application/admin/controller/Index.php +++ b/application/admin/controller/Index.php @@ -30,7 +30,7 @@ class Index extends Backend public function index() { //左侧菜单 - list($menulist, $navlist) = $this->auth->getSidebar([ + list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([ 'dashboard' => 'hot', 'addon' => ['new', 'red', 'badge'], 'auth/rule' => __('Menu'), @@ -44,6 +44,8 @@ class Index extends Backend } $this->view->assign('menulist', $menulist); $this->view->assign('navlist', $navlist); + $this->view->assign('fixedmenu', $fixedmenu); + $this->view->assign('referermenu', $referermenu); $this->view->assign('title', __('Home')); return $this->view->fetch(); } diff --git a/application/admin/controller/auth/Admin.php b/application/admin/controller/auth/Admin.php index 4b53f7bf..40c6c801 100644 --- a/application/admin/controller/auth/Admin.php +++ b/application/admin/controller/auth/Admin.php @@ -17,6 +17,9 @@ use fast\Tree; class Admin extends Backend { + /** + * @var \app\admin\model\Admin + */ protected $model = null; protected $childrenGroupIds = []; protected $childrenAdminIds = []; diff --git a/application/admin/controller/auth/Adminlog.php b/application/admin/controller/auth/Adminlog.php index f54c9c7d..1652ed0a 100644 --- a/application/admin/controller/auth/Adminlog.php +++ b/application/admin/controller/auth/Adminlog.php @@ -14,6 +14,9 @@ use app\common\controller\Backend; class Adminlog extends Backend { + /** + * @var \app\admin\model\AdminLog + */ protected $model = null; protected $childrenGroupIds = []; protected $childrenAdminIds = []; diff --git a/application/admin/controller/auth/Group.php b/application/admin/controller/auth/Group.php index 55e318d8..1f573b81 100644 --- a/application/admin/controller/auth/Group.php +++ b/application/admin/controller/auth/Group.php @@ -15,6 +15,9 @@ use fast\Tree; class Group extends Backend { + /** + * @var \app\admin\model\AuthGroup + */ protected $model = null; //当前登录管理员所有子组别 protected $childrenGroupIds = []; diff --git a/application/admin/controller/auth/Rule.php b/application/admin/controller/auth/Rule.php index 1a271b5c..897f66ba 100644 --- a/application/admin/controller/auth/Rule.php +++ b/application/admin/controller/auth/Rule.php @@ -15,6 +15,9 @@ use think\Cache; class Rule extends Backend { + /** + * @var \app\admin\model\AuthRule + */ protected $model = null; protected $rulelist = []; protected $multiFields = 'ismenu,status'; diff --git a/application/admin/controller/general/Attachment.php b/application/admin/controller/general/Attachment.php index 4ef2c48c..d2ca90a6 100644 --- a/application/admin/controller/general/Attachment.php +++ b/application/admin/controller/general/Attachment.php @@ -13,6 +13,9 @@ use app\common\controller\Backend; class Attachment extends Backend { + /** + * @var \app\common\model\Attachment + */ protected $model = null; public function _initialize() diff --git a/application/admin/controller/general/Config.php b/application/admin/controller/general/Config.php index 2f4d1051..035d8a9c 100644 --- a/application/admin/controller/general/Config.php +++ b/application/admin/controller/general/Config.php @@ -16,6 +16,9 @@ use think\Exception; class Config extends Backend { + /** + * @var \app\common\model\Config + */ protected $model = null; protected $noNeedRight = ['check']; diff --git a/application/admin/controller/user/Group.php b/application/admin/controller/user/Group.php index 6042ccc1..52463eec 100644 --- a/application/admin/controller/user/Group.php +++ b/application/admin/controller/user/Group.php @@ -13,7 +13,7 @@ class Group extends Backend { /** - * UserGroup模型对象 + * @var \app\admin\model\UserGroup */ protected $model = null; diff --git a/application/admin/controller/user/Rule.php b/application/admin/controller/user/Rule.php index ae85d485..4f818d7a 100644 --- a/application/admin/controller/user/Rule.php +++ b/application/admin/controller/user/Rule.php @@ -13,8 +13,9 @@ use fast\Tree; class Rule extends Backend { + /** - * UserRule模型对象 + * @var \app\admin\model\UserRule */ protected $model = null; protected $rulelist = []; diff --git a/application/admin/controller/user/User.php b/application/admin/controller/user/User.php index 772a3abf..fd34f9e9 100644 --- a/application/admin/controller/user/User.php +++ b/application/admin/controller/user/User.php @@ -14,8 +14,9 @@ class User extends Backend protected $relationSearch = true; + /** - * User模型对象 + * @var \app\admin\model\User */ protected $model = null; diff --git a/application/admin/lang/zh-cn.php b/application/admin/lang/zh-cn.php index e64084ba..f58ef4cd 100755 --- a/application/admin/lang/zh-cn.php +++ b/application/admin/lang/zh-cn.php @@ -17,6 +17,8 @@ return [ 'Del' => '删除', 'Delete' => '删除', 'Import' => '导入', + 'Export' => '导出', + 'All' => '全部', 'Detail' => '详情', 'Multi' => '批量更新', 'Setting' => '配置', diff --git a/application/admin/library/Auth.php b/application/admin/library/Auth.php index e1991eb6..eca1ff17 100644 --- a/application/admin/library/Auth.php +++ b/application/admin/library/Auth.php @@ -367,7 +367,8 @@ class Auth extends \fast\Auth // 读取管理员当前拥有的权限节点 $userRule = $this->getRuleList(); - $select_id = 0; + $selected = $referer = []; + $refererUrl = Session::get('referer'); $pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader'); // 必须将结果集转换为数组 $ruleList = collection(\app\admin\model\AuthRule::where('status', 'normal')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray(); @@ -376,15 +377,20 @@ class Auth extends \fast\Auth unset($ruleList[$k]); continue; } - $select_id = $v['name'] == $fixedPage ? $v['id'] : $select_id; $v['icon'] = $v['icon'] . ' fa-fw'; $v['url'] = '/' . $module . '/' . $v['name']; $v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : ''; $v['py'] = $pinyin->abbr($v['title'], ''); $v['pinyin'] = $pinyin->permalink($v['title'], ''); $v['title'] = __($v['title']); + $selected = $v['name'] == $fixedPage ? $v : $selected; + $referer = $v['url'] == $refererUrl ? $v : $referer; + } + if ($selected == $referer) { + $referer = []; } + $select_id = $selected ? $selected['id'] : 0; $menu = $nav = ''; if (Config::get('fastadmin.multiplenav')) { $topList = []; @@ -412,10 +418,16 @@ class Auth extends \fast\Auth // 构造菜单数据 Tree::instance()->init($ruleList); $menu = Tree::instance()->getTreeMenu(0, '
  • @title @caret @badge @childlist
  • ', $select_id, '', 'ul', 'class="treeview-menu"'); + if ($selected) { + $nav .= ''; + } + if ($referer) { + $nav .= ''; + } } - return [$menu, $nav]; + return [$menu, $nav, $selected, $referer]; } /** diff --git a/application/admin/library/traits/Backend.php b/application/admin/library/traits/Backend.php index 187a722c..57ba890e 100755 --- a/application/admin/library/traits/Backend.php +++ b/application/admin/library/traits/Backend.php @@ -92,6 +92,8 @@ trait Backend } } catch (\think\exception\PDOException $e) { $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); } } $this->error(__('Parameter %s can not be empty', '')); @@ -131,6 +133,8 @@ trait Backend } } catch (\think\exception\PDOException $e) { $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); } } $this->error(__('Parameter %s can not be empty', '')); @@ -322,6 +326,8 @@ trait Backend $this->model->saveAll($insert); } catch (\think\exception\PDOException $exception) { $this->error($exception->getMessage()); + } catch (\Exception $e) { + $this->error($e->getMessage()); } $this->success(); diff --git a/application/admin/view/category/index.html b/application/admin/view/category/index.html index 83d7b698..d69a291b 100644 --- a/application/admin/view/category/index.html +++ b/application/admin/view/category/index.html @@ -14,7 +14,7 @@
    - {:build_toolbar()} + {:build_toolbar('refresh,add,edit,del')}