新增CRUD生成回收站视图和JS文件

新增一键生成API文档排序功能
新增表格默认导出选项
新增buttons中text和title的function支持
修复buttons中visible和disable为false时的判断Bug
修复后台添加子管理员的权限错误
pull/98/MERGE
Karson 2019-03-01 10:14:06 +08:00
parent a329e5fbb2
commit 88226015a8
35 changed files with 2942 additions and 1997 deletions

View File

@ -15,7 +15,6 @@ use think\exception\PDOException;
class Addon extends Command class Addon extends Command
{ {
protected function configure() protected function configure()
{ {
$this $this
@ -80,7 +79,6 @@ class Addon extends Command
$createTableSql = $result[0]['Create Table']; $createTableSql = $result[0]['Create Table'];
} }
} catch (PDOException $e) { } catch (PDOException $e) {
} }
$data = [ $data = [
@ -235,7 +233,8 @@ class Addon extends Command
$zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); $zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$files = new \RecursiveIteratorIterator( $files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY new \RecursiveDirectoryIterator($addonDir),
\RecursiveIteratorIterator::LEAVES_ONLY
); );
foreach ($files as $name => $file) { foreach ($files as $name => $file) {
@ -251,7 +250,7 @@ class Addon extends Command
$output->info("Package Successed!"); $output->info("Package Successed!");
break; break;
default : default:
break; break;
} }
} }
@ -315,5 +314,4 @@ class Addon extends Command
{ {
return __DIR__ . '/Addon/stubs/' . $name . '.stub'; return __DIR__ . '/Addon/stubs/' . $name . '.stub';
} }
} }

View File

@ -12,7 +12,6 @@ use think\Exception;
class Api extends Command class Api extends Command
{ {
protected function configure() protected function configure()
{ {
$site = Config::get('site'); $site = Config::get('site');
@ -84,7 +83,8 @@ class Api extends Command
$controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS;
$files = new \RecursiveIteratorIterator( $files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY new \RecursiveDirectoryIterator($controllerDir),
\RecursiveIteratorIterator::LEAVES_ONLY
); );
foreach ($files as $name => $file) { foreach ($files as $name => $file) {
@ -150,7 +150,7 @@ class Api extends Command
//Append the token's value to the name of the namespace //Append the token's value to the name of the namespace
$namespace .= $token[1]; $namespace .= $token[1];
} else if ($token === ';') { } elseif ($token === ';') {
//If the token is the semicolon, then we're done with the namespace declaration //If the token is the semicolon, then we're done with the namespace declaration
$getting_namespace = false; $getting_namespace = false;
@ -175,5 +175,4 @@ class Api extends Command
//Build the fully-qualified class name and return it //Build the fully-qualified class name and return it
return $namespace ? $namespace . '\\' . $class : $class; return $namespace ? $namespace . '\\' . $class : $class;
} }
} }

View File

@ -36,16 +36,27 @@ class Builder
protected function extractAnnotations() protected function extractAnnotations()
{ {
$st_output = [];
foreach ($this->classes as $class) { foreach ($this->classes as $class) {
$classAnnotation = Extractor::getClassAnnotations($class); $classAnnotation = Extractor::getClassAnnotations($class);
// 如果忽略 // 如果忽略
if (isset($classAnnotation['ApiInternal'])) { if (isset($classAnnotation['ApiInternal'])) {
continue; continue;
} }
$st_output[] = Extractor::getAllClassAnnotations($class); Extractor::getClassMethodAnnotations($class);
} }
return end($st_output); $allClassAnnotation = Extractor::getAllClassAnnotations();
$allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations();
// foreach ($allClassMethodAnnotation as $className => &$methods) {
// foreach ($methods as &$method) {
// //权重判断
// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) {
// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh'];
// }
// }
// }
// unset($methods);
return [$allClassAnnotation, $allClassMethodAnnotation];
} }
protected function generateHeadersTemplate($docs) protected function generateHeadersTemplate($docs)
@ -148,12 +159,19 @@ class Builder
public function parse() public function parse()
{ {
$annotations = $this->extractAnnotations(); list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations();
$sectorArr = [];
foreach ($allClassAnnotations as $index => $allClassAnnotation) {
$sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0];
$sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0;
}
arsort($sectorArr);
$counter = 0; $counter = 0;
$section = null; $section = null;
$weigh = 0;
$docslist = []; $docslist = [];
foreach ($annotations 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])) {
$section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0]; $section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0];
@ -163,25 +181,36 @@ class Builder
if (0 === count($docs)) { if (0 === count($docs)) {
continue; continue;
} }
$docslist[$section][$name] = [
$docslist[$section][] = [
'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), 'method_label' => $this->generateBadgeForMethod($docs),
'section' => $section, 'section' => $section,
'route' => is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0], '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], '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],
'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] : '',
]; ];
$counter++; $counter++;
} }
} }
//重建排序
foreach ($docslist as $index => &$methods) {
$methodSectorArr = [];
foreach ($methods as $name => $method) {
$methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0;
}
arsort($methodSectorArr);
$methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods);
}
$docslist = array_merge(array_flip(array_keys($sectorArr)), $docslist);
return $docslist; return $docslist;
} }
@ -203,5 +232,4 @@ class Builder
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

