作者 Karson

新增一键CRUD类名冲突检测

新增一键CRUD自动传递请求参数
新增一键菜单之支持ThinkPHP5标准命名规则
新增Table.api.formatter.toggle自定义URL
新增Table.api.formatter.search自定义搜索字段
修复关联模型不同命名空间之间的BUG
优化时间字段修改器的判断
... ... @@ -10,6 +10,7 @@ use think\console\input\Option;
use think\console\Output;
use think\Db;
use think\Exception;
use think\exception\ErrorException;
use think\Lang;
use think\Loader;
... ... @@ -17,6 +18,9 @@ class Crud extends Command
{
protected $stubList = [];
protected $internalKeywords = [
'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor'
];
/**
* Selectpage搜索字段关联
*/
... ... @@ -181,6 +185,7 @@ class Crud extends Command
$controller = $input->getOption('controller');
//自定义模型
$model = $input->getOption('model');
$model = $model ? $model : $controller;
//验证器类
$validate = $model;
//自定义显示字段
... ... @@ -324,6 +329,8 @@ class Crud extends Command
$relations[] = [
//关联表基础名
'relationName' => $relationName,
//关联表类命名空间
'relationNamespace' => $relationNamespace,
//关联模型名
'relationModel' => $relationModel,
//关联文件
... ... @@ -369,6 +376,8 @@ class Crud extends Command
//视图文件
$viewArr = $controllerArr;
$lastValue = array_pop($viewArr);
$viewArr[] = Loader::parseName($lastValue, 0);
array_unshift($viewArr, 'view');
$viewDir = $adminPath . strtolower(implode(DS, $viewArr)) . DS;
... ... @@ -383,7 +392,7 @@ class Crud extends Command
//是否为删除模式
$delete = $input->getOption('delete');
if ($delete) {
$readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $langFile, $javascriptFile];
$readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $recyclebinFile, $langFile, $javascriptFile];
foreach ($readyFiles as $k => $v) {
$output->warning($v);
}
... ... @@ -409,6 +418,7 @@ class Crud extends Command
case $addFile:
case $editFile:
case $indexFile:
case $recyclebinFile:
$this->removeEmptyBaseDir($v, $viewArr);
break;
default:
... ... @@ -522,6 +532,7 @@ class Crud extends Command
}
$relation['relationForeignKey'] = $relationForeignKey;
$relation['relationPrimaryKey'] = $relationPrimaryKey;
$relation['relationClassName'] = $modelNamespace != $relation['relationNamespace'] ? $relation['relationNamespace'] . '\\' . $relation['relationName'] : $relation['relationName'];
}
unset($relation);
... ... @@ -629,9 +640,8 @@ class Crud extends Command
$defaultDateTime = "{:date('{$phpFormat}')}";
$attrArr['data-date-format'] = $format;
$attrArr['data-use-current'] = "true";
$fieldFunc = $fieldFunc ? "|{$fieldFunc}" : "";
$formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr);
$formEditElement = Form::text($fieldName, "{\$row.{$field}{$fieldFunc}}", $attrArr);
$formEditElement = Form::text($fieldName, ($fieldFunc ? "{:\$row.{$field}?{$fieldFunc}(\$row.{$field}):''}" : "{\$row.{$field}{$fieldFunc}}"), $attrArr);
} elseif ($inputType == 'checkbox' || $inputType == 'radio') {
unset($attrArr['data-rule']);
$fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName;
... ... @@ -928,7 +938,7 @@ class Crud extends Command
if ($langList) {
$this->writeToFile('lang', $data, $langFile);
}
} catch (think\exception\ErrorException $e) {
} catch (ErrorException $e) {
throw new Exception("Code: " . $e->getCode() . "\nLine: " . $e->getLine() . "\nMessage: " . $e->getMessage() . "\nFile: " . $e->getFile());
}
... ... @@ -956,7 +966,7 @@ class Crud extends Command
public function {$methodName}()
{
return [{$itemString}];
}
}
EOD;
$controllerAssignList[] = <<<EOD
\$this->view->assign("{$fieldList}", \$this->model->{$methodName}());
... ... @@ -980,7 +990,7 @@ EOD;
$attrField = ucfirst($this->getCamelizeName($field));
if ($inputType == 'datetime') {
$return = <<<EOD
return \$value && !is_numeric(\$value) ? strtotime(\$value) : \$value;
return \$value === '' ? null : (\$value && !is_numeric(\$value) ? strtotime(\$value) : \$value);
EOD;
} elseif (in_array($inputType, ['checkbox', 'select'])) {
$return = <<<EOD
... ... @@ -1069,9 +1079,9 @@ EOD;
/**
* 获取已解析相关信息
* @param string $module 模块名称
* @param string $name 自定义名称
* @param string $table 数据表名
* @param string $type 解析类型,本例中为controller、model、validate
* @param string $name 自定义名称
* @param string $table 数据表名
* @param string $type 解析类型,本例中为controller、model、validate
* @return array
*/
protected function getParseNameData($module, $name, $table, $type)
... ... @@ -1087,6 +1097,10 @@ EOD;
$parseArr = $arr;
array_push($parseArr, $parseName);
}
//类名不能为内部关键字
if (in_array(strtolower($parseName), $this->internalKeywords)) {
throw new Exception('Unable to use internal variable:' . $parseName);
}
$appNamespace = Config::get('app_namespace');
$parseNamespace = "{$appNamespace}\\{$module}\\{$type}" . ($arr ? "\\" . implode("\\", $arr) : "");
$moduleDir = APP_PATH . $module . DS;
... ... @@ -1097,7 +1111,7 @@ EOD;
/**
* 写入到文件
* @param string $name
* @param array $data
* @param array $data
* @param string $pathname
* @return mixed
*/
... ... @@ -1118,7 +1132,7 @@ EOD;
/**
* 获取替换后的数据
* @param string $name
* @param array $data
* @param array $data
* @return string
*/
protected function getReplacedStub($name, $data)
... ... @@ -1183,7 +1197,7 @@ EOD;
/**
* 读取数据和语言数组列表
* @param array $arr
* @param array $arr
* @param boolean $withTpl
* @return array
*/
... ... @@ -1303,8 +1317,8 @@ EOD;
/**
* 判断是否符合指定后缀
* @param string $field 字段名称
* @param mixed $suffixArr 后缀
* @param string $field 字段名称
* @param mixed $suffixArr 后缀
* @return boolean
*/
protected function isMatchSuffix($field, $suffixArr)
... ... @@ -1371,7 +1385,7 @@ EOD;
* @param string $field
* @param string $datatype
* @param string $extend
* @param array $itemArr
* @param array $itemArr
* @return string
*/
protected function getJsColumn($field, $datatype = '', $extend = '', $itemArr = [])
... ...
... ... @@ -5,7 +5,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: '{%controllerUrl%}/index',
index_url: '{%controllerUrl%}/index' + location.search,
add_url: '{%controllerUrl%}/add',
edit_url: '{%controllerUrl%}/edit',
del_url: '{%controllerUrl%}/del',
... ...
public function {%relationMethod%}()
{
return $this->{%relationMode%}('{%relationName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0);
return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0);
}
\ No newline at end of file
... ...
public function {%methodName%}($value, $data)
{
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
$list = $this->{%listMethodName%}();
return isset($list[$value]) ? $list[$value] : '';
... ...
public function {%methodName%}($value, $data)
{
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
$list = $this->{%listMethodName%}();
return isset($list[$value]) ? $list[$value] : '';
... ...
... ... @@ -12,6 +12,7 @@ use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
use think\Loader;
class Menu extends Command
{
... ... @@ -52,6 +53,18 @@ class Menu extends Command
$ids = [];
$list = $this->model->where(function ($query) use ($controller, $equal) {
foreach ($controller as $index => $item) {
if (stripos($item, '_') !== false) {
$item = Loader::parseName($item, 1);
}
if (stripos($item, '/') !== false) {
$controllerArr = explode('/', $item);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = Loader::parseName($controllerArr[$key]);
} else {
$controllerArr = [Loader::parseName($item)];
}
$item = str_replace('_', '\_', implode('/', $controllerArr));
if ($equal) {
$query->whereOr('name', 'eq', $item);
} else {
... ... @@ -82,10 +95,17 @@ class Menu extends Command
if (!in_array('all-controller', $controller)) {
foreach ($controller as $index => $item) {
$controllerArr = explode('/', $item);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
if (stripos($item, '_') !== false) {
$item = Loader::parseName($item, 1);
}
if (stripos($item, '/') !== false) {
$controllerArr = explode('/', $item);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
} else {
$controllerArr = [ucfirst($item)];
}
$adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php';
if (!is_file($adminPath)) {
$output->error("controller not found");
... ... @@ -159,10 +179,15 @@ class Menu extends Command
protected function importRule($controller)
{
$controller = str_replace('\\', '/', $controller);
$controllerArr = explode('/', $controller);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
if (stripos($controller, '/') !== false) {
$controllerArr = explode('/', $controller);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
} else {
$key = 0;
$controllerArr = [ucfirst($controller)];
}
$classSuffix = Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
$className = "\\app\\admin\\controller\\" . implode("\\", $controllerArr) . $classSuffix;
... ...
... ... @@ -479,14 +479,20 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
var color = typeof this.color !== 'undefined' ? this.color : 'success';
var yes = typeof this.yes !== 'undefined' ? this.yes : 1;
var no = typeof this.no !== 'undefined' ? this.no : 0;
var url = typeof this.url !== 'undefined' ? this.url : '';
return "<a href='javascript:;' data-toggle='tooltip' title='" + __('Click to toggle') + "' class='btn-change' data-id='"
+ row.id + "' data-params='" + this.field + "=" + (value == yes ? no : yes) + "'><i class='fa fa-toggle-on " + (value == yes ? 'text-' + color : 'fa-flip-horizontal text-gray') + " fa-2x'></i></a>";
+ row.id + "' " + (url ? "data-url='" + url + "'" : "") + " data-params='" + this.field + "=" + (value == yes ? no : yes) + "'><i class='fa fa-toggle-on " + (value == yes ? 'text-' + color : 'fa-flip-horizontal text-gray') + " fa-2x'></i></a>";
},
url: function (value, row, index) {
return '<div class="input-group input-group-sm" style="width:250px;margin:0 auto;"><input type="text" class="form-control input-sm" value="' + value + '"><span class="input-group-btn input-group-sm"><a href="' + value + '" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-link"></i></a></span></div>';
},
search: function (value, row, index) {
return '<a href="javascript:;" class="searchit" data-toggle="tooltip" title="' + __('Click to search %s', value) + '" data-field="' + this.field + '" data-value="' + value + '">' + value + '</a>';
var field = this.field;
if (typeof this.customField !== 'undefined' && typeof row[this.customField] !== 'undefined') {
value = row[this.customField];
field = this.customField;
}
return '<a href="javascript:;" class="searchit" data-toggle="tooltip" title="' + __('Click to search %s', value) + '" data-field="' + field + '" data-value="' + value + '">' + value + '</a>';
},
addtabs: function (value, row, index) {
var url = Table.api.replaceurl(this.url, row, this.table);
... ...