作者 Karson

新增CRUD多表关联

新增CRUD筛选字段
新增表格按钮组按钮url支持function功能
新增表格按钮组按钮hidden参数
新增菜单生成支持多控制器

优化错误页面和跳转页面多语言
优化一键生成API文档在PHP5下的提示
优化第三方依赖包配置规则
... ... @@ -8,6 +8,7 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。
* 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
* 支持单管理员多角色
* 支持目录和控制器结构一键生成权限节点
* 支持管理子级数据或个人数据
* 完善的前端功能组件开发
* 基于`AdminLTE`二次开发
* 基于`Bootstrap`开发,自适应手机、平板、PC
... ...
... ... @@ -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];
... ...
... ... @@ -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()
... ...
<!DOCTYPE html>
<html lang="en">
<html lang="{$config.language}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
... ...
... ... @@ -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;
$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);
foreach ($relations as $index => &$relation) {
if ($relation['relationMode'] == 'hasone') {
$relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey;
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 must be contain field:' . $relationPrimaryKey);
if (!in_array($relationPrimaryKey, $fieldArr)) {
throw new Exception('table [' . $modelTableName . '] 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);
} 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, $relationFieldArr))
{
throw new Exception('relation table must be contain field:' . $relationPrimaryKey);
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
{
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("<div class='control-relative'>%s</div>", Form::input('text', $fieldName, $defaultValue, $attrArr));
$formEditElement = sprintf("<div class='control-relative'>%s</div>", 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 = <<<EOD
return \$value && !is_numeric(\$value) ? strtotime(\$value) : \$value;
EOD;
}
else if (in_array($inputType, ['checkbox', 'select']))
{
} else if (in_array($inputType, ['checkbox', 'select'])) {
$return = <<<EOD
return is_array(\$value) ? implode(',', \$value) : \$value;
EOD;
... ... @@ -888,16 +886,13 @@ EOD;
protected function getModelName($model, $table)
{
if (!$model)
{
if (!$model) {
$modelarr = explode('_', strtolower($table));
foreach ($modelarr as $k => &$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<<<EOD
return <<<EOD
<div class="form-group">
<label for="c-{$field}" class="control-label col-xs-12 col-sm-2">{:__('{$langField}')}:</label>
<div class="col-xs-12 col-sm-8">
... ... @@ -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 .= "}";
... ...
... ... @@ -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);
... ...
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
... ...
... ... @@ -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%}
}
... ...
... ... @@ -4,9 +4,9 @@ namespace {%modelNamespace%};
use think\Model;
class {%relationModelName%} extends Model
class {%relationName%} extends Model
{
// 表名
protected ${%relationModelTableType%} = '{%relationModelTableName%}';
protected ${%relationTableType%} = '{%relationTableTypeName%}';
}
... ...
... ... @@ -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
... ...
... ... @@ -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;
}
}
... ...
... ... @@ -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 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)
/**
* 生成下拉列表
* @param string $name
* @param mixed $options
* @param mixed $selected
* @param mixed $attr
* @return string
*/
function build_select($name, $options, $selected = [], $attr = [])
{
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
$options = is_array($options) ? $options : explode(',', $options);
$selected = is_array($selected) ? $selected : explode(',', $selected);
return Form::select($name, $options, $selected, $attr);
}
return '<div class="radio">' . implode(' ', $html) . '</div>';
}
/**
* 生成复选按钮组
* @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)
if (!function_exists('build_radios')) {
/**
* 生成单选按钮组
* @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}"]));
$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 '<div class="radio">' . implode(' ', $html) . '</div>';
}
return '<div class="checkbox">' . implode(' ', $html) . '</div>';
}
/**
* 生成分类下拉列表框
* @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)
if (!function_exists('build_checkboxs')) {
/**
* 生成复选按钮组
* @param string $name
* @param array $list
* @param mixed $selected
* @return string
*/
function build_checkboxs($name, $list = [], $selected = null)
{
$categorydata[$v['id']] = $v['name'];
$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 '<div class="checkbox">' . implode(' ', $html) . '</div>';
}
$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 (!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 = [])
{
//如果未定义或没有权限
if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}")))
{
continue;
$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'];
}
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[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
$attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr);
return build_select($name, $categorydata, $selected, $attr);
}
return implode(' ', $html);
}
/**
* 生成页面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)
if (!function_exists('build_toolbar')) {
/**
* 生成表格操作按钮栏
* @param array $btns 按钮组
* @param array $attr 按钮属性值
* @return string
*/
function build_toolbar($btns = NULL, $attr = [])
{
$title = __($data['title']);
$content = __($data['remark']);
$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[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
}
return implode(' ', $html);
}
if (!$content)
return '';
$result = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container)
}
if (!function_exists('build_heading')) {
/**
* 生成页面Heading
*
* @param string $path 指定的path
* @return string
*/
function build_heading($path = NULL, $container = TRUE)
{
$result = '<div class="panel-heading">' . $result . '</div>';
$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 = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container) {
$result = '<div class="panel-heading">' . $result . '</div>';
}
return $result;
}
return $result;
}
... ...
... ... @@ -113,6 +113,8 @@ return [
'Set to normal' => '设为正常',
'Set to hidden' => '设为隐藏',
//提示
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
'Unknown data format' => '未知的数据格式!',
... ...
... ... @@ -23,7 +23,7 @@ return [
'Pay new window tips' => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!',
'Uninstall tips' => '确认卸载插件?<p class="text-danger">卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!</p>如有重要数据请备份后再操作!',
'Upgrade tips' => '确认升级插件?<p class="text-danger">如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!<br>升级后可能出现部分冗余数据记录,请根据需要移除即可!!!</p>如有重要数据请备份后再操作!',
'Offline installed tips' => '插件安装成功!你需要手动启用该插件,并清除缓存使之生效',
'Offline installed tips' => '插件安装成功!清除插件缓存和框架缓存后生效!',
'Online installed tips' => '插件安装成功!清除插件缓存和框架缓存后生效!',
'Not login tips' => '你当前未登录FastAdmin,登录后将同步已购买的记录,下载时无需二次付费!',
'Not installed tips' => '请安装后再访问插件前台页面!',
... ...
... ... @@ -31,6 +31,7 @@ trait Backend
->limit($offset, $limit)
->select();
$list = collection($list)->toArray();
$result = array("total" => $total, "rows" => $list);
return json($result);
... ...
... ... @@ -90,13 +90,13 @@
<li class="user-body">
<div class="row">
<div class="col-xs-4 text-center">
<a href="http://www.fastadmin.net" target="_blank">官网</a>
<a href="http://www.fastadmin.net" target="_blank">{:__('FastAdmin')}</a>
</div>
<div class="col-xs-4 text-center">
<a href="http://forum.fastadmin.net" target="_blank">论坛</a>
<a href="http://forum.fastadmin.net" target="_blank">{:__('Forum')}</a>
</div>
<div class="col-xs-4 text-center">
<a href="http://doc.fastadmin.net" target="_blank">文档</a>
<a href="http://doc.fastadmin.net" target="_blank">{:__('Docs')}</a>
</div>
</div>
</li>
... ...
... ... @@ -85,6 +85,8 @@ return [
'Donation' => '捐赠',
'Forum' => '社区',
'Docs' => '文档',
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Please login first' => '请登录后再操作',
'Send verification code' => '发送验证码',
'Redirect now' => '立即跳转',
... ...
... ... @@ -2,7 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>跳转提示</title>
<title>{:__('Warning')}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="__CDN__/assets/img/favicon.ico" />
<style type="text/css">
... ... @@ -27,17 +27,18 @@
</style>
</head>
<body>
<div class="system-message <?php echo $code == 1 ? 'success' : ($code == 0 ? 'error' : 'info');?>">
{php}$codeText=$code == 1 ? 'success' : ($code == 0 ? 'error' : 'info');{/php}
<div class="system-message {$codeText}">
<div class="image">
<img src="__CDN__/assets/img/<?php echo $code == 1 ? 'success' : ($code == 0 ? 'error' : 'info');?>.svg" alt="" width="150" />
<img src="__CDN__/assets/img/{$codeText}.svg" alt="" width="150" />
</div>
<h1><?php echo($msg);?></h1>
<h1>{$msg}</h1>
<p class="jump">
页面将在 <span id="wait"><?php echo($wait);?></span> 秒后自动<a id="href" href="<?php echo($url);?>">跳转</a>
{:__('This page will be re-directed in %s seconds', '<span id="wait">' . $wait . '</span>')}
</p>
<p class="clearfix">
<a href="javascript:history.go(-1);" class="btn btn-grey">返回上一步</a>
<a href="<?php echo($url);?>" class="btn btn-primary">立即跳转</a>
<a href="javascript:history.go(-1);" class="btn btn-grey">{:__('Go back')}</a>
<a href="{$url}" class="btn btn-primary">{:__('Jump now')}</a>
</p>
</div>
<div class="copyright">
... ... @@ -45,12 +46,11 @@
</div>
<script type="text/javascript">
(function () {
var wait = document.getElementById('wait'),
href = document.getElementById('href').href;
var wait = document.getElementById('wait');
var interval = setInterval(function () {
var time = --wait.innerHTML;
if (time <= 0) {
location.href = href;
location.href = "{$url}";
clearInterval(interval);
}
}, 1000);
... ...
<?php
$cdnurl = function_exists('config') ? config('view_replace_str.__CDN__') : '';
$publicurl = function_exists('config') ? config('view_replace_str.__PUBLIC__') : '/';
$lang = [
'An error occurred' => '发生错误',
'Home' => '返回主页',
'Feedback' => '反馈错误',
'The page you are looking for is temporarily unavailable' => '你所浏览的页面暂时无法访问',
'You can return to the previous page and try again' => '你可以返回上一页重试,或直接向我们反馈错误报告'
];
$langSet = '';
if (isset($_GET['lang'])) {
$langSet = strtolower($_GET['lang']);
} elseif (isset($_COOKIE['think_var'])) {
$langSet = strtolower($_COOKIE['think_var']);
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
$langSet = strtolower($matches[1]);
}
$langSet = $langSet && in_array($langSet, ['zh-cn', 'en']) ? $langSet : 'zh-cn';
$langSet == 'en' && $lang = array_combine(array_keys($lang), array_keys($lang));
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>发生错误</title>
<title><?=$lang['An error occurred']?></title>
<meta name="robots" content="noindex,nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="shortcut icon" href="<?php echo $cdnurl;?>/assets/img/favicon.ico" />
... ... @@ -54,25 +75,24 @@ $publicurl = function_exists('config') ? config('view_replace_str.__PUBLIC__') :
</style>
</head>
<body class="error-page-wrapper">
<div class="content-container">
<div class="head-line">
<img src="<?php echo $cdnurl;?>/assets/img/error.svg" alt="" width="120" />
<img src="<?=$cdnurl?>/assets/img/error.svg" alt="" width="120"/>
</div>
<div class="subheader">
<?php echo htmlentities($message); ?>
<?=$lang['The page you are looking for is temporarily unavailable']?>
</div>
<div class="hr"></div>
<div class="context">
<p>
你可以返回上一页重试,或直接向我们反馈错误报告
<?=$lang['You can return to the previous page and try again']?>
</p>
</div>
<div class="buttons-container">
<a href="<?php echo $publicurl;?>">返回主页</a>
<a href="<?php echo $publicurl;?>">反馈错误</a>
<a href="<?=$publicurl?>"><?=$lang['Home']?></a>
<a href="<?=$publicurl?>"><?=$lang['Feedback']?></a>
</div>
</div>
</body>
... ...
... ... @@ -253,7 +253,7 @@ return [
//自动检测更新
'checkupdate' => false,
//版本号
'version' => '1.0.0.20180310_beta',
'version' => '1.0.0.20180314_beta',
'api_url' => 'http://api.fastadmin.net',
],
];
... ...
<?php
return [
'Title' => 'Title',
'Auth tips' => 'Unlimited parent-child permission grouping, administrator can belong to multiple groups at the same time',
'Responsive tips' => 'Based on Bootstrap and AdminLTE, mobile phones, tablets, PCs are automatically adapted',
'Languages tips' => 'Backend and Frontend support, View and JS share the same language package',
'Module tips' => 'RequireJS and Bower for frontend package component management,Composer for backend package component management',
'CRUD tips' => 'One key to generate a controller, model, view and JS file, one key to generate an API document and auth rule',
'Extension tips' => 'Installed and uninstalled directly online plug-in, supporting the command line one key operation',
'Do not hesitate' => 'Do not hesitate',
'Start to act' => 'Start Action',
];
\ No newline at end of file
... ...
... ... @@ -68,6 +68,8 @@ return [
'Parent' => '父级',
'Params' => '参数',
'Permission' => '权限',
'Go back' => '返回上一页',
'Jump now' => '立即跳转',
'Advance search' => '高级搜索',
'Check all' => '选中全部',
'Expand all' => '展开全部',
... ... @@ -96,6 +98,8 @@ return [
'Donation' => '捐赠',
'Forum' => '社区',
'Docs' => '文档',
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Please login first' => '请登录后再操作',
'Send verification code' => '发送验证码',
'Redirect now' => '立即跳转',
... ...
<?php
return [
'Title' => '标题',
'Title' => '标题',
'Auth tips' => '基于完善的Auth权限控制管理、无限父子级权限分组、可自由分配子级权限、一个管理员可同时属于多个组别',
'Responsive tips' => '基于Bootstrap和AdminLTE进行二次开发,手机、平板、PC均自动适配,无需要担心兼容性问题',
'Languages tips' => '不仅仅后台开发支持多语言,同时视图部分和JS部分仍然共享同一个语言包,语法相同且自动加载',
'Module tips' => '控制器、模型、视图、JS一一对应,使用RequireJS进行JS模块化管理,采用Bower进行前端包组件管理',
'CRUD tips' => '控制台进行一键生成控制器、模型、视图和JS文件,一键生成API文档,一键生成后台权限节点和菜单栏',
'Extension tips' => 'FastAdmin提供强大的扩展中心,可直接在线安装和卸载插件,同时支持命令行一键操作',
'Do not hesitate' => '不要犹豫',
'Start to act' => '开始行动',
];
... ...
... ... @@ -33,6 +33,9 @@ return [
'Mobile already exist' => '手机号已经存在',
'Username is incorrect' => '用户名不正确',
'Email is incorrect' => '邮箱不正确',
'Reset password' => '修改密码',
'Reset password by email' => '通过邮箱',
'Reset password by mobile' => '通过手机重置',
'Account is locked' => '账户已经被锁定',
'Password is incorrect' => '密码不正确',
'Account is incorrect' => '账户不正确',
... ... @@ -41,6 +44,7 @@ return [
'Username or password is incorrect' => '用户名或密码不正确',
'You are not logged in' => '你当前还未登录',
'You\'ve logged in, do not login again' => '你已经登录,请不要重复登录',
'This guy hasn\'t written anything yet' => '这个人很懒,啥也没写',
'Profile' => '个人资料',
'Old password' => '旧密码',
'New password' => '新密码',
... ... @@ -50,6 +54,7 @@ return [
'New mobile' => '新手机号',
'Change password successful' => '修改密码成功',
'Captcha is incorrect' => '验证码不正确',
'Upload successful' => '上传成功',
'Sign up successful' => '注册成功',
'Logged in successful' => '登录成功',
'Logout successful' => '注销成功',
... ...
... ... @@ -87,42 +87,42 @@
<div class="feature-item">
<i class="icon-user text-primary"></i>
<h3>{:__('Auth')}</h3>
<p class="text-muted">基于完善的Auth权限控制管理、无限父子级权限分组、可自由分配子级权限、一个管理员可同时属于多个组别</p>
<p class="text-muted">{:__('Auth tips')}</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-item">
<i class="icon-screen-smartphone text-primary"></i>
<h3>{:__('Responsive')}</h3>
<p class="text-muted">基于Bootstrap和AdminLTE进行二次开发,手机、平板、PC均自动适配,无需要担心兼容性问题</p>
<p class="text-muted">{:__('Responsive tips')}</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-item">
<i class="icon-present text-primary"></i>
<h3>{:__('Languages')}</h3>
<p class="text-muted">不仅仅后台开发支持多语言,同时视图部分和JS部分仍然共享同一个语言包,语法相同且自动加载</p>
<p class="text-muted">{:__('Languages tips')}</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-item">
<i class="icon-layers text-primary"></i>
<h3>{:__('Module')}</h3>
<p class="text-muted">控制器、模型、视图、JS一一对应,使用RequireJS进行JS模块化管理,采用Bower进行前端包组件管理</p>
<p class="text-muted">{:__('Module tips')}</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-item">
<i class="icon-docs text-primary"></i>
<h3>{:__('CRUD')}</h3>
<p class="text-muted">控制台进行一键生成控制器、模型、视图和JS文件,同时可一键生成后台权限节点和菜单栏</p>
<p class="text-muted">{:__('CRUD tips')}</p>
</div>
</div>
<div class="col-md-4">
<div class="feature-item">
<i class="icon-puzzle text-primary"></i>
<h3>{:__('Extension')}</h3>
<p class="text-muted">FastAdmin提供强大的扩展中心,可直接在线安装和卸载插件,同时支持命令行一键操作</p>
<p class="text-muted">{:__('Extension tips')}</p>
</div>
</div>
</div>
... ... @@ -135,7 +135,7 @@
<section class="cta">
<div class="cta-content">
<div class="container">
<h2>不要犹豫<br>开始行动</h2>
<h2>{:__('Do not hesitate')}<br>{:__('Start to act')}</h2>
<a href="http://doc.fastadmin.net/docs/contributing.html" class="btn btn-outline btn-xl page-scroll">{:__('Contribution')}</a>
</div>
</div>
... ...
... ... @@ -10,19 +10,19 @@
<form id="changepwd-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">旧密码:</label>
<label for="oldpassword" class="control-label col-xs-12 col-sm-2">{:__('Old password')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="password" class="form-control" id="oldpassword" name="oldpassword" value="" data-rule="required" placeholder="{:__('Old password')}">
</div>
</div>
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('New password')}:</label>
<label for="newpassword" class="control-label col-xs-12 col-sm-2">{:__('New password')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="password" class="form-control" id="newpassword" name="newpassword" value="" data-rule="required" placeholder="{:__('New password')}" />
</div>
</div>
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2">{:__('Renew password')}:</label>
<label for="renewpassword" class="control-label col-xs-12 col-sm-2">{:__('Renew password')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="password" class="form-control" id="renewpassword" name="renewpassword" value="" data-rule="required" placeholder="{:__('Renew password')}" />
</div>
... ...
... ... @@ -19,7 +19,7 @@
<!-- Heading -->
<h4><a href="{:url('user/profile')}">{$user.username}</a></h4>
<!-- Paragraph -->
<p><a href="{:url('user/profile')}">{$user.bio|default='这个人很懒,啥也没写'}</a></p>
<p><a href="{:url('user/profile')}">{$user.bio|default=__("This guy hasn't written anything yet")}</a></p>
<!-- Success -->
<div style="margin-top:15px;">
<table class="table">
... ...
... ... @@ -37,8 +37,8 @@
<label for="" class="control-label col-xs-12 col-sm-3">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="radio">
<label for="type-email"><input id="type-email" checked="checked" name="type" data-send-url="{:url('api/ems/send')}" data-check-url="{:url('api/validate/check_ems_correct')}" type="radio" value="email"> 通过邮箱找回</label>
<label for="type-mobile"><input id="type-mobile" name="type" type="radio" data-send-url="{:url('api/sms/send')}" data-check-url="{:url('api/validate/check_sms_correct')}" value="mobile"> 通过手机找回</label>
<label for="type-email"><input id="type-email" checked="checked" name="type" data-send-url="{:url('api/ems/send')}" data-check-url="{:url('api/validate/check_ems_correct')}" type="radio" value="email"> {:__('Reset password by email')}</label>
<label for="type-mobile"><input id="type-mobile" name="type" type="radio" data-send-url="{:url('api/sms/send')}" data-check-url="{:url('api/validate/check_sms_correct')}" value="mobile"> {:__('Reset password by mobile')}</label>
</div>
</div>
</div>
... ...
... ... @@ -41,7 +41,7 @@
{:token()}
<input type="hidden" name="avatar" id="c-avatar" value="{$user.avatar}" />
<div class="form-group">
<label for="c-name" class="control-label col-xs-12 col-sm-2"></label>
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-4">
<div class="profile-avatar-container">
<img class="profile-user-img img-responsive img-circle plupload" src="{$user.avatar}" alt="">
... ... @@ -51,13 +51,13 @@
</div>
</div>
<div class="form-group">
<label for="c-username" class="control-label col-xs-12 col-sm-2">{:__('Username')}:</label>
<label class="control-label col-xs-12 col-sm-2">{:__('Username')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control" id="username" name="username" value="{$user.username}" data-rule="required;username;remote({:url('api/validate/check_username_available')}, id={$user.id})" placeholder="">
</div>
</div>
<div class="form-group">
<label for="c-nickname" class="control-label col-xs-12 col-sm-2">{:__('Nickname')}:</label>
<label class="control-label col-xs-12 col-sm-2">{:__('Nickname')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control" id="nickname" name="nickname" value="{$user.nickname}" data-rule="required" placeholder="">
</div>
... ... @@ -111,14 +111,14 @@
<div class="form-body">
<input type="hidden" name="action" value="changeemail" />
<div class="form-group">
<label for="c-email" class="control-label col-xs-12 col-sm-3">{:__('New Email')}:</label>
<label class="control-label col-xs-12 col-sm-3">{:__('New Email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="email" name="email" value="" data-rule="required;email;remote({:url('api/validate/check_email_available')}, event=changeemail, id={$user.id})" placeholder="{:__('New email')}">
<span class="msg-box"></span>
</div>
</div>
<div class="form-group">
<label for="c-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<label class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" id="email-captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_ems_correct')}, event=changeemail, email:#email)" />
... ... @@ -152,7 +152,7 @@
</div>
</div>
<div class="form-group">
<label for="c-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<label for="mobile-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" id="mobile-captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=changemobile, mobile:#mobile)" />
... ...
... ... @@ -9,26 +9,26 @@
"jquery": "^2.1.4",
"bootstrap": "^3.3.7",
"font-awesome": "^4.6.1",
"bootstrap-table": "^1.11.0",
"bootstrap-table": "~1.11.0",
"layer": "^3.0",
"jstree": "^3.3.2",
"moment": "^2.15.2",
"plupload": "^2.2.0",
"toastr": "^2.1.3",
"jcrop": "^2.0.4",
"eonasdan-bootstrap-datetimepicker": "^4.17.43",
"bootstrap-select": "^1.11.2",
"require-css": "^0.1.8",
"less": "^2.7.1",
"tableExport.jquery.plugin": "^1.9.0",
"jquery-slimscroll": "^1.3.8",
"jquery.cookie": "^1.4.1",
"Sortable": "^1.5.0",
"nice-validator": "^1.1.1",
"art-template": "^3.0.1",
"requirejs-plugins": "^1.0.3",
"bootstrap-daterangepicker": "^2.1.25",
"city-picker": "^1.1.0",
"jstree": "~3.3.2",
"moment": "~2.15.2",
"plupload": "~2.2.0",
"toastr": "~2.1.3",
"jcrop": "~2.0.4",
"eonasdan-bootstrap-datetimepicker": "~4.17.43",
"bootstrap-select": "~1.11.2",
"require-css": "~0.1.8",
"less": "~2.7.1",
"tableExport.jquery.plugin": "~1.9.0",
"jquery-slimscroll": "~1.3.8",
"jquery.cookie": "~1.4.1",
"Sortable": "~1.5.0",
"nice-validator": "~1.1.1",
"art-template": "^3.1.3",
"requirejs-plugins": "~1.0.3",
"bootstrap-daterangepicker": "~2.1.25",
"city-picker": "~1.1.0",
"fastadmin-cxselect": "~1.4.0",
"fastadmin-dragsort": "~1.0.0",
"fastadmin-addtabs": "~1.0.0",
... ...
... ... @@ -3351,7 +3351,7 @@
<div class="row mt0 footer">
<div class="col-md-6" align="left">
Generated on 2018-03-10 00:52:53 </div>
Generated on 2018-03-13 19:46:39 </div>
<div class="col-md-6" align="right">
<a href="http://www.fastadmin.net" target="_blank">FastAdmin</a>
</div>
... ...
... ... @@ -37,7 +37,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
var content = Template(id, {});
Layer.open({
type: 1,
title: "修改",
title: __('Reset password'),
area: ["450px", "355px"],
content: content,
success: function (layero) {
... ... @@ -75,7 +75,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
$("#plupload-avatar").data("upload-success", function (data) {
var url = Fast.api.cdnurl(data.url);
$(".profile-user-img").prop("src", url);
Toastr.success("上传成功!");
Toastr.success(__('Upload successful'));
});
Form.api.bindevent($("#profile-form"));
$(document).on("click", ".btn-change", function () {
... ... @@ -91,7 +91,6 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
var form = $("form", layero);
Form.api.bindevent(form, function (data) {
Layer.closeAll();
console.log(123);
});
}
});
... ...
... ... @@ -202,12 +202,12 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
var that = this;
var ids = Table.api.selectedids(table);
Layer.confirm(
__('Are you sure you want to delete the %s selected item?', ids.length),
{icon: 3, title: __('Warning'), offset: 0, shadeClose: true},
function (index) {
Table.api.multi("del", ids, table, that);
Layer.close(index);
}
__('Are you sure you want to delete the %s selected item?', ids.length),
{icon: 3, title: __('Warning'), offset: 0, shadeClose: true},
function (index) {
Table.api.multi("del", ids, table, that);
Layer.close(index);
}
);
});
// 拖拽排序
... ... @@ -281,12 +281,12 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
var id = $(this).data("id");
var that = this;
Layer.confirm(
__('Are you sure you want to delete this item?'),
{icon: 3, title: __('Warning'), shadeClose: true},
function (index) {
Table.api.multi("del", id, table, that);
Layer.close(index);
}
__('Are you sure you want to delete this item?'),
{icon: 3, title: __('Warning'), shadeClose: true},
function (index) {
Table.api.multi("del", id, table, that);
Layer.close(index);
}
);
});
var id = table.attr("id");
... ... @@ -345,14 +345,14 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
top = left = undefined;
}
Layer.confirm(
__('Are you sure you want to delete this item?'),
{icon: 3, title: __('Warning'), offset: [top, left], shadeClose: true},
function (index) {
var table = $(that).closest('table');
var options = table.bootstrapTable('getOptions');
Table.api.multi("del", row[options.pk], table, that);
Layer.close(index);
}
__('Are you sure you want to delete this item?'),
{icon: 3, title: __('Warning'), offset: [top, left], shadeClose: true},
function (index) {
var table = $(that).closest('table');
var options = table.bootstrapTable('getOptions');
Table.api.multi("del", row[options.pk], table, that);
Layer.close(index);
}
);
}
}
... ... @@ -449,13 +449,29 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
var buttons = $.extend([], this.buttons || []);
if (options.extend.dragsort_url !== '') {
buttons.push({name: 'dragsort', icon: 'fa fa-arrows', title: __('Drag to sort'), classname: 'btn btn-xs btn-primary btn-dragsort'});
buttons.push({
name: 'dragsort',
icon: 'fa fa-arrows',
title: __('Drag to sort'),
classname: 'btn btn-xs btn-primary btn-dragsort'
});
}
if (options.extend.edit_url !== '') {
buttons.push({name: 'edit', icon: 'fa fa-pencil', title: __('Edit'), classname: 'btn btn-xs btn-success btn-editone', url: options.extend.edit_url});
buttons.push({
name: 'edit',
icon: 'fa fa-pencil',
title: __('Edit'),
classname: 'btn btn-xs btn-success btn-editone',
url: options.extend.edit_url
});
}
if (options.extend.del_url !== '') {
buttons.push({name: 'del', icon: 'fa fa-trash', title: __('Del'), classname: 'btn btn-xs btn-danger btn-delone'});
buttons.push({
name: 'del',
icon: 'fa fa-trash',
title: __('Del'),
classname: 'btn btn-xs btn-danger btn-delone'
});
}
return Table.api.buttonlink(this, buttons, value, row, index, 'operate');
},
... ... @@ -470,7 +486,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
type = typeof type === 'undefined' ? 'buttons' : type;
var options = table ? table.bootstrapTable('getOptions') : {};
var html = [];
var url, classname, icon, text, title, extend;
var hidden, url, classname, icon, text, title, refresh, confirm, extend;
var fieldIndex = column.fieldIndex;
$.each(buttons, function (i, j) {
... ... @@ -484,8 +500,12 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
}
var attr = table.data(type + "-" + j.name);
if (typeof attr === 'undefined' || attr) {
hidden = typeof j.hidden === 'function' ? j.hidden.call(table, row, j) : (j.hidden ? j.hidden : false);
if (hidden) {
return true;
}
url = j.url ? j.url : '';
url = 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';
icon = j.icon ? j.icon : '';
text = j.text ? j.text : '';
... ...