@ -20,6 +20,10 @@ class Extractor
*/ */
private static $annotationCache; private static $annotationCache;
private static $classAnnotationCache;
private static $classMethodAnnotationCache;
/** /**
* Indicates that annotations should has strict behavior, 'false' by default * Indicates that annotations should has strict behavior, 'false' by default
* @var boolean * @var boolean
@ -63,27 +67,43 @@ 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::$annotationCache all annotated elements * @return array self::$classAnnotationCache all annotated elements
*/ */
public static function getClassAnnotations($className) public static function getClassAnnotations($className)
{ {
if (!isset(self::$annotationCache[$className])) { if (!isset(self::$classAnnotationCache[$className])) {
$class = new \ReflectionClass($className); $class = new \ReflectionClass($className);
self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment()); self::$classAnnotationCache[$className] = self::parseAnnotations($class->getDocComment());
} }
return self::$annotationCache[$className]; return self::$classAnnotationCache[$className];
} }
public static function getAllClassAnnotations($className) /**
* 获取类所有方法的属性配置
* @param $className
* @return mixed
* @throws \ReflectionException
*/
public static function getClassMethodAnnotations($className)
{ {
$class = new \ReflectionClass($className); $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); self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name);
} }
return self::$annotationCache['annotations']; return self::$classMethodAnnotationCache[$className];
}
public static function getAllClassAnnotations()
{
return self::$classAnnotationCache;
}
public static function getAllClassMethodAnnotations()
{
return self::$classMethodAnnotationCache;
} }
/** /**
@ -181,14 +201,14 @@ class Extractor
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr); preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr);
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr); preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr);
$methodTitle = isset($methodArr[1]) && isset($methodArr[1][0]) ? $methodArr[1][0] : '';
$classTitle = isset($classArr[1]) && isset($classArr[1][0]) ? $classArr[1][0] : '';
if (!isset($methodAnnotations['ApiMethod'])) { if (!isset($methodAnnotations['ApiMethod'])) {
$methodAnnotations['ApiMethod'] = ['get']; $methodAnnotations['ApiMethod'] = ['get'];
} }
if (!isset($methodAnnotations['ApiWeigh'])) {
$methodAnnotations['ApiWeigh'] = [0];
}
if (!isset($methodAnnotations['ApiSummary'])) { if (!isset($methodAnnotations['ApiSummary'])) {
$methodAnnotations['ApiSummary'] = [$methodTitle]; $methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle'];
} }
if ($methodAnnotations) { if ($methodAnnotations) {
@ -210,9 +230,6 @@ class Extractor
} }
} }
} }
if (!isset($methodAnnotations['ApiTitle'])) {
$methodAnnotations['ApiTitle'] = [$methodTitle];
}
if (!isset($methodAnnotations['ApiRoute'])) { if (!isset($methodAnnotations['ApiRoute'])) {
$urlArr = []; $urlArr = [];
$className = $class->getName(); $className = $class->getName();
@ -231,7 +248,7 @@ class Extractor
$methodAnnotations['ApiRoute'] = [implode('/', $urlArr)]; $methodAnnotations['ApiRoute'] = [implode('/', $urlArr)];
} }
if (!isset($methodAnnotations['ApiSector'])) { if (!isset($methodAnnotations['ApiSector'])) {
$methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : [$classTitle]; $methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : $classAnnotations['ApiTitle'];
} }
if (!isset($methodAnnotations['ApiParams'])) { if (!isset($methodAnnotations['ApiParams'])) {
$params = self::parseCustomAnnotations($docblockMethod, 'param'); $params = self::parseCustomAnnotations($docblockMethod, 'param');
@ -292,7 +309,7 @@ class Extractor
$argsParts = trim($matches['args'][$i]); $argsParts = trim($matches['args'][$i]);
if ($name == 'ApiReturn') { if ($name == 'ApiReturn') {
$value = $argsParts; $value = $argsParts;
} else if ($matches['args'][$i] != '') { } elseif ($matches['args'][$i] != '') {
$argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts); $argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts);
$value = self::parseArgs($argsParts); $value = self::parseArgs($argsParts);
if (is_string($value)) { if (is_string($value)) {
@ -307,6 +324,11 @@ class Extractor
if (stripos($docblock, '@ApiInternal') !== false) { if (stripos($docblock, '@ApiInternal') !== false) {
$annotations['ApiInternal'] = [true]; $annotations['ApiInternal'] = [true];
} }
if (!isset($annotations['ApiTitle'])) {
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr);
$title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : '';
$annotations['ApiTitle'] = [$title];
}
return $annotations; return $annotations;
} }
@ -354,7 +376,9 @@ class Extractor
// close delimiter // close delimiter
if ($c !== $nextDelimiter) { if ($c !== $nextDelimiter) {
throw new Exception(sprintf( throw new Exception(sprintf(
"Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c "Parse Error: enclosing error -> expected: [%s], given: [%s]",
$nextDelimiter,
$c
)); ));
} }
@ -362,7 +386,8 @@ class Extractor
if ($i < $len) { if ($i < $len) {
if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) { if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) {
throw new Exception(sprintf( 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)
)); ));
} }
} }
@ -387,7 +412,9 @@ class Extractor
// it means that the string was not enclosed, so it is parsing error. // 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( throw new Exception(sprintf(
"Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c "Parse Error: enclosing error -> expected: [%s], given: [%s]",
$nextDelimiter,
$c
)); ));
} }
@ -416,7 +443,8 @@ class Extractor
// if the string is composing yet means that the structure of var. never was enclosed with '}' // if the string is composing yet means that the structure of var. never was enclosed with '}'
if ($subComposing) { if ($subComposing) {
throw new Exception(sprintf( 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
)); ));
} }
@ -479,5 +507,4 @@ class Extractor
return $val; return $val;
} }
} }

View File

@ -92,6 +92,9 @@
padding-left:0px; padding-left:0px;
} }
} }
.label-primary {
background-color: #248aff;
}
</style> </style>
</head> </head>
@ -547,6 +550,9 @@
contentType: false, contentType: false,
processData: false, processData: false,
headers: headers, headers: headers,
xhrFields: {
withCredentials: true
},
success: function (data, textStatus, xhr) { success: function (data, textStatus, xhr) {
if (typeof data === 'object') { if (typeof data === 'object') {
var str = JSON.stringify(data, null, 2); var str = JSON.stringify(data, null, 2);

View File

@ -15,7 +15,6 @@ use think\Loader;
class Crud extends Command class Crud extends Command
{ {
protected $stubList = []; protected $stubList = [];
/** /**
@ -93,7 +92,7 @@ class Crud extends Command
/** /**
* 保留字段 * 保留字段
*/ */
protected $reservedField = ['admin_id', 'createtime', 'updatetime']; protected $reservedField = ['admin_id'];
/** /**
* 排除字段 * 排除字段
@ -111,6 +110,24 @@ class Crud extends Command
*/ */
protected $headingFilterField = 'status'; protected $headingFilterField = 'status';
/**
* 添加时间字段
* @var string
*/
protected $createTimeField = 'createtime';
/**
* 更新时间字段
* @var string
*/
protected $updateTimeField = 'updatetime';
/**
* 软删除时间字段
* @var string
*/
protected $deleteTimeField = 'deletetime';
/** /**
* 编辑器的Class * 编辑器的Class
*/ */
@ -118,7 +135,7 @@ class Crud extends Command
/** /**
* langList的key最长字节数 * langList的key最长字节数
*/ */
protected $fieldMaxLen = 0; protected $fieldMaxLen = 0;
protected function configure() protected function configure()
@ -215,32 +232,47 @@ class Crud extends Command
$headingfilterfield = $input->getOption('headingfilterfield'); $headingfilterfield = $input->getOption('headingfilterfield');
//编辑器Class //编辑器Class
$editorclass = $input->getOption('editorclass'); $editorclass = $input->getOption('editorclass');
if ($setcheckboxsuffix) if ($setcheckboxsuffix) {
$this->setCheckboxSuffix = $setcheckboxsuffix; $this->setCheckboxSuffix = $setcheckboxsuffix;
if ($enumradiosuffix) }
if ($enumradiosuffix) {
$this->enumRadioSuffix = $enumradiosuffix; $this->enumRadioSuffix = $enumradiosuffix;
if ($imagefield) }
if ($imagefield) {
$this->imageField = $imagefield; $this->imageField = $imagefield;
if ($filefield) }
if ($filefield) {
$this->fileField = $filefield; $this->fileField = $filefield;
if ($intdatesuffix) }
if ($intdatesuffix) {
$this->intDateSuffix = $intdatesuffix; $this->intDateSuffix = $intdatesuffix;
if ($switchsuffix) }
if ($switchsuffix) {
$this->switchSuffix = $switchsuffix; $this->switchSuffix = $switchsuffix;
if ($citysuffix) }
if ($citysuffix) {
$this->citySuffix = $citysuffix; $this->citySuffix = $citysuffix;
if ($selectpagesuffix) }
if ($selectpagesuffix) {
$this->selectpageSuffix = $selectpagesuffix; $this->selectpageSuffix = $selectpagesuffix;
if ($selectpagessuffix) }
if ($selectpagessuffix) {
$this->selectpagesSuffix = $selectpagessuffix; $this->selectpagesSuffix = $selectpagessuffix;
if ($ignoreFields) }
if ($ignoreFields) {
$this->ignoreFields = $ignoreFields; $this->ignoreFields = $ignoreFields;
if ($editorclass) }
if ($editorclass) {
$this->editorClass = $editorclass; $this->editorClass = $editorclass;
if ($sortfield) }
if ($sortfield) {
$this->sortField = $sortfield; $this->sortField = $sortfield;
if ($headingfilterfield) }
if ($headingfilterfield) {
$this->headingFilterField = $headingfilterfield; $this->headingFilterField = $headingfilterfield;
}
$this->reservedField = array_merge($this->reservedField, [$this->createTimeField, $this->updateTimeField, $this->deleteTimeField]);
$dbname = Config::get('database.database'); $dbname = Config::get('database.database');
$prefix = Config::get('database.prefix'); $prefix = Config::get('database.prefix');
@ -254,11 +286,11 @@ class Crud extends Command
$modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; $modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table;
$modelTableType = 'table'; $modelTableType = 'table';
$modelTableTypeName = $modelTableName = $modelName; $modelTableTypeName = $modelTableName = $modelName;
$modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], TRUE); $modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true);
if (!$modelTableInfo) { if (!$modelTableInfo) {
$modelTableType = 'name'; $modelTableType = 'name';
$modelTableName = $prefix . $modelName; $modelTableName = $prefix . $modelName;
$modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], TRUE); $modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true);
if (!$modelTableInfo) { if (!$modelTableInfo) {
throw new Exception("table not found"); throw new Exception("table not found");
} }
@ -275,11 +307,11 @@ class Crud extends Command
$relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable; $relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable;
$relationTableType = 'table'; $relationTableType = 'table';
$relationTableTypeName = $relationTableName = $relationName; $relationTableTypeName = $relationTableName = $relationName;
$relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true);
if (!$relationTableInfo) { if (!$relationTableInfo) {
$relationTableType = 'name'; $relationTableType = 'name';
$relationTableName = $prefix . $relationName; $relationTableName = $prefix . $relationName;
$relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true);
if (!$relationTableInfo) { if (!$relationTableInfo) {
throw new Exception("relation table not found"); throw new Exception("relation table not found");
} }
@ -338,6 +370,7 @@ class Crud extends Command
$addFile = $viewDir . 'add.html'; $addFile = $viewDir . 'add.html';
$editFile = $viewDir . 'edit.html'; $editFile = $viewDir . 'edit.html';
$indexFile = $viewDir . 'index.html'; $indexFile = $viewDir . 'index.html';
$recyclebinFile = $viewDir . 'recyclebin.html';
$langFile = $adminPath . 'lang' . DS . Lang::detect() . DS . $controllerBaseName . '.php'; $langFile = $adminPath . 'lang' . DS . Lang::detect() . DS . $controllerBaseName . '.php';
//是否为删除模式 //是否为删除模式
@ -355,12 +388,13 @@ class Crud extends Command
} }
} }
foreach ($readyFiles as $k => $v) { foreach ($readyFiles as $k => $v) {
if (file_exists($v)) if (file_exists($v)) {
unlink($v); unlink($v);
}
//删除空文件夹 //删除空文件夹
if ($v == $modelFile) { if ($v == $modelFile) {
$this->removeEmptyBaseDir($v, $modelArr); $this->removeEmptyBaseDir($v, $modelArr);
} else if ($v == $validateFile) { } elseif ($v == $validateFile) {
$this->removeEmptyBaseDir($v, $validateArr); $this->removeEmptyBaseDir($v, $validateArr);
} else { } else {
$this->removeEmptyBaseDir($v, $controllerArr); $this->removeEmptyBaseDir($v, $controllerArr);
@ -434,7 +468,7 @@ class Crud extends Command
$langList = []; $langList = [];
$field = 'id'; $field = 'id';
$order = 'id'; $order = 'id';
$priDefined = FALSE; $priDefined = false;
$priKey = ''; $priKey = '';
$relationPrimaryKey = ''; $relationPrimaryKey = '';
foreach ($columnList as $k => $v) { foreach ($columnList as $k => $v) {
@ -484,6 +518,7 @@ class Crud extends Command
$appendAttrList = []; $appendAttrList = [];
$controllerAssignList = []; $controllerAssignList = [];
$headingHtml = '{:build_heading()}'; $headingHtml = '{:build_heading()}';
$recyclebinHtml = '';
//循环所有字段,开始构造视图的HTML和JS信息 //循环所有字段,开始构造视图的HTML和JS信息
foreach ($columnList as $k => $v) { foreach ($columnList as $k => $v) {
@ -506,7 +541,7 @@ class Crud extends Command
$langList[] = $this->getLangItem($field, $v['COLUMN_COMMENT']); $langList[] = $this->getLangItem($field, $v['COLUMN_COMMENT']);
} }
$inputType = ''; $inputType = '';
//createtime和updatetime是保留字段不能修改和添加 //保留字段不能修改和添加
if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) { if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) {
$inputType = $this->getFieldType($v); $inputType = $this->getFieldType($v);
@ -534,7 +569,7 @@ class Crud extends Command
$this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); $this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select');
$itemArr = $this->getLangArray($itemArr, FALSE); $itemArr = $this->getLangArray($itemArr, false);
//添加一个获取器 //添加一个获取器
$this->getAttr($getAttrArr, $field, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); $this->getAttr($getAttrArr, $field, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select');
if ($v['DATA_TYPE'] == 'set') { if ($v['DATA_TYPE'] == 'set') {
@ -543,28 +578,29 @@ class Crud extends Command
$this->appendAttr($appendAttrList, $field); $this->appendAttr($appendAttrList, $field);
$formAddElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); $formAddElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]);
$formEditElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); $formEditElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]);
} else if ($inputType == 'datetime') { } elseif ($inputType == 'datetime') {
$cssClassArr[] = 'datetimepicker'; $cssClassArr[] = 'datetimepicker';
$attrArr['class'] = implode(' ', $cssClassArr); $attrArr['class'] = implode(' ', $cssClassArr);
$format = "YYYY-MM-DD HH:mm:ss"; $format = "YYYY-MM-DD HH:mm:ss";
$phpFormat = "Y-m-d H:i:s"; $phpFormat = "Y-m-d H:i:s";
$fieldFunc = ''; $fieldFunc = '';
switch ($v['DATA_TYPE']) { switch ($v['DATA_TYPE']) {
case 'year'; case 'year':
$format = "YYYY"; $format = "YYYY";
$phpFormat = 'Y'; $phpFormat = 'Y';
break; break;
case 'date'; case 'date':
$format = "YYYY-MM-DD"; $format = "YYYY-MM-DD";
$phpFormat = 'Y-m-d'; $phpFormat = 'Y-m-d';
break; break;
case 'time'; case 'time':
$format = "HH:mm:ss"; $format = "HH:mm:ss";
$phpFormat = 'H:i:s'; $phpFormat = 'H:i:s';
break; break;
case 'timestamp'; case 'timestamp':
$fieldFunc = 'datetime'; $fieldFunc = 'datetime';
case 'datetime'; // no break
case 'datetime':
$format = "YYYY-MM-DD HH:mm:ss"; $format = "YYYY-MM-DD HH:mm:ss";
$phpFormat = 'Y-m-d H:i:s'; $phpFormat = 'Y-m-d H:i:s';
break; break;
@ -581,13 +617,13 @@ class Crud extends Command
$fieldFunc = $fieldFunc ? "|{$fieldFunc}" : ""; $fieldFunc = $fieldFunc ? "|{$fieldFunc}" : "";
$formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr); $formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr);
$formEditElement = Form::text($fieldName, "{\$row.{$field}{$fieldFunc}}", $attrArr); $formEditElement = Form::text($fieldName, "{\$row.{$field}{$fieldFunc}}", $attrArr);
} else if ($inputType == 'checkbox' || $inputType == 'radio') { } elseif ($inputType == 'checkbox' || $inputType == 'radio') {
unset($attrArr['data-rule']); unset($attrArr['data-rule']);
$fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName; $fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName;
$attrArr['name'] = "row[{$fieldName}]"; $attrArr['name'] = "row[{$fieldName}]";
$this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $inputType); $this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $inputType);
$itemArr = $this->getLangArray($itemArr, FALSE); $itemArr = $this->getLangArray($itemArr, false);
//添加一个获取器 //添加一个获取器
$this->getAttr($getAttrArr, $field, $inputType); $this->getAttr($getAttrArr, $field, $inputType);
if ($inputType == 'checkbox') { if ($inputType == 'checkbox') {
@ -598,13 +634,13 @@ class Crud extends Command
$formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]);
$formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]);
} else if ($inputType == 'textarea') { } elseif ($inputType == 'textarea') {
$cssClassArr[] = $this->isMatchSuffix($field, $this->editorSuffix) ? $this->editorClass : ''; $cssClassArr[] = $this->isMatchSuffix($field, $this->editorSuffix) ? $this->editorClass : '';
$attrArr['class'] = implode(' ', $cssClassArr); $attrArr['class'] = implode(' ', $cssClassArr);
$attrArr['rows'] = 5; $attrArr['rows'] = 5;
$formAddElement = Form::textarea($fieldName, $defaultValue, $attrArr); $formAddElement = Form::textarea($fieldName, $defaultValue, $attrArr);
$formEditElement = Form::textarea($fieldName, $editValue, $attrArr); $formEditElement = Form::textarea($fieldName, $editValue, $attrArr);
} else if ($inputType == 'switch') { } elseif ($inputType == 'switch') {
unset($attrArr['data-rule']); unset($attrArr['data-rule']);
if ($defaultValue === '1' || $defaultValue === 'Y') { if ($defaultValue === '1' || $defaultValue === 'Y') {
$yes = $defaultValue; $yes = $defaultValue;
@ -619,7 +655,7 @@ class Crud extends Command
$stateNoClass = 'fa-flip-horizontal text-gray'; $stateNoClass = 'fa-flip-horizontal text-gray';
$formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => $defaultValue, 'fieldSwitchClass' => $defaultValue == $no ? $stateNoClass : '']); $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => $defaultValue, 'fieldSwitchClass' => $defaultValue == $no ? $stateNoClass : '']);
$formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => "{\$row.{$field}}", 'fieldSwitchClass' => "{eq name=\"\$row.{$field}\" value=\"{$no}\"}fa-flip-horizontal text-gray{/eq}"]); $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => "{\$row.{$field}}", 'fieldSwitchClass' => "{eq name=\"\$row.{$field}\" value=\"{$no}\"}fa-flip-horizontal text-gray{/eq}"]);
} else if ($inputType == 'citypicker') { } elseif ($inputType == 'citypicker') {
$attrArr['class'] = implode(' ', $cssClassArr); $attrArr['class'] = implode(' ', $cssClassArr);
$attrArr['data-toggle'] = "city-picker"; $attrArr['data-toggle'] = "city-picker";
$formAddElement = sprintf("<div class='control-relative'>%s</div>", Form::input('text', $fieldName, $defaultValue, $attrArr)); $formAddElement = sprintf("<div class='control-relative'>%s</div>", Form::input('text', $fieldName, $defaultValue, $attrArr));
@ -692,9 +728,13 @@ class Crud extends Command
if ($v['DATA_TYPE'] != 'text') { if ($v['DATA_TYPE'] != 'text') {
//主键 //主键
if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) { if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) {
$priDefined = TRUE; $priDefined = true;
$javascriptList[] = "{checkbox: true}"; $javascriptList[] = "{checkbox: true}";
} }
if ($this->deleteTimeField == $field) {
$recyclebinHtml = $this->getReplacedStub('html/recyclebin-html', ['controllerUrl' => $controllerUrl]);
continue;
}
if (!$fields || in_array($field, explode(',', $fields))) { if (!$fields || in_array($field, explode(',', $fields))) {
//构造JS列信息 //构造JS列信息
$javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr); $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr);
@ -743,7 +783,7 @@ class Crud extends Command
} }
} }
unset($line); unset($line);
$langList = implode(",\n", array_filter($langList)). ","; $langList = implode(",\n", array_filter($langList)) . ",";
//表注释 //表注释
$tableComment = $modelTableInfo['Comment']; $tableComment = $modelTableInfo['Comment'];
@ -754,7 +794,6 @@ class Crud extends Command
$modelInit = $this->getReplacedStub('mixins' . DS . 'modelinit', ['order' => $order]); $modelInit = $this->getReplacedStub('mixins' . DS . 'modelinit', ['order' => $order]);
} }
$data = [ $data = [
'controllerNamespace' => $controllerNamespace, 'controllerNamespace' => $controllerNamespace,
'modelNamespace' => $modelNamespace, 'modelNamespace' => $modelNamespace,
@ -777,14 +816,18 @@ class Crud extends Command
'editList' => $editList, 'editList' => $editList,
'javascriptList' => $javascriptList, 'javascriptList' => $javascriptList,
'langList' => $langList, 'langList' => $langList,
'modelAutoWriteTimestamp' => in_array('createtime', $fieldArr) || in_array('updatetime', $fieldArr) ? "'int'" : 'false', 'sofeDeleteClassPath' => in_array($this->deleteTimeField, $fieldArr) ? "use traits\model\SoftDelete;" : '',
'createTime' => in_array('createtime', $fieldArr) ? "'createtime'" : 'false', 'softDelete' => in_array($this->deleteTimeField, $fieldArr) ? "use SoftDelete;" : '',
'updateTime' => in_array('updatetime', $fieldArr) ? "'updatetime'" : 'false', 'modelAutoWriteTimestamp' => in_array($this->createTimeField, $fieldArr) || in_array($this->updateTimeField, $fieldArr) ? "'int'" : 'false',
'createTime' => in_array($this->createTimeField, $fieldArr) ? "'{$this->createTimeField}'" : 'false',
'updateTime' => in_array($this->updateTimeField, $fieldArr) ? "'{$this->updateTimeField}'" : 'false',
'deleteTime' => in_array($this->deleteTimeField, $fieldArr) ? "'{$this->deleteTimeField}'" : 'false',
'relationSearch' => $relations ? 'true' : 'false', 'relationSearch' => $relations ? 'true' : 'false',
'relationWithList' => '', 'relationWithList' => '',
'relationMethodList' => '', 'relationMethodList' => '',
'controllerIndex' => '', 'controllerIndex' => '',
'headingHtml' => $headingHtml, 'headingHtml' => $headingHtml,
'recyclebinHtml' => $recyclebinHtml,
'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '', 'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '',
'appendAttrList' => implode(",\n", $appendAttrList), 'appendAttrList' => implode(",\n", $appendAttrList),
'getEnumList' => implode("\n\n", $getEnumArr), 'getEnumList' => implode("\n\n", $getEnumArr),
@ -832,8 +875,7 @@ class Crud extends Command
//需要重写index方法 //需要重写index方法
$data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data);
} elseif ($fields) {
} else if ($fields) {
$data = array_merge($data, ['relationWithList' => '', 'relationMethodList' => '', 'relationVisibleFieldList' => '']); $data = array_merge($data, ['relationWithList' => '', 'relationMethodList' => '', 'relationVisibleFieldList' => '']);
//需要重写index方法 //需要重写index方法
$data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data);
@ -852,7 +894,6 @@ class Crud extends Command
$result = $this->writeToFile('relationmodel', $relation, $relation['relationFile']); $result = $this->writeToFile('relationmodel', $relation, $relation['relationFile']);
} }
} }
} }
// 生成验证文件 // 生成验证文件
$result = $this->writeToFile('validate', $data, $validateFile); $result = $this->writeToFile('validate', $data, $validateFile);
@ -860,6 +901,12 @@ class Crud extends Command
$result = $this->writeToFile('add', $data, $addFile); $result = $this->writeToFile('add', $data, $addFile);
$result = $this->writeToFile('edit', $data, $editFile); $result = $this->writeToFile('edit', $data, $editFile);
$result = $this->writeToFile('index', $data, $indexFile); $result = $this->writeToFile('index', $data, $indexFile);
if ($recyclebinHtml) {
$result = $this->writeToFile('recyclebin', $data, $recyclebinFile);
$recyclebinTitle = in_array('title', $fieldArr) ? 'title' : (in_array('name', $fieldArr) ? 'name' : '');
$recyclebinTitleJs = $recyclebinTitle ? "\n {field: '{$recyclebinTitle}', title: __('" . (ucfirst($recyclebinTitle)) . "'), align: 'left'}," : '';
$data['recyclebinJs'] = $this->getReplacedStub('mixins/recyclebinjs', ['recyclebinTitleJs' => $recyclebinTitleJs, 'controllerUrl' => $controllerUrl]);
}
// 生成JS文件 // 生成JS文件
$result = $this->writeToFile('javascript', $data, $javascriptFile); $result = $this->writeToFile('javascript', $data, $javascriptFile);
// 生成语言文件 // 生成语言文件
@ -880,8 +927,9 @@ class Crud extends Command
protected function getEnum(&$getEnum, &$controllerAssignList, $field, $itemArr = '', $inputType = '') protected function getEnum(&$getEnum, &$controllerAssignList, $field, $itemArr = '', $inputType = '')
{ {
if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) {
return; return;
}
$fieldList = $this->getFieldListName($field); $fieldList = $this->getFieldListName($field);
$methodName = 'get' . ucfirst($fieldList); $methodName = 'get' . ucfirst($fieldList);
foreach ($itemArr as $k => &$v) { foreach ($itemArr as $k => &$v) {
@ -902,22 +950,24 @@ EOD;
protected function getAttr(&$getAttr, $field, $inputType = '') protected function getAttr(&$getAttr, $field, $inputType = '')
{ {
if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) {
return; return;
}
$attrField = ucfirst($this->getCamelizeName($field)); $attrField = ucfirst($this->getCamelizeName($field));
$getAttr[] = $this->getReplacedStub("mixins" . DS . $inputType, ['field' => $field, 'methodName' => "get{$attrField}TextAttr", 'listMethodName' => "get{$attrField}List"]); $getAttr[] = $this->getReplacedStub("mixins" . DS . $inputType, ['field' => $field, 'methodName' => "get{$attrField}TextAttr", 'listMethodName' => "get{$attrField}List"]);
} }
protected function setAttr(&$setAttr, $field, $inputType = '') protected function setAttr(&$setAttr, $field, $inputType = '')
{ {
if (!in_array($inputType, ['datetime', 'checkbox', 'select'])) if (!in_array($inputType, ['datetime', 'checkbox', 'select'])) {
return; return;
}
$attrField = ucfirst($this->getCamelizeName($field)); $attrField = ucfirst($this->getCamelizeName($field));
if ($inputType == 'datetime') { if ($inputType == 'datetime') {
$return = <<<EOD $return = <<<EOD
return \$value && !is_numeric(\$value) ? strtotime(\$value) : \$value; return \$value && !is_numeric(\$value) ? strtotime(\$value) : \$value;
EOD; EOD;
} else if (in_array($inputType, ['checkbox', 'select'])) { } elseif (in_array($inputType, ['checkbox', 'select'])) {
$return = <<<EOD $return = <<<EOD
return is_array(\$value) ? implode(',', \$value) : \$value; return is_array(\$value) ? implode(',', \$value) : \$value;
EOD; EOD;
@ -1118,7 +1168,7 @@ EOD;
* @param boolean $withTpl * @param boolean $withTpl
* @return array * @return array
*/ */
protected function getLangArray($arr, $withTpl = TRUE) protected function getLangArray($arr, $withTpl = true)
{ {
$langArr = []; $langArr = [];
foreach ($arr as $k => $v) { foreach ($arr as $k => $v) {
@ -1134,8 +1184,9 @@ EOD;
*/ */
protected function getArrayString($arr) protected function getArrayString($arr)
{ {
if (!is_array($arr)) if (!is_array($arr)) {
return $arr; return $arr;
}
$stringArr = []; $stringArr = [];
foreach ($arr as $k => $v) { foreach ($arr as $k => $v) {
$is_var = in_array(substr($v, 0, 1), ['$', '_']); $is_var = in_array(substr($v, 0, 1), ['$', '_']);
@ -1197,11 +1248,11 @@ EOD;
case 'tinytext': case 'tinytext':
$inputType = 'textarea'; $inputType = 'textarea';
break; break;
case 'year'; case 'year':
case 'date'; case 'date':
case 'time'; case 'time':
case 'datetime'; case 'datetime':
case 'timestamp'; case 'timestamp':
$inputType = 'datetime'; $inputType = 'datetime';
break; break;
default: default:
@ -1327,8 +1378,9 @@ EOD;
$formatter = 'label'; $formatter = 'label';
} }
foreach ($itemArr as $k => &$v) { foreach ($itemArr as $k => &$v) {
if (substr($v, 0, 3) !== '__(') if (substr($v, 0, 3) !== '__(') {
$v = "__('" . mb_ucfirst($v) . "')"; $v = "__('" . mb_ucfirst($v) . "')";
}
} }
unset($v); unset($v);
$searchList = json_encode($itemArr, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE); $searchList = json_encode($itemArr, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE);
@ -1338,22 +1390,23 @@ EOD;
} }
if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') { if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') {
$html .= ", operate:'RANGE', addclass:'datetimerange'"; $html .= ", operate:'RANGE', addclass:'datetimerange'";
} else if (in_array($datatype, ['float', 'double', 'decimal'])) { } elseif (in_array($datatype, ['float', 'double', 'decimal'])) {
$html .= ", operate:'BETWEEN'"; $html .= ", operate:'BETWEEN'";
} }
if (in_array($datatype, ['set'])) { if (in_array($datatype, ['set'])) {
$html .= ", operate:'FIND_IN_SET'"; $html .= ", operate:'FIND_IN_SET'";
} }
if (in_array($formatter, ['image','images'])) { if (in_array($formatter, ['image', 'images'])) {
$html .= ", events: Table.api.events.image"; $html .= ", events: Table.api.events.image";
} }
if ($itemArr && !$formatter) { if ($itemArr && !$formatter) {
$formatter = 'normal'; $formatter = 'normal';
} }
if ($formatter) if ($formatter) {
$html .= ", formatter: Table.api.formatter." . $formatter . "}"; $html .= ", formatter: Table.api.formatter." . $formatter . "}";
else } else {
$html .= "}"; $html .= "}";
}
return $html; return $html;
} }
@ -1367,5 +1420,4 @@ EOD;
{ {
return $this->getCamelizeName($field) . 'List'; return $this->getCamelizeName($field) . 'List';
} }
} }

View File

@ -0,0 +1 @@
<a class="btn btn-success btn-recyclebin btn-dialog" href="{%controllerUrl%}/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>

View File

@ -19,6 +19,8 @@
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li> <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul> </ul>
</div> </div>
{%recyclebinHtml%}
</div> </div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap" <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('{%controllerUrl%}/edit')}" data-operate-edit="{:$auth->check('{%controllerUrl%}/edit')}"

View File

@ -30,7 +30,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
// 为表格绑定事件 // 为表格绑定事件
Table.api.bindevent(table); Table.api.bindevent(table);
}, },{%recyclebinJs%}
add: function () { add: function () {
Controller.api.bindevent(); Controller.api.bindevent();
}, },

View File

@ -0,0 +1,58 @@
recyclebin: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
'dragsort_url': ''
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: '{%controllerUrl%}/recyclebin',
pk: 'id',
sortName: 'id',
columns: [
[
{checkbox: true},
{field: 'id', title: __('Id')},{%recyclebinTitleJs%}
{
field: 'deletetime',
title: __('Deletetime'),
operate: 'RANGE',
addclass: 'datetimerange',
formatter: Table.api.formatter.datetime
},
{
field: 'operate',
width: '130px',
title: __('Operate'),
table: table,
events: Table.api.events.operate,
buttons: [
{
name: 'Restore',
text: __('Restore'),
classname: 'btn btn-xs btn-info btn-restoreit',
icon: 'fa fa-rotate-left',
url: '{%controllerUrl%}/restore'
},
{
name: 'Destroy',
text: __('Destroy'),
classname: 'btn btn-xs btn-danger btn-destroyit',
icon: 'fa fa-times',
url: '{%controllerUrl%}/destroy'
}
],
formatter: Table.api.formatter.operate
}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},

View File

@ -3,9 +3,13 @@
namespace {%modelNamespace%}; namespace {%modelNamespace%};
use think\Model; use think\Model;
{%sofeDeleteClassPath%}
class {%modelName%} extends Model class {%modelName%} extends Model
{ {
{%softDelete%}
// 表名 // 表名
protected ${%modelTableType%} = '{%modelTableTypeName%}'; protected ${%modelTableType%} = '{%modelTableTypeName%}';
@ -15,7 +19,8 @@ class {%modelName%} extends Model
// 定义时间戳字段名 // 定义时间戳字段名
protected $createTime = {%createTime%}; protected $createTime = {%createTime%};
protected $updateTime = {%updateTime%}; protected $updateTime = {%updateTime%};
protected $deleteTime = {%deleteTime%};
// 追加属性 // 追加属性
protected $append = [ protected $append = [
{%appendAttrList%} {%appendAttrList%}

View File

@ -0,0 +1,25 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar('refresh')}
<a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
<a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
<a class="btn btn-success btn-restoreall {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
<a class="btn btn-danger btn-destroyall {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-restore="{:$auth->check('{%controllerUrl%}/restore')}"
data-operate-destroy="{:$auth->check('{%controllerUrl%}/destroy')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -13,7 +13,6 @@ use think\Exception;
class Install extends Command class Install extends Command
{ {
protected $model = null; protected $model = null;
protected function configure() protected function configure()
@ -27,7 +26,7 @@ class Install extends Command
->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix']) ->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix'])
->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username']) ->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username'])
->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password']) ->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password'])
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', FALSE) ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', false)
->setDescription('New installation of FastAdmin'); ->setDescription('New installation of FastAdmin');
} }
@ -93,5 +92,4 @@ class Install extends Command
$output->info("Install Successed!"); $output->info("Install Successed!");
} }
} }

