From f28089b1f0b8142ef6a27643e0a4057c2aaef3c4 Mon Sep 17 00:00:00 2001 From: Karson Date: Wed, 14 Mar 2018 16:51:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9ECRUD=E5=A4=9A=E8=A1=A8?= =?UTF-8?q?=E5=85=B3=E8=81=94=20=E6=96=B0=E5=A2=9ECRUD=E7=AD=9B=E9=80=89?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=20=E6=96=B0=E5=A2=9E=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E7=BB=84=E6=8C=89=E9=92=AEurl=E6=94=AF?= =?UTF-8?q?=E6=8C=81function=E5=8A=9F=E8=83=BD=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E6=8C=89=E9=92=AE=E7=BB=84=E6=8C=89=E9=92=AE?= =?UTF-8?q?hidden=E5=8F=82=E6=95=B0=20=E6=96=B0=E5=A2=9E=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=94=AF=E6=8C=81=E5=A4=9A=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化错误页面和跳转页面多语言 优化一键生成API文档在PHP5下的提示 优化第三方依赖包配置规则 --- README.md | 1 + application/admin/command/Api.php | 90 +- .../admin/command/Api/library/Builder.php | 2 +- .../admin/command/Api/template/index.html | 2 +- application/admin/command/Crud.php | 711 +- .../command/Crud/stubs/controllerindex.stub | 12 +- .../stubs/mixins/modelrelationmethod.stub | 2 +- .../admin/command/Crud/stubs/model.stub | 4 +- .../command/Crud/stubs/relationmodel.stub | 4 +- .../admin/command/Install/fastadmin.sql | 4 +- application/admin/command/Menu.php | 159 +- application/admin/common.php | 280 +- application/admin/lang/zh-cn.php | 2 + application/admin/lang/zh-cn/addon.php | 2 +- application/admin/library/traits/Backend.php | 1 + application/admin/view/common/header.html | 6 +- application/common/lang/zh-cn/addon.php | 2 + application/common/view/tpl/dispatch_jump.tpl | 20 +- .../common/view/tpl/think_exception.tpl | 34 +- application/config.php | 2 +- application/index/lang/en/index.php | 13 + application/index/lang/zh-cn.php | 4 + application/index/lang/zh-cn/index.php | 10 +- application/index/lang/zh-cn/user.php | 5 + application/index/view/index/index.html | 14 +- application/index/view/user/changepwd.html | 6 +- application/index/view/user/index.html | 2 +- application/index/view/user/login.html | 4 +- application/index/view/user/profile.html | 12 +- bower.json | 38 +- public/api.html | 2 +- public/assets/js/frontend/user.js | 5 +- public/assets/js/require-backend.min.js | 8332 ++++++++--------- public/assets/js/require-table.js | 70 +- 34 files changed, 4820 insertions(+), 5037 deletions(-) mode change 100644 => 100755 application/admin/command/Api.php mode change 100644 => 100755 application/admin/command/Api/library/Builder.php mode change 100644 => 100755 application/admin/command/Api/template/index.html mode change 100644 => 100755 application/admin/command/Crud.php mode change 100644 => 100755 application/admin/command/Crud/stubs/controllerindex.stub mode change 100644 => 100755 application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub mode change 100644 => 100755 application/admin/command/Crud/stubs/model.stub mode change 100644 => 100755 application/admin/command/Crud/stubs/relationmodel.stub mode change 100644 => 100755 application/admin/common.php mode change 100644 => 100755 application/admin/lang/zh-cn.php mode change 100644 => 100755 application/admin/lang/zh-cn/addon.php mode change 100644 => 100755 application/admin/library/traits/Backend.php mode change 100644 => 100755 application/admin/view/common/header.html mode change 100644 => 100755 application/common/lang/zh-cn/addon.php mode change 100644 => 100755 application/common/view/tpl/dispatch_jump.tpl mode change 100644 => 100755 application/common/view/tpl/think_exception.tpl create mode 100755 application/index/lang/en/index.php mode change 100644 => 100755 application/index/lang/zh-cn.php mode change 100644 => 100755 application/index/lang/zh-cn/index.php mode change 100644 => 100755 application/index/lang/zh-cn/user.php mode change 100644 => 100755 application/index/view/index/index.html mode change 100644 => 100755 application/index/view/user/changepwd.html mode change 100644 => 100755 application/index/view/user/index.html mode change 100644 => 100755 application/index/view/user/login.html mode change 100644 => 100755 bower.json mode change 100644 => 100755 public/api.html mode change 100644 => 100755 public/assets/js/frontend/user.js diff --git a/README.md b/README.md index c28f5b4b..9025d314 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。 * 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置 * 支持单管理员多角色 * 支持目录和控制器结构一键生成权限节点 + * 支持管理子级数据或个人数据 * 完善的前端功能组件开发 * 基于`AdminLTE`二次开发 * 基于`Bootstrap`开发,自适应手机、平板、PC diff --git a/application/admin/command/Api.php b/application/admin/command/Api.php old mode 100644 new mode 100755 index 28885148..57cdfff1 --- a/application/admin/command/Api.php +++ b/application/admin/command/Api.php @@ -17,17 +17,17 @@ class Api extends Command { $site = Config::get('site'); $this - ->setName('api') - ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') - ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') - ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') - ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') - ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) - ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) - ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name']) - ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) - ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') - ->setDescription('Compress js and css file'); + ->setName('api') + ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') + ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') + ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') + ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) + ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) + ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name']) + ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) + ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') + ->setDescription('Compress js and css file'); } protected function execute(Input $input, Output $output) @@ -37,24 +37,22 @@ class Api extends Command $force = $input->getOption('force'); $url = $input->getOption('url'); $language = $input->getOption('language'); + $language = $language ? $language : 'zh-cn'; $langFile = $apiDir . 'lang' . DS . $language . '.php'; - if (!is_file($langFile)) - { + if (!is_file($langFile)) { throw new Exception('language file not found'); } - $lang = include $langFile; + $lang = include_once $langFile; // 目标目录 $output_dir = ROOT_PATH . 'public' . DS; $output_file = $output_dir . $input->getOption('output'); - if (is_file($output_file) && !$force) - { + if (is_file($output_file) && !$force) { throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true "); } // 模板文件 $template_dir = $apiDir . 'template' . DS; $template_file = $template_dir . $input->getOption('template'); - if (!is_file($template_file)) - { + if (!is_file($template_file)) { throw new Exception('template file not found'); } // 额外的类 @@ -67,19 +65,30 @@ class Api extends Command $module = $input->getOption('module'); $moduleDir = APP_PATH . $module . DS; - if (!is_dir($moduleDir)) - { + if (!is_dir($moduleDir)) { throw new Exception('module not found'); } + + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + if (extension_loaded('opcache')) { + $configuration = opcache_get_configuration(); + $directives = $configuration['directives']; + $configName = request()->isCli() ? 'opcache.enable_cli' : 'opcache.enable'; + if (!$directives[$configName]) { + throw new Exception("Please make sure {$configName} is turned on, Get help:http://forum.fastadmin.net/d/1321"); + } + } else { + throw new Exception("Please make sure opcache already enabled, Get help:http://forum.fastadmin.net/d/1321"); + } + } + $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; $files = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY + new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY ); - foreach ($files as $name => $file) - { - if (!$file->isDir()) - { + foreach ($files as $name => $file) { + if (!$file->isDir()) { $filePath = $file->getRealPath(); $classes[] = $this->get_class_from_file($filePath); } @@ -90,12 +99,12 @@ class Api extends Command 'author' => $author, 'description' => '', 'apiurl' => $url, + 'language' => $language, ]; $builder = new Builder($classes); $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); - if (!file_put_contents($output_file, $content)) - { + if (!file_put_contents($output_file, $content)) { throw new Exception('Cannot save the content to ' . $output_file); } $output->info("Build Successed!"); @@ -103,7 +112,7 @@ class Api extends Command /** * get full qualified class name - * + * * @param string $path_to_file * @author JBYRNE http://jarretbyrne.com/2015/06/197/ * @return string @@ -120,34 +129,27 @@ class Api extends Command $getting_namespace = $getting_class = false; //Go through each token and evaluate it as necessary - foreach (token_get_all($contents) as $token) - { + foreach (token_get_all($contents) as $token) { //If this token is the namespace declaring, then flag that the next tokens will be the namespace name - if (is_array($token) && $token[0] == T_NAMESPACE) - { + if (is_array($token) && $token[0] == T_NAMESPACE) { $getting_namespace = true; } //If this token is the class declaring, then flag that the next tokens will be the class name - if (is_array($token) && $token[0] == T_CLASS) - { + if (is_array($token) && $token[0] == T_CLASS) { $getting_class = true; } //While we're grabbing the namespace name... - if ($getting_namespace === true) - { + if ($getting_namespace === true) { //If the token is a string or the namespace separator... - if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) - { + if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) { //Append the token's value to the name of the namespace $namespace .= $token[1]; - } - else if ($token === ';') - { + } else if ($token === ';') { //If the token is the semicolon, then we're done with the namespace declaration $getting_namespace = false; @@ -155,12 +157,10 @@ class Api extends Command } //While we're grabbing the class name... - if ($getting_class === true) - { + if ($getting_class === true) { //If the token is a string, it's the name of the class - if (is_array($token) && $token[0] == T_STRING) - { + if (is_array($token) && $token[0] == T_STRING) { //Store the token's value as the class name $class = $token[1]; diff --git a/application/admin/command/Api/library/Builder.php b/application/admin/command/Api/library/Builder.php old mode 100644 new mode 100755 index 6769a913..264610aa --- a/application/admin/command/Api/library/Builder.php +++ b/application/admin/command/Api/library/Builder.php @@ -31,7 +31,7 @@ class Builder public function __construct($classes = []) { $this->classes = array_merge($this->classes, $classes); - $this->view = \think\View::instance(Config::get('template'), Config::get('view_replace_str')); + $this->view = new \think\View(Config::get('template'), Config::get('view_replace_str')); } protected function extractAnnotations() diff --git a/application/admin/command/Api/template/index.html b/application/admin/command/Api/template/index.html old mode 100644 new mode 100755 index 264eae1c..0f545a79 --- a/application/admin/command/Api/template/index.html +++ b/application/admin/command/Api/template/index.html @@ -1,5 +1,5 @@ - + diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php old mode 100644 new mode 100755 index 94e3c266..fef1e16f --- a/application/admin/command/Crud.php +++ b/application/admin/command/Crud.php @@ -11,6 +11,7 @@ use think\console\Output; use think\Db; use think\Exception; use think\Lang; +use think\Loader; class Crud extends Command { @@ -105,32 +106,34 @@ class Crud extends Command protected function configure() { $this - ->setName('crud') - ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) - ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) - ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) - ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) - ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) - ->addOption('relation', 'r', Option::VALUE_OPTIONAL, 'relation table name without prefix', null) - ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL, 'relation model name', null) - ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL, 'relation foreign key', null) - ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL, 'relation primary key', null) - ->addOption('mode', 'o', Option::VALUE_OPTIONAL, 'relation table mode,hasone or belongsto', 'belongsto') - ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) - ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) - ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) - ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) - ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) - ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) - ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) - ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) - ->addOption('citysuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate citypicker component with suffix', null) - ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) - ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) - ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) - ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) - ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) - ->setDescription('Build CRUD controller and model from table'); + ->setName('crud') + ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) + ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) + ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) + ->addOption('fields', 'i', Option::VALUE_OPTIONAL, 'model visible fields', null) + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override or force delete,without tips', null) + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) + ->addOption('relation', 'r', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table name without prefix', null) + ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation model name', null) + ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation foreign key', null) + ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation primary key', null) + ->addOption('relationfields', 's', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table fields', null) + ->addOption('relationmode', 'o', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table mode,hasone or belongsto', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) + ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) + ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) + ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) + ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) + ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) + ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) + ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) + ->addOption('citysuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate citypicker component with suffix', null) + ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) + ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) + ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) + ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) + ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) + ->setDescription('Build CRUD controller and model from table'); } protected function execute(Input $input, Output $output) @@ -142,12 +145,13 @@ class Crud extends Command $controller = $input->getOption('controller'); //自定义模型 $model = $input->getOption('model'); + //自定义显示字段 + $fields = $input->getOption('fields'); //强制覆盖 $force = $input->getOption('force'); //是否为本地model,为0时表示为全局model将会把model放在app/common/model中 $local = $input->getOption('local'); - if (!$table) - { + if (!$table) { throw new Exception('table name can\'t empty'); } //是否生成菜单 @@ -157,11 +161,13 @@ class Crud extends Command //自定义关联表模型 $relationModel = $input->getOption('relationmodel'); //模式 - $mode = $input->getOption('mode'); + $relationMode = $mode = $input->getOption('relationmode'); //外键 $relationForeignKey = $input->getOption('relationforeignkey'); //主键 $relationPrimaryKey = $input->getOption('relationprimarykey'); + //关联表显示字段 + $relationFields = $input->getOption('relationfields'); //复选框后缀 $setcheckboxsuffix = $input->getOption('setcheckboxsuffix'); //单选框后缀 @@ -211,49 +217,74 @@ class Crud extends Command if ($sortfield) $this->sortField = $sortfield; - //如果有启用关联模式 - if ($relation && !in_array($mode, ['hasone', 'belongsto'])) - { - throw new Exception("relation table only work in hasone or belongsto mode"); - } - $dbname = Config::get('database.database'); $prefix = Config::get('database.prefix'); //检查主表 - $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; - $modelTableName = $tableName = $table; + $modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; $modelTableType = 'table'; - $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); - if (!$tableInfo) - { - $tableName = $prefix . $table; + $modelTableTypeName = $modelTableName = $modelName; + $modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], TRUE); + if (!$modelTableInfo) { $modelTableType = 'name'; - $tableInfo = Db::query("SHOW TABLE STATUS LIKE '{$tableName}'", [], TRUE); - if (!$tableInfo) - { + $modelTableName = $prefix . $modelName; + $modelTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], TRUE); + if (!$modelTableInfo) { throw new Exception("table not found"); } } - $tableInfo = $tableInfo[0]; + $modelTableInfo = $modelTableInfo[0]; - $relationModelTableName = $relationTableName = $relation; - $relationModelTableType = 'table'; + $relations = []; //检查关联表 - if ($relation) - { - $relation = stripos($relation, $prefix) === 0 ? substr($relation, strlen($prefix)) : $relation; - $relationModelTableName = $relationTableName = $relation; - $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); - if (!$relationTableInfo) - { - $relationTableName = $prefix . $relation; - $relationModelTableType = 'name'; + if ($relation) { + $relationArr = $relation; + $relations = []; + + foreach ($relationArr as $index => $relationTable) { + $relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable; + $relationTableType = 'table'; + $relationTableTypeName = $relationTableName = $relationName; $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); - if (!$relationTableInfo) - { - throw new Exception("relation table not found"); + if (!$relationTableInfo) { + $relationTableType = 'name'; + $relationTableName = $prefix . $relationName; + $relationTableInfo = Db::query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], TRUE); + if (!$relationTableInfo) { + throw new Exception("relation table not found"); + } } + $relationTableInfo = $relationTableInfo[0]; + $relationModel = isset($relationModel[$index]) ? $relationModel[$index] : ''; + + //关联模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入relationmodel,不支持目录层级 + $relationName = $this->getModelName($relationModel, $relationName); + $relationFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $relationName . '.php'; + + $relations[] = [ + //关联表基础名 + 'relationName' => $relationName, + //关联模型名 + 'relationModel' => $relationModel, + //关联文件 + 'relationFile' => $relationFile, + //关联表名称 + 'relationTableName' => $relationTableName, + //关联表信息 + 'relationTableInfo' => $relationTableInfo, + //关联模型表类型(name或table) + 'relationTableType' => $relationTableType, + //关联模型表类型名称 + 'relationTableTypeName' => $relationTableTypeName, + //关联模式 + 'relationFields' => isset($relationFields[$index]) ? explode(',', $relationFields[$index]) : [], + //关联模式 + 'relationMode' => isset($relationMode[$index]) ? $relationMode[$index] : 'belongsto', + //关联表外键 + 'relationForeignKey' => isset($relationForeignKey[$index]) ? $relationForeignKey[$index] : Loader::parseName($relationName) . '_id', + //关联表主键 + 'relationPrimaryKey' => isset($relationPrimaryKey[$index]) ? $relationPrimaryKey[$index] : '', + ]; } } @@ -284,27 +315,21 @@ class Crud extends Command $validateFile = $adminPath . 'validate' . DS . $modelName . '.php'; - //关联模型默认以表名进行处理,以下划线进行分隔,如果需要自定义则需要传入relationmodel,不支持目录层级 - $relationModelName = $this->getModelName($relationModel, $relation); - $relationModelFile = ($local ? $adminPath : APP_PATH . 'common' . DS) . 'model' . DS . $relationModelName . '.php'; - //是否为删除模式 $delete = $input->getOption('delete'); - if ($delete) - { + if ($delete) { $readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $langFile, $javascriptFile]; - foreach ($readyFiles as $k => $v) - { + foreach ($readyFiles as $k => $v) { $output->warning($v); } - $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); - $line = fgets(STDIN); - if (trim($line) != 'yes') - { - throw new Exception("Operation is aborted!"); + if (!$force) { + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } } - foreach ($readyFiles as $k => $v) - { + foreach ($readyFiles as $k => $v) { if (file_exists($v)) unlink($v); } @@ -314,20 +339,17 @@ class Crud extends Command } //非覆盖模式时如果存在控制器文件则报错 - if (is_file($controllerFile) && !$force) - { + if (is_file($controllerFile) && !$force) { throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true "); } //非覆盖模式时如果存在模型文件则报错 - if (is_file($modelFile) && !$force) - { + if (is_file($modelFile) && !$force) { throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true "); } //非覆盖模式时如果存在验证文件则报错 - if (is_file($validateFile) && !$force) - { + if (is_file($validateFile) && !$force) { throw new Exception("validate already exists!\nIf you need to rebuild again, use the parameter --force=true "); } @@ -335,26 +357,43 @@ class Crud extends Command //从数据库中获取表字段信息 $sql = "SELECT * FROM `information_schema`.`columns` " - . "WHERE TABLE_SCHEMA = ? AND table_name = ? " - . "ORDER BY ORDINAL_POSITION"; - $columnList = Db::query($sql, [$dbname, $tableName]); - $relationColumnList = []; - if ($relation) - { - $relationColumnList = Db::query($sql, [$dbname, $relationTableName]); - } - + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $columnList = Db::query($sql, [$dbname, $modelTableName]); $fieldArr = []; - foreach ($columnList as $k => $v) - { + foreach ($columnList as $k => $v) { $fieldArr[] = $v['COLUMN_NAME']; } - $relationFieldArr = []; - foreach ($relationColumnList as $k => $v) - { - $relationFieldArr[] = $v['COLUMN_NAME']; + // 加载关联表的列 + foreach ($relations as $index => &$relation) { + $relationColumnList = Db::query($sql, [$dbname, $relation['relationTableName']]); + + $relationFieldList = []; + foreach ($relationColumnList as $k => $v) { + $relationFieldList[] = $v['COLUMN_NAME']; + } + if (!$relation['relationPrimaryKey']) { + foreach ($relationColumnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { + $relation['relationPrimaryKey'] = $v['COLUMN_NAME']; + break; + } + } + } + // 如果主键为空 + if (!$relation['relationPrimaryKey']) { + throw new Exception('Relation Primary key not found!'); + } + // 如果主键不在表字段中 + if (!in_array($relation['relationPrimaryKey'], $relationFieldList)) { + throw new Exception('Relation Primary key not found in table!'); + } + $relation['relationColumnList'] = $relationColumnList; + $relation['relationFieldList'] = $relationFieldList; } + unset($relation); $addList = []; $editList = []; @@ -364,70 +403,47 @@ class Crud extends Command $order = 'id'; $priDefined = FALSE; $priKey = ''; - $relationPriKey = ''; - foreach ($columnList as $k => $v) - { - if ($v['COLUMN_KEY'] == 'PRI') - { + $relationPrimaryKey = ''; + foreach ($columnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { $priKey = $v['COLUMN_NAME']; break; } } - if (!$priKey) - { + if (!$priKey) { throw new Exception('Primary key not found!'); } - if ($relation) - { - foreach ($relationColumnList as $k => $v) - { - if ($v['COLUMN_KEY'] == 'PRI') - { - $relationPriKey = $v['COLUMN_NAME']; - break; - } - } - if (!$relationPriKey) - { - throw new Exception('Relation Primary key not found!'); - } - } + $order = $priKey; - //如果是关联模型 - if ($relation) - { - if ($mode == 'hasone') - { - $relationForeignKey = $relationForeignKey ? $relationForeignKey : $table . "_id"; - $relationPrimaryKey = $relationPrimaryKey ? $relationPrimaryKey : $priKey; - if (!in_array($relationForeignKey, $relationFieldArr)) - { - throw new Exception('relation table must be contain field:' . $relationForeignKey); - } - if (!in_array($relationPrimaryKey, $fieldArr)) - { - throw new Exception('table must be contain field:' . $relationPrimaryKey); - } - } - else - { - $relationForeignKey = $relationForeignKey ? $relationForeignKey : $relation . "_id"; - $relationPrimaryKey = $relationPrimaryKey ? $relationPrimaryKey : $relationPriKey; - if (!in_array($relationForeignKey, $fieldArr)) - { - throw new Exception('table must be contain field:' . $relationForeignKey); - } - if (!in_array($relationPrimaryKey, $relationFieldArr)) - { - throw new Exception('relation table must be contain field:' . $relationPrimaryKey); - } - } - } + foreach ($relations as $index => &$relation) { + if ($relation['relationMode'] == 'hasone') { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; - try - { + if (!in_array($relationForeignKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']'); + } + } else { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : Loader::parseName($relation['relationName']) . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $relation['relationPriKey']; + if (!in_array($relationForeignKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationPrimaryKey . ']'); + } + } + $relation['relationForeignKey'] = $relationForeignKey; + $relation['relationPrimaryKey'] = $relationPrimaryKey; + } + unset($relation); + + try { Form::setEscapeHtml(false); $setAttrArr = []; $getAttrArr = []; @@ -436,26 +452,26 @@ class Crud extends Command $controllerAssignList = []; //循环所有字段,开始构造视图的HTML和JS信息 - foreach ($columnList as $k => $v) - { + foreach ($columnList as $k => $v) { $field = $v['COLUMN_NAME']; $itemArr = []; // 这里构建Enum和Set类型的列表数据 - if (in_array($v['DATA_TYPE'], ['enum', 'set'])) - { + if (in_array($v['DATA_TYPE'], ['enum', 'set', 'tinyint'])) { $itemArr = substr($v['COLUMN_TYPE'], strlen($v['DATA_TYPE']) + 1, -1); $itemArr = explode(',', str_replace("'", '', $itemArr)); $itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']); + //如果类型为tinyint且有使用备注数据 + if ($itemArr && $v['DATA_TYPE'] == 'tinyint') { + $v['DATA_TYPE'] = 'enum'; + } } // 语言列表 - if ($v['COLUMN_COMMENT'] != '') - { + if ($v['COLUMN_COMMENT'] != '') { $langList[] = $this->getLangItem($field, $v['COLUMN_COMMENT']); } $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); // 如果是number类型时增加一个步长 @@ -467,17 +483,14 @@ class Crud extends Command $defaultValue = $v['COLUMN_DEFAULT']; $editValue = "{\$row.{$field}}"; // 如果默认值非null,则是一个必选项 - if ($v['IS_NULLABLE'] == 'NO') - { + if ($v['IS_NULLABLE'] == 'NO') { $attrArr['data-rule'] = 'required'; } - if ($inputType == 'select') - { + if ($inputType == 'select') { $cssClassArr[] = 'selectpicker'; $attrArr['class'] = implode(' ', $cssClassArr); - if ($v['DATA_TYPE'] == 'set') - { + if ($v['DATA_TYPE'] == 'set') { $attrArr['multiple'] = ''; $fieldName .= "[]"; } @@ -488,23 +501,19 @@ class Crud extends Command $itemArr = $this->getLangArray($itemArr, FALSE); //添加一个获取器 $this->getAttr($getAttrArr, $field, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); - if ($v['DATA_TYPE'] == 'set') - { + if ($v['DATA_TYPE'] == 'set') { $this->setAttr($setAttrArr, $field, $inputType); } $this->appendAttr($appendAttrList, $field); $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}"]); - } - else if ($inputType == 'datetime') - { + } else if ($inputType == 'datetime') { $cssClassArr[] = 'datetimepicker'; $attrArr['class'] = implode(' ', $cssClassArr); $format = "YYYY-MM-DD HH:mm:ss"; $phpFormat = "Y-m-d H:i:s"; $fieldFunc = ''; - switch ($v['DATA_TYPE']) - { + switch ($v['DATA_TYPE']) { case 'year'; $format = "YYYY"; $phpFormat = 'Y'; @@ -536,9 +545,7 @@ class Crud extends Command $fieldFunc = $fieldFunc ? "|{$fieldFunc}" : ""; $formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr); $formEditElement = Form::text($fieldName, "{\$row.{$field}{$fieldFunc}}", $attrArr); - } - else if ($inputType == 'checkbox' || $inputType == 'radio') - { + } else if ($inputType == 'checkbox' || $inputType == 'radio') { unset($attrArr['data-rule']); $fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName; $attrArr['name'] = "row[{$fieldName}]"; @@ -547,8 +554,7 @@ class Crud extends Command $itemArr = $this->getLangArray($itemArr, FALSE); //添加一个获取器 $this->getAttr($getAttrArr, $field, $inputType); - if ($inputType == 'checkbox') - { + if ($inputType == 'checkbox') { $this->setAttr($setAttrArr, $field, $inputType); } $this->appendAttr($appendAttrList, $field); @@ -556,47 +562,35 @@ class Crud extends Command $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}"]); - } - else if ($inputType == 'textarea') - { + } else if ($inputType == 'textarea') { $cssClassArr[] = substr($field, -7) == 'content' ? $this->editorClass : ''; $attrArr['class'] = implode(' ', $cssClassArr); $attrArr['rows'] = 5; $formAddElement = Form::textarea($fieldName, $defaultValue, $attrArr); $formEditElement = Form::textarea($fieldName, $editValue, $attrArr); - } - else if ($inputType == 'switch') - { + } else if ($inputType == 'switch') { unset($attrArr['data-rule']); - if ($defaultValue === '1' || $defaultValue === 'Y') - { + if ($defaultValue === '1' || $defaultValue === 'Y') { $yes = $defaultValue; $no = $defaultValue === '1' ? '0' : 'N'; - } - else - { + } else { $no = $defaultValue; $yes = $defaultValue === '0' ? '1' : 'Y'; } $formAddElement = $formEditElement = Form::hidden($fieldName, $no, array_merge(['checked' => ''], $attrArr)); $attrArr['id'] = $fieldName . "-switch"; - $formAddElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class'=>'control-label']), Form::checkbox($fieldName, $yes, $defaultValue === $yes, $attrArr)); - $formEditElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class'=>'control-label']), Form::checkbox($fieldName, $yes, 0, $attrArr)); + $formAddElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class' => 'control-label']), Form::checkbox($fieldName, $yes, $defaultValue === $yes, $attrArr)); + $formEditElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class' => 'control-label']), Form::checkbox($fieldName, $yes, 0, $attrArr)); $formEditElement = str_replace('type="checkbox"', 'type="checkbox" {in name="' . "\$row.{$field}" . '" value="' . $yes . '"}checked{/in}', $formEditElement); - } - else if ($inputType == 'citypicker') - { + } else if ($inputType == 'citypicker') { $attrArr['class'] = implode(' ', $cssClassArr); $attrArr['data-toggle'] = "city-picker"; $formAddElement = sprintf("
%s
", Form::input('text', $fieldName, $defaultValue, $attrArr)); $formEditElement = sprintf("
%s
", Form::input('text', $fieldName, $editValue, $attrArr)); - } - else - { + } else { $search = $replace = ''; //特殊字段为关联搜索 - if ($this->isMatchSuffix($field, $this->selectpageSuffix)) - { + if ($this->isMatchSuffix($field, $this->selectpageSuffix)) { $inputType = 'text'; $defaultValue = ''; $attrArr['data-rule'] = 'required'; @@ -604,21 +598,17 @@ class Crud extends Command $selectpageController = str_replace('_', '/', substr($field, 0, strripos($field, '_'))); $attrArr['data-source'] = $selectpageController . "/index"; //如果是类型表需要特殊处理下 - if ($selectpageController == 'category') - { + if ($selectpageController == 'category') { $attrArr['data-source'] = 'category/selectpage'; $attrArr['data-params'] = '##replacetext##'; $search = '"##replacetext##"'; $replace = '\'{"custom[type]":"' . $table . '"}\''; } - if ($this->isMatchSuffix($field, $this->selectpagesSuffix)) - { + if ($this->isMatchSuffix($field, $this->selectpagesSuffix)) { $attrArr['data-multiple'] = 'true'; } - foreach ($this->fieldSelectpageMap as $m => $n) - { - if (in_array($field, $n)) - { + foreach ($this->fieldSelectpageMap as $m => $n) { + if (in_array($field, $n)) { $attrArr['data-field'] = $m; break; } @@ -628,31 +618,26 @@ class Crud extends Command $step = array_intersect($cssClassArr, ['selectpage']) ? 0 : $step; $attrArr['class'] = implode(' ', $cssClassArr); $isUpload = false; - if ($this->isMatchSuffix($field, array_merge($this->imageField, $this->fileField))) - { + if ($this->isMatchSuffix($field, array_merge($this->imageField, $this->fileField))) { $isUpload = true; } //如果是步长则加上步长 - if ($step) - { + if ($step) { $attrArr['step'] = $step; } //如果是图片加上个size - if ($isUpload) - { + if ($isUpload) { $attrArr['size'] = 50; } $formAddElement = Form::input($inputType, $fieldName, $defaultValue, $attrArr); $formEditElement = Form::input($inputType, $fieldName, $editValue, $attrArr); - if ($search && $replace) - { + if ($search && $replace) { $formAddElement = str_replace($search, $replace, $formAddElement); $formEditElement = str_replace($search, $replace, $formEditElement); } //如果是图片或文件 - if ($isUpload) - { + if ($isUpload) { $formAddElement = $this->getImageUpload($field, $formAddElement); $formEditElement = $this->getImageUpload($field, $formEditElement); } @@ -663,11 +648,9 @@ class Crud extends Command } //过滤text类型字段 - if ($v['DATA_TYPE'] != 'text') - { + if ($v['DATA_TYPE'] != 'text') { //主键 - if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) - { + if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) { $priDefined = TRUE; $javascriptList[] = "{checkbox: true}"; } @@ -679,25 +662,25 @@ class Crud extends Command } } - $relationPriKey = 'id'; - $relationFieldArr = []; - foreach ($relationColumnList as $k => $v) - { - $relationField = $v['COLUMN_NAME']; - $relationFieldArr[] = $field; + //循环关联表,追加语言包和JS列 + foreach ($relations as $index => $relation) { + foreach ($relation['relationColumnList'] as $k => $v) { + // 不显示的字段直接过滤掉 + if ($relation['relationFields'] && !in_array($v['COLUMN_NAME'], $relation['relationFields'])) { + continue; + } - $relationField = strtolower($relationModelName) . "." . $relationField; - // 语言列表 - if ($v['COLUMN_COMMENT'] != '') - { - $langList[] = $this->getLangItem($relationField, $v['COLUMN_COMMENT']); - } + $relationField = strtolower($relation['relationName']) . "." . $v['COLUMN_NAME']; + // 语言列表 + if ($v['COLUMN_COMMENT'] != '') { + $langList[] = $this->getLangItem($relationField, $v['COLUMN_COMMENT']); + } - //过滤text类型字段 - if ($v['DATA_TYPE'] != 'text') - { - //构造JS列信息 - $javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE']); + //过滤text类型字段 + if ($v['DATA_TYPE'] != 'text') { + //构造JS列信息 + $javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE']); + } } } @@ -709,7 +692,7 @@ class Crud extends Command $langList = implode(",\n", array_filter($langList)); //表注释 - $tableComment = $tableInfo['Comment']; + $tableComment = $modelTableInfo['Comment']; $tableComment = mb_substr($tableComment, -1) == '表' ? mb_substr($tableComment, 0, -1) . '管理' : $tableComment; $appNamespace = Config::get('app_namespace'); @@ -720,8 +703,7 @@ class Crud extends Command $validateName = $modelName; $modelInit = ''; - if ($priKey != $order) - { + if ($priKey != $order) { $modelInit = $this->getReplacedStub('mixins' . DS . 'modelinit', ['order' => $order]); } @@ -734,13 +716,16 @@ class Crud extends Command 'controllerName' => $controllerName, 'controllerAssignList' => implode("\n", $controllerAssignList), 'modelName' => $modelName, + 'modelTableName' => $modelTableName, + 'modelTableType' => $modelTableType, + 'modelTableTypeName' => $modelTableTypeName, 'validateName' => $validateName, 'tableComment' => $tableComment, 'iconName' => $iconName, 'pk' => $priKey, 'order' => $order, 'table' => $table, - 'tableName' => $tableName, + 'tableName' => $modelTableName, 'addList' => $addList, 'editList' => $editList, 'javascriptList' => $javascriptList, @@ -748,52 +733,73 @@ class Crud extends Command 'modelAutoWriteTimestamp' => in_array('createtime', $fieldArr) || in_array('updatetime', $fieldArr) ? "'int'" : 'false', 'createTime' => in_array('createtime', $fieldArr) ? "'createtime'" : 'false', 'updateTime' => in_array('updatetime', $fieldArr) ? "'updatetime'" : 'false', - 'modelTableName' => $modelTableName, - 'modelTableType' => $modelTableType, - 'relationModelTableName' => $relationModelTableName, - 'relationModelTableType' => $relationModelTableType, - 'relationModelName' => $relationModelName, - 'relationWith' => '', - 'relationMethod' => '', - 'relationModel' => '', - 'relationForeignKey' => '', - 'relationPrimaryKey' => '', - 'relationSearch' => $relation ? 'true' : 'false', + 'relationSearch' => $relations ? 'true' : 'false', + 'relationWithList' => '', + 'relationMethodList' => '', 'controllerIndex' => '', + 'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '', 'appendAttrList' => implode(",\n", $appendAttrList), 'getEnumList' => implode("\n\n", $getEnumArr), 'getAttrList' => implode("\n\n", $getAttrArr), 'setAttrList' => implode("\n\n", $setAttrArr), 'modelInit' => $modelInit, - 'modelRelationMethod' => '', ]; //如果使用关联模型 - if ($relation) - { - //需要构造关联的方法 - $data['relationMethod'] = strtolower($relationModelName); - //预载入的方法 - $data['relationWith'] = "->with('{$data['relationMethod']}')"; + if ($relations) { + $relationWithList = $relationMethodList = $relationVisibleFieldList = []; + foreach ($relations as $index => $relation) { + //需要构造关联的方法 + $relation['relationMethod'] = strtolower($relation['relationName']); + + //关联的模式 + $relation['relationMode'] = $relation['relationMode'] == 'hasone' ? 'hasOne' : 'belongsTo'; + + //关联字段 + $relation['relationForeignKey'] = $relation['relationForeignKey']; + $relation['relationPrimaryKey'] = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; + + //预载入的方法 + $relationWithList[] = $relation['relationMethod']; + + unset($relation['relationColumnList'], $relation['relationFieldList'], $relation['relationTableInfo']); + + //构造关联模型的方法 + $relationMethodList[] = $this->getReplacedStub('mixins' . DS . 'modelrelationmethod', $relation); + + //显示的字段 + if ($relation['relationFields']) { + $relationVisibleFieldList[] = "\$row->getRelation('" . $relation['relationMethod'] . "')->visible(['" . implode("','", $relation['relationFields']) . "']);"; + } + } + + $data['relationWithList'] = "->with(['" . implode("','", $relationWithList) . "'])"; + $data['relationMethodList'] = implode("\n\n", $relationMethodList); + $data['relationVisibleFieldList'] = implode("\n\t\t\t\t", $relationVisibleFieldList); + + //需要重写index方法 + $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); + + } else if ($fields) { + $data = array_merge($data, ['relationWithList' => '', 'relationMethodList' => '', 'relationVisibleFieldList' => '']); //需要重写index方法 $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); - //关联的模式 - $data['relationMode'] = $mode == 'hasone' ? 'hasOne' : 'belongsTo'; - //关联字段 - $data['relationForeignKey'] = $relationForeignKey; - $data['relationPrimaryKey'] = $relationPrimaryKey ? $relationPrimaryKey : $priKey; - //构造关联模型的方法 - $data['modelRelationMethod'] = $this->getReplacedStub('mixins' . DS . 'modelrelationmethod', $data); } // 生成控制器文件 $result = $this->writeToFile('controller', $data, $controllerFile); // 生成模型文件 $result = $this->writeToFile('model', $data, $modelFile); - if ($relation && !is_file($relationModelFile)) - { - // 生成关联模型文件 - $result = $this->writeToFile('relationmodel', $data, $relationModelFile); + + if ($relations) { + foreach ($relations as $i => $relation) { + $relation['modelNamespace'] = $data['modelNamespace']; + if (!is_file($relation['relationFile'])) { + // 生成关联模型文件 + $result = $this->writeToFile('relationmodel', $relation, $relation['relationFile']); + } + } + } // 生成验证文件 $result = $this->writeToFile('validate', $data, $validateFile); @@ -804,19 +810,15 @@ class Crud extends Command // 生成JS文件 $result = $this->writeToFile('javascript', $data, $javascriptFile); // 生成语言文件 - if ($langList) - { + if ($langList) { $result = $this->writeToFile('lang', $data, $langFile); } - } - catch (\think\exception\ErrorException $e) - { + } catch (\think\exception\ErrorException $e) { throw new Exception("Code: " . $e->getCode() . "\nLine: " . $e->getLine() . "\nMessage: " . $e->getMessage() . "\nFile: " . $e->getFile()); } //继续生成菜单 - if ($menu) - { + if ($menu) { exec("php think menu -c {$controllerUrl}"); } @@ -829,8 +831,7 @@ class Crud extends Command return; $fieldList = $this->getFieldListName($field); $methodName = 'get' . ucfirst($fieldList); - foreach ($itemArr as $k => &$v) - { + foreach ($itemArr as $k => &$v) { $v = "__('" . mb_ucfirst($v) . "')"; } unset($v); @@ -859,14 +860,11 @@ EOD; if (!in_array($inputType, ['datetime', 'checkbox', 'select'])) return; $attrField = ucfirst($this->getCamelizeName($field)); - if ($inputType == 'datetime') - { + if ($inputType == 'datetime') { $return = << &$v) $v = ucfirst($v); unset($v); $modelName = implode('', $modelarr); - } - else - { + } else { $modelName = ucfirst($model); } return $modelName; @@ -912,10 +907,13 @@ EOD; */ protected function writeToFile($name, $data, $pathname) { + foreach ($data as $index => &$datum) { + $datum = is_array($datum) ? '' : $datum; + } + unset($datum); $content = $this->getReplacedStub($name, $data); - if (!is_dir(dirname($pathname))) - { + if (!is_dir(dirname($pathname))) { mkdir(strtolower(dirname($pathname)), 0755, true); } return file_put_contents($pathname, $content); @@ -929,19 +927,19 @@ EOD; */ protected function getReplacedStub($name, $data) { + foreach ($data as $index => &$datum) { + $datum = is_array($datum) ? '' : $datum; + } + unset($datum); $search = $replace = []; - foreach ($data as $k => $v) - { + foreach ($data as $k => $v) { $search[] = "{%{$k}%}"; $replace[] = $v; } $stubname = $this->getStub($name); - if (isset($this->stubList[$stubname])) - { + if (isset($this->stubList[$stubname])) { $stub = $this->stubList[$stubname]; - } - else - { + } else { $this->stubList[$stubname] = $stub = file_get_contents($stubname); } $content = str_replace($search, $replace, $stub); @@ -960,37 +958,28 @@ EOD; protected function getLangItem($field, $content) { - if ($content || !Lang::has($field)) - { + if ($content || !Lang::has($field)) { $itemArr = []; $content = str_replace(',', ',', $content); - if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false) - { + if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false) { list($fieldLang, $item) = explode(':', $content); $itemArr = [$field => $fieldLang]; - foreach (explode(',', $item) as $k => $v) - { + foreach (explode(',', $item) as $k => $v) { $valArr = explode('=', $v); - if (count($valArr) == 2) - { + if (count($valArr) == 2) { list($key, $value) = $valArr; $itemArr[$field . ' ' . $key] = $value; } } - } - else - { + } else { $itemArr = [$field => $content]; } $resultArr = []; - foreach ($itemArr as $k => $v) - { + foreach ($itemArr as $k => $v) { $resultArr[] = " '" . mb_ucfirst($k) . "' => '{$v}'"; } return implode(",\n", $resultArr); - } - else - { + } else { return ''; } } @@ -1004,8 +993,7 @@ EOD; protected function getLangArray($arr, $withTpl = TRUE) { $langArr = []; - foreach ($arr as $k => $v) - { + foreach ($arr as $k => $v) { $langArr[$k] = is_numeric($k) ? ($withTpl ? "{:" : "") . "__('" . mb_ucfirst($v) . "')" . ($withTpl ? "}" : "") : $v; } return $langArr; @@ -1021,11 +1009,9 @@ EOD; if (!is_array($arr)) return $arr; $stringArr = []; - foreach ($arr as $k => $v) - { + foreach ($arr as $k => $v) { $is_var = in_array(substr($v, 0, 1), ['$', '_']); - if (!$is_var) - { + if (!$is_var) { $v = str_replace("'", "\'", $v); $k = str_replace("'", "\'", $k); } @@ -1038,24 +1024,18 @@ EOD; { $itemArr = []; $comment = str_replace(',', ',', $comment); - if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) - { + if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) { list($fieldLang, $item) = explode(':', $comment); $itemArr = []; - foreach (explode(',', $item) as $k => $v) - { + foreach (explode(',', $item) as $k => $v) { $valArr = explode('=', $v); - if (count($valArr) == 2) - { + if (count($valArr) == 2) { list($key, $value) = $valArr; $itemArr[$key] = $field . ' ' . $key; } } - } - else - { - foreach ($item as $k => $v) - { + } else { + foreach ($item as $k => $v) { $itemArr[$v] = is_numeric($v) ? $field . ' ' . $v : $v; } } @@ -1065,8 +1045,7 @@ EOD; protected function getFieldType(& $v) { $inputType = 'text'; - switch ($v['DATA_TYPE']) - { + switch ($v['DATA_TYPE']) { case 'bigint': case 'int': case 'mediumint': @@ -1102,28 +1081,23 @@ EOD; } $fieldsName = $v['COLUMN_NAME']; // 指定后缀说明也是个时间字段 - if ($this->isMatchSuffix($fieldsName, $this->intDateSuffix)) - { + if ($this->isMatchSuffix($fieldsName, $this->intDateSuffix)) { $inputType = 'datetime'; } // 指定后缀结尾且类型为enum,说明是个单选框 - if ($this->isMatchSuffix($fieldsName, $this->enumRadioSuffix) && $v['DATA_TYPE'] == 'enum') - { + if ($this->isMatchSuffix($fieldsName, $this->enumRadioSuffix) && $v['DATA_TYPE'] == 'enum') { $inputType = "radio"; } // 指定后缀结尾且类型为set,说明是个复选框 - if ($this->isMatchSuffix($fieldsName, $this->setCheckboxSuffix) && $v['DATA_TYPE'] == 'set') - { + if ($this->isMatchSuffix($fieldsName, $this->setCheckboxSuffix) && $v['DATA_TYPE'] == 'set') { $inputType = "checkbox"; } // 指定后缀结尾且类型为char或tinyint且长度为1,说明是个Switch复选框 - if ($this->isMatchSuffix($fieldsName, $this->switchSuffix) && ($v['COLUMN_TYPE'] == 'tinyint(1)' || $v['COLUMN_TYPE'] == 'char(1)') && $v['COLUMN_DEFAULT'] !== '' && $v['COLUMN_DEFAULT'] !== null) - { + if ($this->isMatchSuffix($fieldsName, $this->switchSuffix) && ($v['COLUMN_TYPE'] == 'tinyint(1)' || $v['COLUMN_TYPE'] == 'char(1)') && $v['COLUMN_DEFAULT'] !== '' && $v['COLUMN_DEFAULT'] !== null) { $inputType = "switch"; } // 指定后缀结尾城市选择框 - if ($this->isMatchSuffix($fieldsName, $this->citySuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'char')) - { + if ($this->isMatchSuffix($fieldsName, $this->citySuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'char')) { $inputType = "citypicker"; } return $inputType; @@ -1138,10 +1112,8 @@ EOD; protected function isMatchSuffix($field, $suffixArr) { $suffixArr = is_array($suffixArr) ? $suffixArr : explode(',', $suffixArr); - foreach ($suffixArr as $k => $v) - { - if (preg_match("/{$v}$/i", $field)) - { + foreach ($suffixArr as $k => $v) { + if (preg_match("/{$v}$/i", $field)) { return true; } } @@ -1157,7 +1129,7 @@ EOD; protected function getFormGroup($field, $content) { $langField = mb_ucfirst($field); - return<<
@@ -1176,8 +1148,7 @@ EOD; protected function getImageUpload($field, $content) { $uploadfilter = $selectfilter = ''; - if ($this->isMatchSuffix($field, $this->imageField)) - { + if ($this->isMatchSuffix($field, $this->imageField)) { $uploadfilter = ' data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp"'; $selectfilter = ' data-mimetype="image/*"'; } @@ -1209,68 +1180,52 @@ EOD; { $lang = mb_ucfirst($field); $formatter = ''; - foreach ($this->fieldFormatterSuffix as $k => $v) - { - if (preg_match("/{$k}$/i", $field)) - { - if (is_array($v)) - { - if (in_array($datatype, $v['type'])) - { + foreach ($this->fieldFormatterSuffix as $k => $v) { + if (preg_match("/{$k}$/i", $field)) { + if (is_array($v)) { + if (in_array($datatype, $v['type'])) { $formatter = $v['name']; break; } - } - else - { + } else { $formatter = $v; break; } } } - if ($formatter) - { + if ($formatter) { $extend = ''; } $html = str_repeat(" ", 24) . "{field: '{$field}{$extend}', title: __('{$lang}')"; //$formatter = $extend ? '' : $formatter; - if ($extend) - { + if ($extend) { $html .= ", operate:false"; - if ($datatype == 'set') - { + if ($datatype == 'set') { $formatter = 'label'; } } - foreach ($itemArr as $k => &$v) - { + foreach ($itemArr as $k => &$v) { if (substr($v, 0, 3) !== '__(') $v = "__('" . $v . "')"; } unset($v); $searchList = json_encode($itemArr, JSON_FORCE_OBJECT); $searchList = str_replace(['":"', '"}', ')","'], ['":', '}', '),"'], $searchList); - if ($itemArr && !$extend) - { + if ($itemArr && !$extend) { $html .= ", searchList: " . $searchList; } - if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') - { + if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') { $html .= ", operate:'RANGE', addclass:'datetimerange'"; - } - else if (in_array($datatype, ['float', 'double', 'decimal'])) - { + } else if (in_array($datatype, ['float', 'double', 'decimal'])) { $html .= ", operate:'BETWEEN'"; } if ($formatter) $html .= ", formatter: Table.api.formatter." . $formatter . "}"; else $html .= "}"; - if ($extend) - { + if ($extend) { $origin = str_repeat(" ", 24) . "{field: '{$field}', title: __('{$lang}'), visible:false"; - if ($searchList) - { + if ($searchList) { $origin .= ", searchList: " . $searchList; } $origin .= "}"; diff --git a/application/admin/command/Crud/stubs/controllerindex.stub b/application/admin/command/Crud/stubs/controllerindex.stub old mode 100644 new mode 100755 index 35c94e3e..5294600c --- a/application/admin/command/Crud/stubs/controllerindex.stub +++ b/application/admin/command/Crud/stubs/controllerindex.stub @@ -7,22 +7,28 @@ //当前是否为关联查询 $this->relationSearch = {%relationSearch%}; //设置过滤方法 - $this->request->filter(['strip_tags', 'htmlspecialchars']); + $this->request->filter(['strip_tags']); if ($this->request->isAjax()) { list($where, $sort, $order, $offset, $limit) = $this->buildparams(); $total = $this->model - {%relationWith%} + {%relationWithList%} ->where($where) ->order($sort, $order) ->count(); $list = $this->model - {%relationWith%} + {%relationWithList%} ->where($where) ->order($sort, $order) ->limit($offset, $limit) ->select(); + + foreach ($list as $row) { + {%visibleFieldList%} + {%relationVisibleFieldList%} + } + $list = collection($list)->toArray(); $result = array("total" => $total, "rows" => $list); return json($result); diff --git a/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub b/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub old mode 100644 new mode 100755 index fa6c4276..712daebf --- a/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub +++ b/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub @@ -1,5 +1,5 @@ public function {%relationMethod%}() { - return $this->{%relationMode%}('{%relationModelName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}')->setEagerlyType(0); + return $this->{%relationMode%}('{%relationName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0); } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/model.stub b/application/admin/command/Crud/stubs/model.stub old mode 100644 new mode 100755 index ae3da174..c08152b0 --- a/application/admin/command/Crud/stubs/model.stub +++ b/application/admin/command/Crud/stubs/model.stub @@ -7,7 +7,7 @@ use think\Model; class {%modelName%} extends Model { // 表名 - protected ${%modelTableType%} = '{%modelTableName%}'; + protected ${%modelTableType%} = '{%modelTableTypeName%}'; // 自动写入时间戳字段 protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%}; @@ -29,5 +29,5 @@ class {%modelName%} extends Model {%setAttrList%} -{%modelRelationMethod%} +{%relationMethodList%} } diff --git a/application/admin/command/Crud/stubs/relationmodel.stub b/application/admin/command/Crud/stubs/relationmodel.stub old mode 100644 new mode 100755 index 99b56f6c..dd22a533 --- a/application/admin/command/Crud/stubs/relationmodel.stub +++ b/application/admin/command/Crud/stubs/relationmodel.stub @@ -4,9 +4,9 @@ namespace {%modelNamespace%}; use think\Model; -class {%relationModelName%} extends Model +class {%relationName%} extends Model { // 表名 - protected ${%relationModelTableType%} = '{%relationModelTableName%}'; + protected ${%relationTableType%} = '{%relationTableTypeName%}'; } diff --git a/application/admin/command/Install/fastadmin.sql b/application/admin/command/Install/fastadmin.sql index 8ec0f2f5..50610976 100755 --- a/application/admin/command/Install/fastadmin.sql +++ b/application/admin/command/Install/fastadmin.sql @@ -50,7 +50,7 @@ CREATE TABLE `fa_admin_log` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID', `username` varchar(30) NOT NULL DEFAULT '' COMMENT '管理员名字', - `url` varchar(100) NOT NULL DEFAULT '' COMMENT '操作页面', + `url` varchar(255) NOT NULL DEFAULT '' COMMENT '操作页面', `title` varchar(100) NOT NULL DEFAULT '' COMMENT '日志标题', `content` text NOT NULL COMMENT '内容', `ip` varchar(50) NOT NULL DEFAULT '' COMMENT 'IP', @@ -58,7 +58,7 @@ CREATE TABLE `fa_admin_log` ( `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '操作时间', PRIMARY KEY (`id`), KEY `name` (`username`) -) ENGINE=InnoDB AUTO_INCREMENT=1317 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='管理员日志表'; +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='管理员日志表'; -- ---------------------------- -- Table structure for fa_attachment diff --git a/application/admin/command/Menu.php b/application/admin/command/Menu.php index 3b501565..b25791dc 100755 --- a/application/admin/command/Menu.php +++ b/application/admin/command/Menu.php @@ -21,10 +21,11 @@ class Menu extends Command protected function configure() { $this - ->setName('menu') - ->addOption('controller', 'c', Option::VALUE_REQUIRED, 'controller name,use \'all-controller\' when build all menu', null) - ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '') - ->setDescription('Build auth menu from controller'); + ->setName('menu') + ->addOption('controller', 'c', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name,use \'all-controller\' when build all menu', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force delete menu,without tips', null) + ->setDescription('Build auth menu from controller'); } protected function execute(Input $input, Output $output) @@ -33,34 +34,35 @@ class Menu extends Command $adminPath = dirname(__DIR__) . DS; //控制器名 $controller = $input->getOption('controller') ?: ''; - if (!$controller) - { + if (!$controller) { throw new Exception("please input controller name"); } + $force = $input->getOption('force'); //是否为删除模式 $delete = $input->getOption('delete'); - if ($delete) - { - if ($controller == 'all-controller') - { + if ($delete) { + if (in_array('all-controller', $controller)) { throw new Exception("could not delete all menu"); } $ids = []; - $list = $this->model->where('name', 'like', strtolower($controller) . "%")->select(); - foreach ($list as $k => $v) - { + $list = $this->model->where(function ($query) use ($controller) { + foreach ($controller as $index => $item) { + $query->whereOr('name', 'like', strtolower($item) . "%"); + } + })->select(); + foreach ($list as $k => $v) { $output->warning($v->name); $ids[] = $v->id; } - if (!$ids) - { + if (!$ids) { throw new Exception("There is no menu to delete"); } - $output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: "); - $line = fgets(STDIN); - if (trim($line) != 'yes') - { - throw new Exception("Operation is aborted!"); + if (!$force) { + $output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: "); + $line = fgets(STDIN); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } } AuthRule::destroy($ids); @@ -69,22 +71,21 @@ class Menu extends Command return; } - if ($controller != 'all-controller') - { - $controllerArr = explode('/', $controller); - end($controllerArr); - $key = key($controllerArr); - $controllerArr[$key] = ucfirst($controllerArr[$key]); - $adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php'; - if (!is_file($adminPath)) - { - $output->error("controller not found"); - return; + if (!in_array('all-controller', $controller)) { + foreach ($controller as $index => $item) { + $controllerArr = explode('/', $item); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = ucfirst($controllerArr[$key]); + $adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php'; + if (!is_file($adminPath)) { + $output->error("controller not found"); + return; + } + $this->importRule($item); } - $this->importRule($controller); - } - else - { + + } else { $this->model->where('id', '>', 0)->delete(); $controllerDir = $adminPath . 'controller' . DS; // 扫描新的节点信息并导入 @@ -103,16 +104,11 @@ class Menu extends Command { $result = []; $cdir = scandir($dir); - foreach ($cdir as $value) - { - if (!in_array($value, array(".", ".."))) - { - if (is_dir($dir . DS . $value)) - { + foreach ($cdir as $value) { + if (!in_array($value, array(".", ".."))) { + if (is_dir($dir . DS . $value)) { $result[$value] = $this->scandir($dir . DS . $value); - } - else - { + } else { $result[] = $value; } } @@ -129,19 +125,14 @@ class Menu extends Command public function import($dirarr, $parentdir = []) { $menuarr = []; - foreach ($dirarr as $k => $v) - { - if (is_array($v)) - { + foreach ($dirarr as $k => $v) { + if (is_array($v)) { //当前是文件夹 $nowparentdir = array_merge($parentdir, [$k]); $this->import($v, $nowparentdir); - } - else - { + } else { //只匹配PHP文件 - if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) - { + if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) { continue; } //导入文件 @@ -177,8 +168,7 @@ class Menu extends Command //反射机制调用类的注释和方法名 $reflector = new ReflectionClass($className); - if (isset($tempClassFile)) - { + if (isset($tempClassFile)) { //删除临时文件 @unlink($tempClassFile); } @@ -190,34 +180,27 @@ class Menu extends Command $softDeleteMethods = ['destroy', 'restore', 'recyclebin']; $withSofeDelete = false; preg_match_all("/\\\$this\->model\s*=\s*model\('(\w+)'\);/", $classContent, $matches); - if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) - { + if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) { \think\Request::instance()->module('admin'); $model = model($matches[1][0]); - if (in_array('trashed', get_class_methods($model))) - { + if (in_array('trashed', get_class_methods($model))) { $withSofeDelete = true; } } //忽略的类 - if (stripos($classComment, "@internal") !== FALSE) - { + if (stripos($classComment, "@internal") !== FALSE) { return; } preg_match_all('#(@.*?)\n#s', $classComment, $annotations); $controllerIcon = 'fa fa-circle-o'; $controllerRemark = ''; //判断注释中是否设置了icon值 - if (isset($annotations[1])) - { - foreach ($annotations[1] as $tag) - { - if (stripos($tag, '@icon') !== FALSE) - { + if (isset($annotations[1])) { + foreach ($annotations[1] as $tag) { + if (stripos($tag, '@icon') !== FALSE) { $controllerIcon = substr($tag, stripos($tag, ' ') + 1); } - if (stripos($tag, '@remark') !== FALSE) - { + if (stripos($tag, '@remark') !== FALSE) { $controllerRemark = substr($tag, stripos($tag, ' ') + 1); } } @@ -230,8 +213,7 @@ class Menu extends Command //先导入菜单的数据 $pid = 0; - foreach ($controllerArr as $k => $v) - { + foreach ($controllerArr as $k => $v) { $key = $k + 1; $name = strtolower(implode('/', array_slice($controllerArr, 0, $key))); $title = (!isset($controllerArr[$key]) ? $controllerTitle : ''); @@ -239,42 +221,34 @@ class Menu extends Command $remark = (!isset($controllerArr[$key]) ? $controllerRemark : ''); $title = $title ? $title : $v; $rulemodel = $this->model->get(['name' => $name]); - if (!$rulemodel) - { + if (!$rulemodel) { $this->model - ->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal']) - ->isUpdate(false) - ->save(); + ->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal']) + ->isUpdate(false) + ->save(); $pid = $this->model->id; - } - else - { + } else { $pid = $rulemodel->id; } } $ruleArr = []; - foreach ($methods as $m => $n) - { + foreach ($methods as $m => $n) { //过滤特殊的类 - if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') - { + if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') { continue; } //未启用软删除时过滤相关方法 - if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) - { + if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) { continue; } //只匹配符合的方法 - if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) - { + if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) { unset($methods[$m]); continue; } $comment = $reflector->getMethod($n->name)->getDocComment(); //忽略的方法 - if (stripos($comment, "@internal") !== FALSE) - { + if (stripos($comment, "@internal") !== FALSE) { continue; } //过滤掉其它字符 @@ -293,11 +267,10 @@ class Menu extends Command //获取主键 protected function getAuthRulePK($name) { - if (!empty($name)) - { + if (!empty($name)) { $id = $this->model - ->where('name', $name) - ->value('id'); + ->where('name', $name) + ->value('id'); return $id ? $id : null; } } diff --git a/application/admin/common.php b/application/admin/common.php old mode 100644 new mode 100755 index ec2661a7..2b61dde6 --- a/application/admin/common.php +++ b/application/admin/common.php @@ -5,149 +5,159 @@ use fast\Form; use fast\Tree; use think\Db; -/** - * 生成下拉列表 - * @param string $name - * @param mixed $options - * @param mixed $selected - * @param mixed $attr - * @return string - */ -function build_select($name, $options, $selected = [], $attr = []) -{ - $options = is_array($options) ? $options : explode(',', $options); - $selected = is_array($selected) ? $selected : explode(',', $selected); - return Form::select($name, $options, $selected, $attr); +if (!function_exists('build_select')) { + + /** + * 生成下拉列表 + * @param string $name + * @param mixed $options + * @param mixed $selected + * @param mixed $attr + * @return string + */ + function build_select($name, $options, $selected = [], $attr = []) + { + $options = is_array($options) ? $options : explode(',', $options); + $selected = is_array($selected) ? $selected : explode(',', $selected); + return Form::select($name, $options, $selected, $attr); + } } -/** - * 生成单选按钮组 - * @param string $name - * @param array $list - * @param mixed $selected - * @return string - */ -function build_radios($name, $list = [], $selected = null) -{ - $html = []; - $selected = is_null($selected) ? key($list) : $selected; - $selected = is_array($selected) ? $selected : explode(',', $selected); - foreach ($list as $k => $v) - { - $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); - } - return '
' . implode(' ', $html) . '
'; -} +if (!function_exists('build_radios')) { -/** - * 生成复选按钮组 - * @param string $name - * @param array $list - * @param mixed $selected - * @return string - */ -function build_checkboxs($name, $list = [], $selected = null) -{ - $html = []; - $selected = is_null($selected) ? [] : $selected; - $selected = is_array($selected) ? $selected : explode(',', $selected); - foreach ($list as $k => $v) + /** + * 生成单选按钮组 + * @param string $name + * @param array $list + * @param mixed $selected + * @return string + */ + function build_radios($name, $list = [], $selected = null) { - $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); - } - return '
' . implode(' ', $html) . '
'; -} - -/** - * 生成分类下拉列表框 - * @param string $name - * @param string $type - * @param mixed $selected - * @param array $attr - * @return string - */ -function build_category_select($name, $type, $selected = null, $attr = [], $header = []) -{ - $tree = Tree::instance(); - $tree->init(Category::getCategoryArray($type), 'pid'); - $categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); - $categorydata = $header ? $header : []; - foreach ($categorylist as $k => $v) - { - $categorydata[$v['id']] = $v['name']; - } - $attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr); - return build_select($name, $categorydata, $selected, $attr); -} - -/** - * 生成表格操作按钮栏 - * @param array $btns 按钮组 - * @param array $attr 按钮属性值 - * @return string - */ -function build_toolbar($btns = NULL, $attr = []) -{ - $auth = \app\admin\library\Auth::instance(); - $controller = str_replace('.', '/', strtolower(think\Request::instance()->controller())); - $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import']; - $btns = is_array($btns) ? $btns : explode(',', $btns); - $index = array_search('delete', $btns); - if ($index !== FALSE) - { - $btns[$index] = 'del'; - } - $btnAttr = [ - 'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', '', __('Refresh')], - 'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add'), __('Add')], - 'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit'), __('Edit')], - 'del' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete'), __('Delete')], - 'import' => ['javascript:;', 'btn btn-danger btn-import', 'fa fa-upload', __('Import'), __('Import')], - ]; - $btnAttr = array_merge($btnAttr, $attr); - $html = []; - foreach ($btns as $k => $v) - { - //如果未定义或没有权限 - if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}"))) - { - continue; + $html = []; + $selected = is_null($selected) ? key($list) : $selected; + $selected = is_array($selected) ? $selected : explode(',', $selected); + foreach ($list as $k => $v) { + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); } - list($href, $class, $icon, $text, $title) = $btnAttr[$v]; - $extend = $v == 'import' ? 'id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"' : ''; - $html[] = ' ' . $text . ''; + return '
' . implode(' ', $html) . '
'; } - return implode(' ', $html); } -/** - * 生成页面Heading - * - * @param string $path 指定的path - * @return string - */ -function build_heading($path = NULL, $container = TRUE) -{ - $title = $content = ''; - if (is_null($path)) +if (!function_exists('build_checkboxs')) { + + /** + * 生成复选按钮组 + * @param string $name + * @param array $list + * @param mixed $selected + * @return string + */ + function build_checkboxs($name, $list = [], $selected = null) { - $action = request()->action(); - $controller = str_replace('.', '/', request()->controller()); - $path = strtolower($controller . ($action && $action != 'index' ? '/' . $action : '')); + $html = []; + $selected = is_null($selected) ? [] : $selected; + $selected = is_array($selected) ? $selected : explode(',', $selected); + foreach ($list as $k => $v) { + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + } + return '
' . implode(' ', $html) . '
'; + } +} + + +if (!function_exists('build_category_select')) { + + /** + * 生成分类下拉列表框 + * @param string $name + * @param string $type + * @param mixed $selected + * @param array $attr + * @return string + */ + function build_category_select($name, $type, $selected = null, $attr = [], $header = []) + { + $tree = Tree::instance(); + $tree->init(Category::getCategoryArray($type), 'pid'); + $categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); + $categorydata = $header ? $header : []; + foreach ($categorylist as $k => $v) { + $categorydata[$v['id']] = $v['name']; + } + $attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr); + return build_select($name, $categorydata, $selected, $attr); + } +} + +if (!function_exists('build_toolbar')) { + + /** + * 生成表格操作按钮栏 + * @param array $btns 按钮组 + * @param array $attr 按钮属性值 + * @return string + */ + function build_toolbar($btns = NULL, $attr = []) + { + $auth = \app\admin\library\Auth::instance(); + $controller = str_replace('.', '/', strtolower(think\Request::instance()->controller())); + $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import']; + $btns = is_array($btns) ? $btns : explode(',', $btns); + $index = array_search('delete', $btns); + if ($index !== FALSE) { + $btns[$index] = 'del'; + } + $btnAttr = [ + 'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', '', __('Refresh')], + 'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add'), __('Add')], + 'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit'), __('Edit')], + 'del' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete'), __('Delete')], + 'import' => ['javascript:;', 'btn btn-danger btn-import', 'fa fa-upload', __('Import'), __('Import')], + ]; + $btnAttr = array_merge($btnAttr, $attr); + $html = []; + foreach ($btns as $k => $v) { + //如果未定义或没有权限 + if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}"))) { + continue; + } + list($href, $class, $icon, $text, $title) = $btnAttr[$v]; + $extend = $v == 'import' ? 'id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"' : ''; + $html[] = ' ' . $text . ''; + } + return implode(' ', $html); + } +} + +if (!function_exists('build_heading')) { + + /** + * 生成页面Heading + * + * @param string $path 指定的path + * @return string + */ + function build_heading($path = NULL, $container = TRUE) + { + $title = $content = ''; + if (is_null($path)) { + $action = request()->action(); + $controller = str_replace('.', '/', request()->controller()); + $path = strtolower($controller . ($action && $action != 'index' ? '/' . $action : '')); + } + // 根据当前的URI自动匹配父节点的标题和备注 + $data = Db::name('auth_rule')->where('name', $path)->field('title,remark')->find(); + if ($data) { + $title = __($data['title']); + $content = __($data['remark']); + } + if (!$content) + return ''; + $result = '
' . $title . '' . $content . '
'; + if ($container) { + $result = '
' . $result . '
'; + } + return $result; } - // 根据当前的URI自动匹配父节点的标题和备注 - $data = Db::name('auth_rule')->where('name', $path)->field('title,remark')->find(); - if ($data) - { - $title = __($data['title']); - $content = __($data['remark']); - } - if (!$content) - return ''; - $result = '
' . $title . '' . $content . '
'; - if ($container) - { - $result = '
' . $result . '
'; - } - return $result; } diff --git a/application/admin/lang/zh-cn.php b/application/admin/lang/zh-cn.php old mode 100644 new mode 100755 index 791143b8..2f0c4c11 --- a/application/admin/lang/zh-cn.php +++ b/application/admin/lang/zh-cn.php @@ -113,6 +113,8 @@ return [ 'Set to normal' => '设为正常', 'Set to hidden' => '设为隐藏', //提示 + 'Go back' => '返回首页', + 'Jump now' => '立即跳转', 'Operation completed' => '操作成功!', 'Operation failed' => '操作失败!', 'Unknown data format' => '未知的数据格式!', diff --git a/application/admin/lang/zh-cn/addon.php b/application/admin/lang/zh-cn/addon.php old mode 100644 new mode 100755 index 3fb633b0..c00f1527 --- a/application/admin/lang/zh-cn/addon.php +++ b/application/admin/lang/zh-cn/addon.php @@ -23,7 +23,7 @@ return [ 'Pay new window tips' => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!', 'Uninstall tips' => '确认卸载插件?

卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!

如有重要数据请备份后再操作!', 'Upgrade tips' => '确认升级插件?

如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!
升级后可能出现部分冗余数据记录,请根据需要移除即可!!!

如有重要数据请备份后再操作!', - 'Offline installed tips' => '插件安装成功!你需要手动启用该插件,并清除缓存使之生效', + 'Offline installed tips' => '插件安装成功!清除插件缓存和框架缓存后生效!', 'Online installed tips' => '插件安装成功!清除插件缓存和框架缓存后生效!', 'Not login tips' => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!', 'Not installed tips' => '请安装后再访问插件前台页面!', diff --git a/application/admin/library/traits/Backend.php b/application/admin/library/traits/Backend.php old mode 100644 new mode 100755 index 8645e58b..37087670 --- a/application/admin/library/traits/Backend.php +++ b/application/admin/library/traits/Backend.php @@ -31,6 +31,7 @@ trait Backend ->limit($offset, $limit) ->select(); + $list = collection($list)->toArray(); $result = array("total" => $total, "rows" => $list); return json($result); diff --git a/application/admin/view/common/header.html b/application/admin/view/common/header.html old mode 100644 new mode 100755 index 883fa51c..430969ea --- a/application/admin/view/common/header.html +++ b/application/admin/view/common/header.html @@ -90,13 +90,13 @@
  • diff --git a/application/common/lang/zh-cn/addon.php b/application/common/lang/zh-cn/addon.php old mode 100644 new mode 100755 index 6b777137..a33b83ba --- a/application/common/lang/zh-cn/addon.php +++ b/application/common/lang/zh-cn/addon.php @@ -85,6 +85,8 @@ return [ 'Donation' => '捐赠', 'Forum' => '社区', 'Docs' => '文档', + 'Go back' => '返回首页', + 'Jump now' => '立即跳转', 'Please login first' => '请登录后再操作', 'Send verification code' => '发送验证码', 'Redirect now' => '立即跳转', diff --git a/application/common/view/tpl/dispatch_jump.tpl b/application/common/view/tpl/dispatch_jump.tpl old mode 100644 new mode 100755 index 5869a6df..c6b3878d --- a/application/common/view/tpl/dispatch_jump.tpl +++ b/application/common/view/tpl/dispatch_jump.tpl @@ -2,7 +2,7 @@ - 跳转提示 + {:__('Warning')} -
    + {php}$codeText=$code == 1 ? 'success' : ($code == 0 ? 'error' : 'info');{/php} +
    - +
    -

    +

    {$msg}

    - 页面将在 秒后自动跳转 + {:__('This page will be re-directed in %s seconds', '' . $wait . '')}

    - 返回上一步 - 立即跳转 + {:__('Go back')} + {:__('Jump now')}