View File

@ -15,7 +15,6 @@ use think\Exception;
class Menu extends Command class Menu extends Command
{ {
protected $model = null; protected $model = null;
protected function configure() protected function configure()
@ -32,7 +31,6 @@ class Menu extends Command
protected function execute(Input $input, Output $output) protected function execute(Input $input, Output $output)
{ {
$this->model = new AuthRule(); $this->model = new AuthRule();
$adminPath = dirname(__DIR__) . DS; $adminPath = dirname(__DIR__) . DS;
//控制器名 //控制器名
@ -54,10 +52,11 @@ class Menu extends Command
$ids = []; $ids = [];
$list = $this->model->where(function ($query) use ($controller, $equal) { $list = $this->model->where(function ($query) use ($controller, $equal) {
foreach ($controller as $index => $item) { foreach ($controller as $index => $item) {
if ($equal) if ($equal) {
$query->whereOr('name', 'eq', $item); $query->whereOr('name', 'eq', $item);
else } else {
$query->whereOr('name', 'like', strtolower($item) . "%"); $query->whereOr('name', 'like', strtolower($item) . "%");
}
} }
})->select(); })->select();
foreach ($list as $k => $v) { foreach ($list as $k => $v) {
@ -94,8 +93,11 @@ class Menu extends Command
} }
$this->importRule($item); $this->importRule($item);
} }
} else { } else {
$authRuleList = AuthRule::select();
//生成权限规则备份文件
file_put_contents(RUNTIME_PATH . 'authrule.json', json_encode(collection($authRuleList)->toArray()));
$this->model->where('id', '>', 0)->delete(); $this->model->where('id', '>', 0)->delete();
$controllerDir = $adminPath . 'controller' . DS; $controllerDir = $adminPath . 'controller' . DS;
// 扫描新的节点信息并导入 // 扫描新的节点信息并导入
@ -199,7 +201,7 @@ class Menu extends Command
} }
} }
//忽略的类 //忽略的类
if (stripos($classComment, "@internal") !== FALSE) { if (stripos($classComment, "@internal") !== false) {
return; return;
} }
preg_match_all('#(@.*?)\n#s', $classComment, $annotations); preg_match_all('#(@.*?)\n#s', $classComment, $annotations);
@ -208,10 +210,10 @@ class Menu extends Command
//判断注释中是否设置了icon值 //判断注释中是否设置了icon值
if (isset($annotations[1])) { if (isset($annotations[1])) {
foreach ($annotations[1] as $tag) { foreach ($annotations[1] as $tag) {
if (stripos($tag, '@icon') !== FALSE) { if (stripos($tag, '@icon') !== false) {
$controllerIcon = substr($tag, stripos($tag, ' ') + 1); $controllerIcon = substr($tag, stripos($tag, ' ') + 1);
} }
if (stripos($tag, '@remark') !== FALSE) { if (stripos($tag, '@remark') !== false) {
$controllerRemark = substr($tag, stripos($tag, ' ') + 1); $controllerRemark = substr($tag, stripos($tag, ' ') + 1);
} }
} }
@ -226,7 +228,13 @@ class Menu extends Command
$pid = 0; $pid = 0;
foreach ($controllerArr as $k => $v) { foreach ($controllerArr as $k => $v) {
$key = $k + 1; $key = $k + 1;
$name = strtolower(implode('/', array_slice($controllerArr, 0, $key))); //驼峰转下划线
$controllerNameArr = array_slice($controllerArr, 0, $key);
foreach ($controllerNameArr as &$val) {
$val = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $val), "_"));
}
unset($val);
$name = implode('/', $controllerNameArr);
$title = (!isset($controllerArr[$key]) ? $controllerTitle : ''); $title = (!isset($controllerArr[$key]) ? $controllerTitle : '');
$icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list'); $icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list');
$remark = (!isset($controllerArr[$key]) ? $controllerRemark : ''); $remark = (!isset($controllerArr[$key]) ? $controllerRemark : '');
@ -259,7 +267,7 @@ class Menu extends Command
} }
$comment = $reflector->getMethod($n->name)->getDocComment(); $comment = $reflector->getMethod($n->name)->getDocComment();
//忽略的方法 //忽略的方法
if (stripos($comment, "@internal") !== FALSE) { if (stripos($comment, "@internal") !== false) {
continue; continue;
} }
//过滤掉其它字符 //过滤掉其它字符
@ -285,5 +293,4 @@ class Menu extends Command
return $id ? $id : null; return $id ? $id : null;
} }
} }
} }

View File

@ -37,12 +37,10 @@ class Min extends Command
$resource = $input->getOption('resource') ?: ''; $resource = $input->getOption('resource') ?: '';
$optimize = $input->getOption('optimize') ?: 'none'; $optimize = $input->getOption('optimize') ?: 'none';
if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) {
{
throw new Exception('Please input correct module name'); throw new Exception('Please input correct module name');
} }
if (!$resource || !in_array($resource, ['js', 'css', 'all'])) if (!$resource || !in_array($resource, ['js', 'css', 'all'])) {
{
throw new Exception('Please input correct resource name'); throw new Exception('Please input correct resource name');
} }
@ -55,41 +53,31 @@ class Min extends Command
$nodeExec = ''; $nodeExec = '';
if (!$nodeExec) if (!$nodeExec) {
{ if (IS_WIN) {
if (IS_WIN)
{
// Winsows下请手动配置配置该值,一般将该值配置为 '"C:\Program Files\nodejs\node.exe"'除非你的Node安装路径有变更 // Winsows下请手动配置配置该值,一般将该值配置为 '"C:\Program Files\nodejs\node.exe"'除非你的Node安装路径有变更
$nodeExec = 'C:\Program Files\nodejs\node.exe'; $nodeExec = 'C:\Program Files\nodejs\node.exe';
if (file_exists($nodeExec)){ if (file_exists($nodeExec)) {
$nodeExec = '"' . $nodeExec . '"'; $nodeExec = '"' . $nodeExec . '"';
}else{ } else {
// 如果 '"C:\Program Files\nodejs\node.exe"' 不存在可能是node安装路径有变更 // 如果 '"C:\Program Files\nodejs\node.exe"' 不存在可能是node安装路径有变更
// 但安装node会自动配置环境变量直接执行 '"node.exe"' 提高第一次使用压缩打包的成功率 // 但安装node会自动配置环境变量直接执行 '"node.exe"' 提高第一次使用压缩打包的成功率
$nodeExec = '"node.exe"'; $nodeExec = '"node.exe"';
} }
} } else {
else try {
{
try
{
$nodeExec = exec("which node"); $nodeExec = exec("which node");
if (!$nodeExec) if (!$nodeExec) {
{
throw new Exception("node environment not found!please install node first!"); throw new Exception("node environment not found!please install node first!");
} }
} } catch (Exception $e) {
catch (Exception $e)
{
throw new Exception($e->getMessage()); throw new Exception($e->getMessage());
} }
} }
} }
foreach ($moduleArr as $mod) foreach ($moduleArr as $mod) {
{ foreach ($resourceArr as $res) {
foreach ($resourceArr as $res)
{
$data = [ $data = [
'publicPath' => $publicPath, 'publicPath' => $publicPath,
'jsBaseName' => str_replace('{module}', $mod, $this->options['jsBaseName']), 'jsBaseName' => str_replace('{module}', $mod, $this->options['jsBaseName']),
@ -104,17 +92,14 @@ class Min extends Command
//源文件 //源文件
$from = $data["{$res}BasePath"] . $data["{$res}BaseName"] . '.' . $res; $from = $data["{$res}BasePath"] . $data["{$res}BaseName"] . '.' . $res;
if (!is_file($from)) if (!is_file($from)) {
{
$output->error("{$res} source file not found!file:{$from}"); $output->error("{$res} source file not found!file:{$from}");
continue; continue;
} }
if ($res == "js") if ($res == "js") {
{
$content = file_get_contents($from); $content = file_get_contents($from);
preg_match("/require\.config\(\{[\r\n]?[\n]?+(.*?)[\r\n]?[\n]?}\);/is", $content, $matches); preg_match("/require\.config\(\{[\r\n]?[\n]?+(.*?)[\r\n]?[\n]?}\);/is", $content, $matches);
if (!isset($matches[1])) if (!isset($matches[1])) {
{
$output->error("js config not found!"); $output->error("js config not found!");
continue; continue;
} }
@ -128,16 +113,14 @@ class Min extends Command
// 执行压缩 // 执行压缩
$command = "{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\""; $command = "{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\"";
if ($output->isDebug()) if ($output->isDebug()) {
{
$output->warning($command); $output->warning($command);
} }
echo exec($command); echo exec($command);
} }
} }
if (!$output->isDebug()) if (!$output->isDebug()) {
{
@unlink($tempFile); @unlink($tempFile);
} }
@ -154,16 +137,14 @@ class Min extends Command
protected function writeToFile($name, $data, $pathname) protected function writeToFile($name, $data, $pathname)
{ {
$search = $replace = []; $search = $replace = [];
foreach ($data as $k => $v) foreach ($data as $k => $v) {
{
$search[] = "{%{$k}%}"; $search[] = "{%{$k}%}";
$replace[] = $v; $replace[] = $v;
} }
$stub = file_get_contents($this->getStub($name)); $stub = file_get_contents($this->getStub($name));
$content = str_replace($search, $replace, $stub); $content = str_replace($search, $replace, $stub);
if (!is_dir(dirname($pathname))) if (!is_dir(dirname($pathname))) {
{
mkdir(strtolower(dirname($pathname)), 0755, true); mkdir(strtolower(dirname($pathname)), 0755, true);
} }
return file_put_contents($pathname, $content); return file_put_contents($pathname, $content);
@ -178,5 +159,4 @@ class Min extends Command
{ {
return __DIR__ . DS . 'Min' . DS . 'stubs' . DS . $name . '.stub'; return __DIR__ . DS . 'Min' . DS . 'stubs' . DS . $name . '.stub';
} }
} }

View File

@ -74,6 +74,7 @@ if (!function_exists('build_category_select')) {
* @param string $type * @param string $type
* @param mixed $selected * @param mixed $selected
* @param array $attr * @param array $attr
* @param array $header
* @return string * @return string
*/ */
function build_category_select($name, $type, $selected = null, $attr = [], $header = []) function build_category_select($name, $type, $selected = null, $attr = [], $header = [])
@ -98,14 +99,14 @@ if (!function_exists('build_toolbar')) {
* @param array $attr 按钮属性值 * @param array $attr 按钮属性值
* @return string * @return string
*/ */
function build_toolbar($btns = NULL, $attr = []) function build_toolbar($btns = null, $attr = [])
{ {
$auth = \app\admin\library\Auth::instance(); $auth = \app\admin\library\Auth::instance();
$controller = str_replace('.', '/', strtolower(think\Request::instance()->controller())); $controller = str_replace('.', '/', strtolower(think\Request::instance()->controller()));
$btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import']; $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import'];
$btns = is_array($btns) ? $btns : explode(',', $btns); $btns = is_array($btns) ? $btns : explode(',', $btns);
$index = array_search('delete', $btns); $index = array_search('delete', $btns);
if ($index !== FALSE) { if ($index !== false) {
$btns[$index] = 'del'; $btns[$index] = 'del';
} }
$btnAttr = [ $btnAttr = [
@ -140,7 +141,7 @@ if (!function_exists('build_toolbar')) {
} }
$download .= empty($download) ? '' : "\n "; $download .= empty($download) ? '' : "\n ";
if (!empty($download)) { if (!empty($download)) {
$html[] = <<<EOT $html[] = <<<EOT
<div class="btn-group"> <div class="btn-group">
<button type="button" href="{$href}" class="btn btn-info btn-import" title="{$title}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="{$icon}"></i> {$text}</button> <button type="button" href="{$href}" class="btn btn-info btn-import" title="{$title}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="{$icon}"></i> {$text}</button>
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" title="下载批量导入模版"> <button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" title="下载批量导入模版">
@ -169,7 +170,7 @@ if (!function_exists('build_heading')) {
* @param string $path 指定的path * @param string $path 指定的path
* @return string * @return string
*/ */
function build_heading($path = NULL, $container = TRUE) function build_heading($path = null, $container = true)
{ {
$title = $content = ''; $title = $content = '';
if (is_null($path)) { if (is_null($path)) {
@ -183,8 +184,9 @@ if (!function_exists('build_heading')) {
$title = __($data['title']); $title = __($data['title']);
$content = __($data['remark']); $content = __($data['remark']);
} }
if (!$content) if (!$content) {
return ''; return '';
}
$result = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>'; $result = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container) { if ($container) {
$result = '<div class="panel-heading">' . $result . '</div>'; $result = '<div class="panel-heading">' . $result . '</div>';

View File

@ -37,21 +37,16 @@ class Group extends Backend
Tree::instance()->init($groupList); Tree::instance()->init($groupList);
$result = []; $result = [];
if ($this->auth->isSuperAdmin()) if ($this->auth->isSuperAdmin()) {
{
$result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0)); $result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0));
} } else {
else
{
$groups = $this->auth->getGroups(); $groups = $this->auth->getGroups();
foreach ($groups as $m => $n) foreach ($groups as $m => $n) {
{
$result = array_merge($result, Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['pid']))); $result = array_merge($result, Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['pid'])));
} }
} }
$groupName = []; $groupName = [];
foreach ($result as $k => $v) foreach ($result as $k => $v) {
{
$groupName[$v['id']] = $v['name']; $groupName[$v['id']] = $v['name'];
} }
@ -66,20 +61,16 @@ class Group extends Backend
*/ */
public function index() public function index()
{ {
if ($this->request->isAjax()) if ($this->request->isAjax()) {
{
$list = AuthGroup::all(array_keys($this->groupdata)); $list = AuthGroup::all(array_keys($this->groupdata));
$list = collection($list)->toArray(); $list = collection($list)->toArray();
$groupList = []; $groupList = [];
foreach ($list as $k => $v) foreach ($list as $k => $v) {
{
$groupList[$v['id']] = $v; $groupList[$v['id']] = $v;
} }
$list = []; $list = [];
foreach ($this->groupdata as $k => $v) foreach ($this->groupdata as $k => $v) {
{ if (isset($groupList[$k])) {
if (isset($groupList[$k]))
{
$groupList[$k]['name'] = $v; $groupList[$k]['name'] = $v;
$list[] = $groupList[$k]; $list[] = $groupList[$k];
} }
@ -97,17 +88,14 @@ class Group extends Backend
*/ */
public function add() public function add()
{ {
if ($this->request->isPost()) if ($this->request->isPost()) {
{
$params = $this->request->post("row/a", [], 'strip_tags'); $params = $this->request->post("row/a", [], 'strip_tags');
$params['rules'] = explode(',', $params['rules']); $params['rules'] = explode(',', $params['rules']);
if (!in_array($params['pid'], $this->childrenGroupIds)) if (!in_array($params['pid'], $this->childrenGroupIds)) {
{
$this->error(__('The parent group can not be its own child')); $this->error(__('The parent group can not be its own child'));
} }
$parentmodel = model("AuthGroup")->get($params['pid']); $parentmodel = model("AuthGroup")->get($params['pid']);
if (!$parentmodel) if (!$parentmodel) {
{
$this->error(__('The parent group can not found')); $this->error(__('The parent group can not found'));
} }
// 父级别的规则节点 // 父级别的规则节点
@ -120,8 +108,7 @@ class Group extends Backend
// 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限 // 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限
$rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules); $rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules);
$params['rules'] = implode(',', $rules); $params['rules'] = implode(',', $rules);
if ($params) if ($params) {
{
$this->model->create($params); $this->model->create($params);
$this->success(); $this->success();
} }
@ -133,24 +120,22 @@ class Group extends Backend
/** /**
* 编辑 * 编辑
*/ */
public function edit($ids = NULL) public function edit($ids = null)
{ {
$row = $this->model->get(['id' => $ids]); $row = $this->model->get(['id' => $ids]);
if (!$row) if (!$row) {
$this->error(__('No Results were found')); $this->error(__('No Results were found'));
if ($this->request->isPost()) }
{ if ($this->request->isPost()) {
$params = $this->request->post("row/a", [], 'strip_tags'); $params = $this->request->post("row/a", [], 'strip_tags');
// 父节点不能是它自身的子节点 // 父节点不能是它自身的子节点
if (!in_array($params['pid'], $this->childrenGroupIds)) if (!in_array($params['pid'], $this->childrenGroupIds)) {
{
$this->error(__('The parent group can not be its own child')); $this->error(__('The parent group can not be its own child'));
} }
$params['rules'] = explode(',', $params['rules']); $params['rules'] = explode(',', $params['rules']);
$parentmodel = model("AuthGroup")->get($params['pid']); $parentmodel = model("AuthGroup")->get($params['pid']);
if (!$parentmodel) if (!$parentmodel) {
{
$this->error(__('The parent group can not found')); $this->error(__('The parent group can not found'));
} }
// 父级别的规则节点 // 父级别的规则节点
@ -163,8 +148,7 @@ class Group extends Backend
// 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限 // 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限
$rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules); $rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules);
$params['rules'] = implode(',', $rules); $params['rules'] = implode(',', $rules);
if ($params) if ($params) {
{
$row->save($params); $row->save($params);
$this->success(); $this->success();
} }
@ -180,11 +164,10 @@ class Group extends Backend
*/ */
public function del($ids = "") public function del($ids = "")
{ {
if ($ids) if ($ids) {
{
$ids = explode(',', $ids); $ids = explode(',', $ids);
$grouplist = $this->auth->getGroups(); $grouplist = $this->auth->getGroups();
$group_ids = array_map(function($group) { $group_ids = array_map(function ($group) {
return $group['id']; return $group['id'];
}, $grouplist); }, $grouplist);
// 移除掉当前管理员所在组别 // 移除掉当前管理员所在组别
@ -193,30 +176,25 @@ class Group extends Backend
// 循环判断每一个组别是否可删除 // 循环判断每一个组别是否可删除
$grouplist = $this->model->where('id', 'in', $ids)->select(); $grouplist = $this->model->where('id', 'in', $ids)->select();
$groupaccessmodel = model('AuthGroupAccess'); $groupaccessmodel = model('AuthGroupAccess');
foreach ($grouplist as $k => $v) foreach ($grouplist as $k => $v) {
{
// 当前组别下有管理员 // 当前组别下有管理员
$groupone = $groupaccessmodel->get(['group_id' => $v['id']]); $groupone = $groupaccessmodel->get(['group_id' => $v['id']]);
if ($groupone) if ($groupone) {
{
$ids = array_diff($ids, [$v['id']]); $ids = array_diff($ids, [$v['id']]);
continue; continue;
} }
// 当前组别下有子组别 // 当前组别下有子组别
$groupone = $this->model->get(['pid' => $v['id']]); $groupone = $this->model->get(['pid' => $v['id']]);
if ($groupone) if ($groupone) {
{
$ids = array_diff($ids, [$v['id']]); $ids = array_diff($ids, [$v['id']]);
continue; continue;
} }
} }
if (!$ids) if (!$ids) {
{
$this->error(__('You can not delete group that contain child group and administrators')); $this->error(__('You can not delete group that contain child group and administrators'));
} }
$count = $this->model->where('id', 'in', $ids)->delete(); $count = $this->model->where('id', 'in', $ids)->delete();
if ($count) if ($count) {
{
$this->success(); $this->success();
} }
} }
@ -235,7 +213,7 @@ class Group extends Backend
/** /**
* 读取角色权限树 * 读取角色权限树
* *
* @internal * @internal
*/ */
public function roletree() public function roletree()
@ -246,35 +224,32 @@ class Group extends Backend
$id = $this->request->post("id"); $id = $this->request->post("id");
$pid = $this->request->post("pid"); $pid = $this->request->post("pid");
$parentGroupModel = $model->get($pid); $parentGroupModel = $model->get($pid);
$currentGroupModel = NULL; $currentGroupModel = null;
if ($id) if ($id) {
{
$currentGroupModel = $model->get($id); $currentGroupModel = $model->get($id);
} }
if (($pid || $parentGroupModel) && (!$id || $currentGroupModel)) if (($pid || $parentGroupModel) && (!$id || $currentGroupModel)) {
{ $id = $id ? $id : null;
$id = $id ? $id : NULL;
$ruleList = collection(model('AuthRule')->order('weigh', 'desc')->order('id', 'asc')->select())->toArray(); $ruleList = collection(model('AuthRule')->order('weigh', 'desc')->order('id', 'asc')->select())->toArray();
//读取父类角色所有节点列表 //读取父类角色所有节点列表
$parentRuleList = []; $parentRuleList = [];
if (in_array('*', explode(',', $parentGroupModel->rules))) if (in_array('*', explode(',', $parentGroupModel->rules))) {
{
$parentRuleList = $ruleList; $parentRuleList = $ruleList;
} } else {
else
{
$parentRuleIds = explode(',', $parentGroupModel->rules); $parentRuleIds = explode(',', $parentGroupModel->rules);
foreach ($ruleList as $k => $v) foreach ($ruleList as $k => $v) {
{ if (in_array($v['id'], $parentRuleIds)) {
if (in_array($v['id'], $parentRuleIds))
{
$parentRuleList[] = $v; $parentRuleList[] = $v;
} }
} }
} }
$ruleTree = new Tree();
$groupTree = new Tree();
//当前所有正常规则列表 //当前所有正常规则列表
Tree::instance()->init($parentRuleList); $ruleTree->init($parentRuleList);
//角色组列表
$groupTree->init(collection(model('AuthGroup')->where('id', 'in', $this->childrenGroupIds)->select())->toArray());
//读取当前角色下规则ID集合 //读取当前角色下规则ID集合
$adminRuleIds = $this->auth->getRuleIds(); $adminRuleIds = $this->auth->getRuleIds();
@ -283,39 +258,34 @@ class Group extends Backend
//当前拥有的规则ID集合 //当前拥有的规则ID集合
$currentRuleIds = $id ? explode(',', $currentGroupModel->rules) : []; $currentRuleIds = $id ? explode(',', $currentGroupModel->rules) : [];
if (!$id || !in_array($pid, Tree::instance()->getChildrenIds($id, TRUE))) if (!$id || !in_array($pid, $this->childrenGroupIds) || !in_array($pid, $groupTree->getChildrenIds($id, true))) {
{ $parentRuleList = $ruleTree->getTreeList($ruleTree->getTreeArray(0), 'name');
$parentRuleList = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'name');
$hasChildrens = []; $hasChildrens = [];
foreach ($parentRuleList as $k => $v) foreach ($parentRuleList as $k => $v) {
{ if ($v['haschild']) {
if ($v['haschild'])
$hasChildrens[] = $v['id']; $hasChildrens[] = $v['id'];
}
} }
$parentRuleIds = array_map(function($item) { $parentRuleIds = array_map(function ($item) {
return $item['id']; return $item['id'];
}, $parentRuleList); }, $parentRuleList);
$nodeList = []; $nodeList = [];
foreach ($parentRuleList as $k => $v) foreach ($parentRuleList as $k => $v) {
{ if (!$superadmin && !in_array($v['id'], $adminRuleIds)) {
if (!$superadmin && !in_array($v['id'], $adminRuleIds))
continue; continue;
if ($v['pid'] && !in_array($v['pid'], $parentRuleIds)) }
if ($v['pid'] && !in_array($v['pid'], $parentRuleIds)) {
continue; continue;
}
$state = array('selected' => in_array($v['id'], $currentRuleIds) && !in_array($v['id'], $hasChildrens)); $state = array('selected' => in_array($v['id'], $currentRuleIds) && !in_array($v['id'], $hasChildrens));
$nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state); $nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state);
} }
$this->success('', null, $nodeList); $this->success('', null, $nodeList);
} } else {
else
{
$this->error(__('Can not change the parent to child')); $this->error(__('Can not change the parent to child'));
} }
} } else {
else
{
$this->error(__('Group not found')); $this->error(__('Group not found'));
} }
} }
} }

View File

@ -113,6 +113,12 @@ return [
'%d year%s ago' => '%d年前', '%d year%s ago' => '%d年前',
'Set to normal' => '设为正常', 'Set to normal' => '设为正常',
'Set to hidden' => '设为隐藏', 'Set to hidden' => '设为隐藏',
'Recycle bin' => '回收站',
'Restore' => '还原',
'Restore all' => '还原全部',
'Destroy' => '销毁',
'Destroy all' => '清空回收站',
'Nothing need restore' => '没有需要还原的数据',
//提示 //提示
'Go back' => '返回首页', 'Go back' => '返回首页',
'Jump now' => '立即跳转', 'Jump now' => '立即跳转',
@ -130,6 +136,7 @@ return [
'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?', 'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?',
'Are you sure you want to delete this item?' => '确定删除此项?', 'Are you sure you want to delete this item?' => '确定删除此项?',
'Are you sure you want to delete or turncate?' => '确定删除或清空?', 'Are you sure you want to delete or turncate?' => '确定删除或清空?',
'Are you sure you want to truncate?' => '确定清空?',
'You have no permission' => '你没有权限访问', 'You have no permission' => '你没有权限访问',
'Please enter your username' => '请输入你的用户名', 'Please enter your username' => '请输入你的用户名',
'Please enter your password' => '请输入你的密码', 'Please enter your password' => '请输入你的密码',

View File

@ -7,6 +7,8 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xls; use PhpOffice\PhpSpreadsheet\Reader\Xls;
use PhpOffice\PhpSpreadsheet\Reader\Csv; use PhpOffice\PhpSpreadsheet\Reader\Csv;
use think\Exception;
trait Backend trait Backend
{ {
@ -19,15 +21,12 @@ trait Backend
{ {
if (is_array($this->excludeFields)) { if (is_array($this->excludeFields)) {
foreach ($this->excludeFields as $field) { foreach ($this->excludeFields as $field) {
if (key_exists($field,$params)) if (key_exists($field, $params)) {
{
unset($params[$field]); unset($params[$field]);
} }
} }
} else { } else {
if (key_exists($this->excludeFields, $params)) {
if (key_exists($this->excludeFields,$params))
{
unset($params[$this->excludeFields]); unset($params[$this->excludeFields]);
} }
} }
@ -104,7 +103,6 @@ trait Backend
if ($this->request->isPost()) { if ($this->request->isPost()) {
$params = $this->request->post("row/a"); $params = $this->request->post("row/a");
if ($params) { if ($params) {
$params = $this->preExcludeFields($params); $params = $this->preExcludeFields($params);
if ($this->dataLimit && $this->dataLimitFieldAutoFill) { if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
@ -137,11 +135,12 @@ trait Backend
/** /**
* 编辑 * 编辑
*/ */
public function edit($ids = NULL) public function edit($ids = null)
{ {
$row = $this->model->get($ids); $row = $this->model->get($ids);
if (!$row) if (!$row) {
$this->error(__('No Results were found')); $this->error(__('No Results were found'));
}
$adminIds = $this->getDataLimitAdminIds(); $adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) { if (is_array($adminIds)) {
if (!in_array($row[$this->dataLimitField], $adminIds)) { if (!in_array($row[$this->dataLimitField], $adminIds)) {
@ -151,7 +150,6 @@ trait Backend
if ($this->request->isPost()) { if ($this->request->isPost()) {
$params = $this->request->post("row/a"); $params = $this->request->post("row/a");
if ($params) { if ($params) {
$params = $this->preExcludeFields($params); $params = $this->preExcludeFields($params);
try { try {
@ -419,5 +417,4 @@ trait Backend
$this->success(); $this->success();
} }
} }

View File

@ -100,6 +100,7 @@
<input id="c-{$item.name}" class="form-control" size="50" name="row[{$item.name}]" type="text" value="{$item.value}" data-tip="{$item.tip}"> <input id="c-{$item.name}" class="form-control" size="50" name="row[{$item.name}]" type="text" value="{$item.value}" data-tip="{$item.tip}">
<span><button type="button" id="plupload-{$item.name}" class="btn btn-danger plupload" data-input-id="c-{$item.name}" data-mimetype="image/*" data-multiple="{$item.type=='image'?'false':'true'}" data-preview-id="p-{$item.name}"><i class="fa fa-upload"></i> {:__('Upload')}</button></span> <span><button type="button" id="plupload-{$item.name}" class="btn btn-danger plupload" data-input-id="c-{$item.name}" data-mimetype="image/*" data-multiple="{$item.type=='image'?'false':'true'}" data-preview-id="p-{$item.name}"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-{$item.name}" class="btn btn-primary fachoose" data-input-id="c-{$item.name}" data-mimetype="image/*" data-multiple="{$item.type=='image'?'false':'true'}"><i class="fa fa-list"></i> {:__('Choose')}</button></span> <span><button type="button" id="fachoose-{$item.name}" class="btn btn-primary fachoose" data-input-id="c-{$item.name}" data-mimetype="image/*" data-multiple="{$item.type=='image'?'false':'true'}"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
<span class="msg-box n-right" for="c-{$item.name}"></span>
<ul class="row list-inline plupload-preview" id="p-{$item.name}"></ul> <ul class="row list-inline plupload-preview" id="p-{$item.name}"></ul>
</div> </div>
{/case} {/case}
@ -109,6 +110,7 @@
<input id="c-{$item.name}" class="form-control" size="50" name="row[{$item.name}]" type="text" value="{$item.value}" data-tip="{$item.tip}"> <input id="c-{$item.name}" class="form-control" size="50" name="row[{$item.name}]" type="text" value="{$item.value}" data-tip="{$item.tip}">
<span><button type="button" id="plupload-{$item.name}" class="btn btn-danger plupload" data-input-id="c-{$item.name}" data-multiple="{$item.type=='file'?'false':'true'}"><i class="fa fa-upload"></i> {:__('Upload')}</button></span> <span><button type="button" id="plupload-{$item.name}" class="btn btn-danger plupload" data-input-id="c-{$item.name}" data-multiple="{$item.type=='file'?'false':'true'}"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-{$item.name}" class="btn btn-primary fachoose" data-input-id="c-{$item.name}" data-multiple="{$item.type=='file'?'false':'true'}"><i class="fa fa-list"></i> {:__('Choose')}</button></span> <span><button type="button" id="fachoose-{$item.name}" class="btn btn-primary fachoose" data-input-id="c-{$item.name}" data-multiple="{$item.type=='file'?'false':'true'}"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
<span class="msg-box n-right" for="c-{$item.name}"></span>
</div> </div>
{/case} {/case}
{case switch} {case switch}

View File

@ -7,7 +7,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="nickname" class="control-label col-xs-12 col-sm-2">{:__('Permission')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Permission')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
<span class="text-muted"><input type="checkbox" name="" id="checkall" /> <label for="checkall"><small>{:__('Check all')}</small></label></span> <span class="text-muted"><input type="checkbox" name="" id="checkall" /> <label for="checkall"><small>{:__('Check all')}</small></label></span>
<span class="text-muted"><input type="checkbox" name="" id="expandall" /> <label for="expandall"><small>{:__('Expand all')}</small></label></span> <span class="text-muted"><input type="checkbox" name="" id="expandall" /> <label for="expandall"><small>{:__('Expand all')}</small></label></span>
@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="c-status" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
<div class="radio"> <div class="radio">

View File

@ -7,7 +7,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="nickname" class="control-label col-xs-12 col-sm-2">{:__('Permission')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Permission')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
<span class="text-muted"><input type="checkbox" name="" id="checkall" /> <label for="checkall"><small>{:__('Check all')}</small></label></span> <span class="text-muted"><input type="checkbox" name="" id="checkall" /> <label for="checkall"><small>{:__('Check all')}</small></label></span>
<span class="text-muted"><input type="checkbox" name="" id="expandall" /> <label for="expandall"><small>{:__('Expand all')}</small></label></span> <span class="text-muted"><input type="checkbox" name="" id="expandall" /> <label for="expandall"><small>{:__('Expand all')}</small></label></span>
@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="c-status" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
<div class="radio"> <div class="radio">

View File

@ -1,13 +1,13 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action=""> <form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group"> <div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Ismenu')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Ismenu')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
{:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])} {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="c-pid" class="control-label col-xs-12 col-sm-2">{:__('Pid')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Pid')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
{:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])} {:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])}
</div> </div>
@ -37,7 +37,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div> </div>

View File

@ -1,13 +1,13 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action=""> <form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group"> <div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Ismenu')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Ismenu')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
{:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])} {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pid" class="control-label col-xs-12 col-sm-2">{:__('Parent')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Parent')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
{:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])} {:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])}
</div> </div>
@ -37,7 +37,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label> <label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8"> <div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div> </div>

View File

@ -21,7 +21,7 @@
<div class="form-group"> <div class="form-group">
<label for="c-password" class="control-label col-xs-12 col-sm-2">{:__('Password')}:</label> <label for="c-password" class="control-label col-xs-12 col-sm-2">{:__('Password')}:</label>
<div class="col-xs-12 col-sm-4"> <div class="col-xs-12 col-sm-4">
<input id="c-password" data-rule="" class="form-control" name="row[password]" type="text" value="" placeholder="{:__('Leave password blank if dont want to change')}" autocomplete="new-password" /> <input id="c-password" data-rule="password" class="form-control" name="row[password]" type="text" value="" placeholder="{:__('Leave password blank if dont want to change')}" autocomplete="new-password" />
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -32,7 +32,7 @@ class User extends Api
/** /**
* 会员登录 * 会员登录
* *
* @param string $account 账号 * @param string $account 账号
* @param string $password 密码 * @param string $password 密码
*/ */
@ -40,25 +40,21 @@ class User extends Api
{ {
$account = $this->request->request('account'); $account = $this->request->request('account');
$password = $this->request->request('password'); $password = $this->request->request('password');
if (!$account || !$password) if (!$account || !$password) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
$ret = $this->auth->login($account, $password); $ret = $this->auth->login($account, $password);
if ($ret) if ($ret) {
{
$data = ['userinfo' => $this->auth->getUserinfo()]; $data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Logged in successful'), $data); $this->success(__('Logged in successful'), $data);
} } else {
else
{
$this->error($this->auth->getError()); $this->error($this->auth->getError());
} }
} }
/** /**
* 手机验证码登录 * 手机验证码登录
* *
* @param string $mobile 手机号 * @param string $mobile 手机号
* @param string $captcha 验证码 * @param string $captcha 验证码
*/ */
@ -66,43 +62,34 @@ class User extends Api
{ {
$mobile = $this->request->request('mobile'); $mobile = $this->request->request('mobile');
$captcha = $this->request->request('captcha'); $captcha = $this->request->request('captcha');
if (!$mobile || !$captcha) if (!$mobile || !$captcha) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
if (!Validate::regex($mobile, "^1\d{10}$")) if (!Validate::regex($mobile, "^1\d{10}$")) {
{
$this->error(__('Mobile is incorrect')); $this->error(__('Mobile is incorrect'));
} }
if (!Sms::check($mobile, $captcha, 'mobilelogin')) if (!Sms::check($mobile, $captcha, 'mobilelogin')) {
{
$this->error(__('Captcha is incorrect')); $this->error(__('Captcha is incorrect'));
} }
$user = \app\common\model\User::getByMobile($mobile); $user = \app\common\model\User::getByMobile($mobile);
if ($user) if ($user) {
{
//如果已经有账号则直接登录 //如果已经有账号则直接登录
$ret = $this->auth->direct($user->id); $ret = $this->auth->direct($user->id);
} } else {
else
{
$ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []); $ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []);
} }
if ($ret) if ($ret) {
{
Sms::flush($mobile, 'mobilelogin'); Sms::flush($mobile, 'mobilelogin');
$data = ['userinfo' => $this->auth->getUserinfo()]; $data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Logged in successful'), $data); $this->success(__('Logged in successful'), $data);
} } else {
else
{
$this->error($this->auth->getError()); $this->error($this->auth->getError());
} }
} }
/** /**
* 注册会员 * 注册会员
* *
* @param string $username 用户名 * @param string $username 用户名
* @param string $password 密码 * @param string $password 密码
* @param string $email 邮箱 * @param string $email 邮箱
@ -114,26 +101,20 @@ class User extends Api
$password = $this->request->request('password'); $password = $this->request->request('password');
$email = $this->request->request('email'); $email = $this->request->request('email');
$mobile = $this->request->request('mobile'); $mobile = $this->request->request('mobile');
if (!$username || !$password) if (!$username || !$password) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
if ($email && !Validate::is($email, "email")) if ($email && !Validate::is($email, "email")) {
{
$this->error(__('Email is incorrect')); $this->error(__('Email is incorrect'));
} }
if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) {
{
$this->error(__('Mobile is incorrect')); $this->error(__('Mobile is incorrect'));
} }
$ret = $this->auth->register($username, $password, $email, $mobile, []); $ret = $this->auth->register($username, $password, $email, $mobile, []);
if ($ret) if ($ret) {
{
$data = ['userinfo' => $this->auth->getUserinfo()]; $data = ['userinfo' => $this->auth->getUserinfo()];
$this->success(__('Sign up successful'), $data); $this->success(__('Sign up successful'), $data);
} } else {
else
{
$this->error($this->auth->getError()); $this->error($this->auth->getError());
} }
} }
@ -149,7 +130,7 @@ class User extends Api
/** /**
* 修改会员个人信息 * 修改会员个人信息
* *
* @param string $avatar 头像地址 * @param string $avatar 头像地址
* @param string $username 用户名 * @param string $username 用户名
* @param string $nickname 昵称 * @param string $nickname 昵称
@ -162,12 +143,13 @@ class User extends Api
$nickname = $this->request->request('nickname'); $nickname = $this->request->request('nickname');
$bio = $this->request->request('bio'); $bio = $this->request->request('bio');
$avatar = $this->request->request('avatar'); $avatar = $this->request->request('avatar');
$exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find(); if ($username) {
if ($exists) $exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find();
{ if ($exists) {
$this->error(__('Username already exists')); $this->error(__('Username already exists'));
}
$user->username = $username;
} }
$user->username = $username;
$user->nickname = $nickname; $user->nickname = $nickname;
$user->bio = $bio; $user->bio = $bio;
$user->avatar = $avatar; $user->avatar = $avatar;
@ -177,7 +159,7 @@ class User extends Api
/** /**
* 修改邮箱 * 修改邮箱
* *
* @param string $email 邮箱 * @param string $email 邮箱
* @param string $captcha 验证码 * @param string $captcha 验证码
*/ */
@ -186,21 +168,17 @@ class User extends Api
$user = $this->auth->getUser(); $user = $this->auth->getUser();
$email = $this->request->post('email'); $email = $this->request->post('email');
$captcha = $this->request->request('captcha'); $captcha = $this->request->request('captcha');
if (!$email || !$captcha) if (!$email || !$captcha) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
if (!Validate::is($email, "email")) if (!Validate::is($email, "email")) {
{
$this->error(__('Email is incorrect')); $this->error(__('Email is incorrect'));
} }
if (\app\common\model\User::where('email', $email)->where('id', '<>', $user->id)->find()) if (\app\common\model\User::where('email', $email)->where('id', '<>', $user->id)->find()) {
{
$this->error(__('Email already exists')); $this->error(__('Email already exists'));
} }
$result = Ems::check($email, $captcha, 'changeemail'); $result = Ems::check($email, $captcha, 'changeemail');
if (!$result) if (!$result) {
{
$this->error(__('Captcha is incorrect')); $this->error(__('Captcha is incorrect'));
} }
$verification = $user->verification; $verification = $user->verification;
@ -215,7 +193,7 @@ class User extends Api
/** /**
* 修改手机号 * 修改手机号
* *
* @param string $email 手机号 * @param string $email 手机号
* @param string $captcha 验证码 * @param string $captcha 验证码
*/ */
@ -224,21 +202,17 @@ class User extends Api
$user = $this->auth->getUser(); $user = $this->auth->getUser();
$mobile = $this->request->request('mobile'); $mobile = $this->request->request('mobile');
$captcha = $this->request->request('captcha'); $captcha = $this->request->request('captcha');
if (!$mobile || !$captcha) if (!$mobile || !$captcha) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
if (!Validate::regex($mobile, "^1\d{10}$")) if (!Validate::regex($mobile, "^1\d{10}$")) {
{
$this->error(__('Mobile is incorrect')); $this->error(__('Mobile is incorrect'));
} }
if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) {
{
$this->error(__('Mobile already exists')); $this->error(__('Mobile already exists'));
} }
$result = Sms::check($mobile, $captcha, 'changemobile'); $result = Sms::check($mobile, $captcha, 'changemobile');
if (!$result) if (!$result) {
{
$this->error(__('Captcha is incorrect')); $this->error(__('Captcha is incorrect'));
} }
$verification = $user->verification; $verification = $user->verification;
@ -253,7 +227,7 @@ class User extends Api
/** /**
* 第三方登录 * 第三方登录
* *
* @param string $platform 平台名称 * @param string $platform 平台名称
* @param string $code Code码 * @param string $code Code码
*/ */
@ -263,18 +237,15 @@ class User extends Api
$platform = $this->request->request("platform"); $platform = $this->request->request("platform");
$code = $this->request->request("code"); $code = $this->request->request("code");
$config = get_addon_config('third'); $config = get_addon_config('third');
if (!$config || !isset($config[$platform])) if (!$config || !isset($config[$platform])) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
$app = new \addons\third\library\Application($config); $app = new \addons\third\library\Application($config);
//通过code换access_token和绑定会员 //通过code换access_token和绑定会员
$result = $app->{$platform}->getUserInfo(['code' => $code]); $result = $app->{$platform}->getUserInfo(['code' => $code]);
if ($result) if ($result) {
{
$loginret = \addons\third\library\Service::connect($platform, $result); $loginret = \addons\third\library\Service::connect($platform, $result);
if ($loginret) if ($loginret) {
{
$data = [ $data = [
'userinfo' => $this->auth->getUserinfo(), 'userinfo' => $this->auth->getUserinfo(),
'thirdinfo' => $result 'thirdinfo' => $result
@ -287,7 +258,7 @@ class User extends Api
/** /**
* 重置密码 * 重置密码
* *
* @param string $mobile 手机号 * @param string $mobile 手机号
* @param string $newpassword 新密码 * @param string $newpassword 新密码
* @param string $captcha 验证码 * @param string $captcha 验证码
@ -299,42 +270,32 @@ class User extends Api
$email = $this->request->request("email"); $email = $this->request->request("email");
$newpassword = $this->request->request("newpassword"); $newpassword = $this->request->request("newpassword");
$captcha = $this->request->request("captcha"); $captcha = $this->request->request("captcha");
if (!$newpassword || !$captcha) if (!$newpassword || !$captcha) {
{
$this->error(__('Invalid parameters')); $this->error(__('Invalid parameters'));
} }
if ($type == 'mobile') if ($type == 'mobile') {
{ if (!Validate::regex($mobile, "^1\d{10}$")) {
if (!Validate::regex($mobile, "^1\d{10}$"))
{
$this->error(__('Mobile is incorrect')); $this->error(__('Mobile is incorrect'));
} }
$user = \app\common\model\User::getByMobile($mobile); $user = \app\common\model\User::getByMobile($mobile);
if (!$user) if (!$user) {
{
$this->error(__('User not found')); $this->error(__('User not found'));
} }
$ret = Sms::check($mobile, $captcha, 'resetpwd'); $ret = Sms::check($mobile, $captcha, 'resetpwd');
if (!$ret) if (!$ret) {
{
$this->error(__('Captcha is incorrect')); $this->error(__('Captcha is incorrect'));
} }
Sms::flush($mobile, 'resetpwd'); Sms::flush($mobile, 'resetpwd');
} } else {
else if (!Validate::is($email, "email")) {
{
if (!Validate::is($email, "email"))
{
$this->error(__('Email is incorrect')); $this->error(__('Email is incorrect'));
} }
$user = \app\common\model\User::getByEmail($email); $user = \app\common\model\User::getByEmail($email);
if (!$user) if (!$user) {
{
$this->error(__('User not found')); $this->error(__('User not found'));
} }
$ret = Ems::check($email, $captcha, 'resetpwd'); $ret = Ems::check($email, $captcha, 'resetpwd');
if (!$ret) if (!$ret) {
{
$this->error(__('Captcha is incorrect')); $this->error(__('Captcha is incorrect'));
} }
Ems::flush($email, 'resetpwd'); Ems::flush($email, 'resetpwd');
@ -342,12 +303,9 @@ class User extends Api
//模拟一次登录 //模拟一次登录
$this->auth->direct($user->id); $this->auth->direct($user->id);
$ret = $this->auth->changepwd($newpassword, '', true); $ret = $this->auth->changepwd($newpassword, '', true);
if ($ret) if ($ret) {
{
$this->success(__('Reset password successful')); $this->success(__('Reset password successful'));
} } else {
else
{
$this->error($this->auth->getError()); $this->error($this->auth->getError());
} }
} }

View File

@ -64,7 +64,7 @@ class Frontend extends Controller
$this->auth->init($token); $this->auth->init($token);
//检测是否登录 //检测是否登录
if (!$this->auth->isLogin()) { if (!$this->auth->isLogin()) {
$this->error(__('Please login first'), 'user/login'); $this->error(__('Please login first'), 'index/user/login');
} }
// 判断是否需要验证权限 // 判断是否需要验证权限
if (!$this->auth->match($this->noNeedRight)) { if (!$this->auth->match($this->noNeedRight)) {

View File

@ -92,6 +92,9 @@
padding-left:0px; padding-left:0px;
} }
} }
.label-primary {
background-color: #248aff;
}
</style> </style>
</head> </head>
@ -3727,7 +3730,7 @@
<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 2018-10-19 17:00:36 </div> Generated on 2019-02-26 17:13:43 </div>
<div class="col-md-6" align="right"> <div class="col-md-6" align="right">
<a href="https://www.fastadmin.net" target="_blank">FastAdmin</a> <a href="https://www.fastadmin.net" target="_blank">FastAdmin</a>
</div> </div>
@ -3929,6 +3932,9 @@
contentType: false, contentType: false,
processData: false, processData: false,
headers: headers, headers: headers,
xhrFields: {
withCredentials: true
},
success: function (data, textStatus, xhr) { success: function (data, textStatus, xhr) {
if (typeof data === 'object') { if (typeof data === 'object') {
var str = JSON.stringify(data, null, 2); var str = JSON.stringify(data, null, 2);

File diff suppressed because one or more lines are too long

View File

@ -92,7 +92,7 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
'click .btn-chooseone': function (e, value, row, index) { 'click .btn-chooseone': function (e, value, row, index) {
var multiple = Backend.api.query('multiple'); var multiple = Backend.api.query('multiple');
multiple = multiple == 'true' ? true : false; multiple = multiple == 'true' ? true : false;
Fast.api.close({url: row.url, multiple: false}); Fast.api.close({url: row.url, multiple: multiple});
}, },
}, formatter: function () { }, formatter: function () {
return '<a href="javascript:;" class="btn btn-danger btn-chooseone btn-xs"><i class="fa fa-check"></i> ' + __('Choose') + '</a>'; return '<a href="javascript:;" class="btn btn-danger btn-chooseone btn-xs"><i class="fa fa-check"></i> ' + __('Choose') + '</a>';
@ -110,7 +110,7 @@ define(['jquery', 'bootstrap', 'backend', 'form', 'table'], function ($, undefin
}); });
var multiple = Backend.api.query('multiple'); var multiple = Backend.api.query('multiple');
multiple = multiple == 'true' ? true : false; multiple = multiple == 'true' ? true : false;
Fast.api.close({url: urlArr.join(","), multiple: true}); Fast.api.close({url: urlArr.join(","), multiple: multiple});
}); });
// 为表格绑定事件 // 为表格绑定事件

View File

@ -350,7 +350,7 @@
var searchQuery = getSearchQuery(this); var searchQuery = getSearchQuery(this);
this.trigger('common-search', this, searchQuery); this.trigger('common-search', this, searchQuery);
this.options.pageNumber = 1; this.options.pageNumber = 1;
this.options.pageSize = $.fn.bootstrapTable.defaults.pageSize; //this.options.pageSize = $.fn.bootstrapTable.defaults.pageSize;
this.refresh({}); this.refresh({});
}; };

View File

@ -114,7 +114,7 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
}, },
//打开一个弹出窗口 //打开一个弹出窗口
open: function (url, title, options) { open: function (url, title, options) {
title = title ? title : ""; title = options && options.title ? options.title : (title ? title : "");
url = Fast.api.fixurl(url); url = Fast.api.fixurl(url);
url = url + (url.indexOf("?") > -1 ? "&" : "?") + "dialog=1"; url = url + (url.indexOf("?") > -1 ? "&" : "?") + "dialog=1";
var area = [$(window).width() > 800 ? '800px' : '95%', $(window).height() > 600 ? '600px' : '95%']; var area = [$(window).width() > 800 ? '800px' : '95%', $(window).height() > 600 ? '600px' : '95%'];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,10 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
showExport: true, showExport: true,
exportDataType: "all", exportDataType: "all",
exportTypes: ['json', 'xml', 'csv', 'txt', 'doc', 'excel'], exportTypes: ['json', 'xml', 'csv', 'txt', 'doc', 'excel'],
exportOptions: {
fileName: 'export_' + Moment().format("YYYY-MM-DD"),
ignoreColumn: [0, 'operate'] //默认不导出第一列(checkbox)与操作(operate)列
},
pageSize: 10, pageSize: 10,
pageList: [10, 25, 50, 'All'], pageList: [10, 25, 50, 'All'],
pagination: true, pagination: true,
@ -62,6 +66,10 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
multibtn: '.btn-multi', multibtn: '.btn-multi',
disabledbtn: '.btn-disabled', disabledbtn: '.btn-disabled',
editonebtn: '.btn-editone', editonebtn: '.btn-editone',
restoreonebtn: '.btn-restoreone',
destroyonebtn: '.btn-destroyone',
restoreallbtn: '.btn-restoreall',
destroyallbtn: '.btn-destroyall',
dragsortfield: 'weigh', dragsortfield: 'weigh',
}, },
api: { api: {
@ -203,6 +211,29 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
Fast.api.open(url, __('Edit'), $(that).data() || {}); Fast.api.open(url, __('Edit'), $(that).data() || {});
}); });
}); });
//清空回收站
$(document).on('click', Table.config.destroyallbtn, function () {
var that = this;
Layer.confirm(__('Are you sure you want to truncate?'), function () {
var url = $(that).data("url") ? $(that).data("url") : $(that).attr("href");
Fast.api.ajax(url, function () {
Layer.closeAll();
table.bootstrapTable('refresh');
}, function () {
Layer.closeAll();
});
});
return false;
});
//还原或删除
$(document).on('click', Table.config.restoreallbtn + ',' + Table.config.restoreonebtn + ',' + Table.config.destroyonebtn, function () {
var that = this;
var url = $(that).data("url") ? $(that).data("url") : $(that).attr("href");
Fast.api.ajax(url, function () {
table.bootstrapTable('refresh');
});
return false;
});
// 批量操作按钮事件 // 批量操作按钮事件
$(toolbar).on('click', Table.config.multibtn, function () { $(toolbar).on('click', Table.config.multibtn, function () {
var ids = Table.api.selectedids(table); var ids = Table.api.selectedids(table);
@ -369,14 +400,14 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
},//单元格图片预览 },//单元格图片预览
image: { image: {
'click .img-center': function (e, value, row, index) { 'click .img-center': function (e, value, row, index) {
data = []; var data = [];
value= value.split(","); value = value.split(",");
$.each(value, function (index, value) { $.each(value, function (index, value) {
data.push({ data.push({
src: Fast.api.cdnurl(value), src: Fast.api.cdnurl(value),
}); });
}); });
layer.photos({ Layer.photos({
photos: { photos: {
"data": data "data": data
}, },
@ -558,7 +589,8 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
type = typeof type === 'undefined' ? 'buttons' : type; type = typeof type === 'undefined' ? 'buttons' : type;
var options = table ? table.bootstrapTable('getOptions') : {}; var options = table ? table.bootstrapTable('getOptions') : {};
var html = []; var html = [];
var hidden, visible, disable, url, classname, icon, text, title, refresh, confirm, extend, click, dropdown, link; var hidden, visible, disable, url, classname, icon, text, title, refresh, confirm, extend,
dropdown, link;
var fieldIndex = column.fieldIndex; var fieldIndex = column.fieldIndex;
var dropdowns = {}; var dropdowns = {};
@ -573,11 +605,11 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
} }
var attr = table.data(type + "-" + j.name); var attr = table.data(type + "-" + j.name);
if (typeof attr === 'undefined' || attr) { if (typeof attr === 'undefined' || attr) {
hidden = typeof j.hidden === 'function' ? j.hidden.call(table, row, j) : (j.hidden ? j.hidden : false); hidden = typeof j.hidden === 'function' ? j.hidden.call(table, row, j) : (typeof j.hidden !== 'undefined' ? j.hidden : false);
if (hidden) { if (hidden) {
return true; return true;
} }
visible = typeof j.visible === 'function' ? j.visible.call(table, row, j) : (j.visible ? j.visible : true); visible = typeof j.visible === 'function' ? j.visible.call(table, row, j) : (typeof j.visible !== 'undefined' ? j.visible : true);
if (!visible) { if (!visible) {
return true; return true;
} }
@ -586,12 +618,12 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
url = typeof url === 'function' ? url.call(table, row, j) : (url ? Fast.api.fixurl(Table.api.replaceurl(url, row, table)) : 'javascript:;'); url = typeof url === 'function' ? url.call(table, row, j) : (url ? Fast.api.fixurl(Table.api.replaceurl(url, row, table)) : 'javascript:;');
classname = j.classname ? j.classname : 'btn-primary btn-' + name + 'one'; classname = j.classname ? j.classname : 'btn-primary btn-' + name + 'one';
icon = j.icon ? j.icon : ''; icon = j.icon ? j.icon : '';
text = j.text ? j.text : ''; text = typeof j.text === 'function' ? j.text.call(table, row, j) : j.text ? j.text : '';
title = j.title ? j.title : text; title = typeof j.title === 'function' ? j.title.call(table, row, j) : j.title ? j.title : text;
refresh = j.refresh ? 'data-refresh="' + j.refresh + '"' : ''; refresh = j.refresh ? 'data-refresh="' + j.refresh + '"' : '';
confirm = j.confirm ? 'data-confirm="' + j.confirm + '"' : ''; confirm = j.confirm ? 'data-confirm="' + j.confirm + '"' : '';
extend = j.extend ? j.extend : ''; extend = j.extend ? j.extend : '';
disable = typeof j.disable === 'function' ? j.disable.call(table, row, j) : (j.disable ? j.disable : false); disable = typeof j.disable === 'function' ? j.disable.call(table, row, j) : (typeof j.disable !== 'undefined' ? j.disable : false);
if (disable) { if (disable) {
classname = classname + ' disabled'; classname = classname + ' disabled';
} }