新增CRUD多表关联

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

优化错误页面和跳转页面多语言
优化一键生成API文档在PHP5下的提示
优化第三方依赖包配置规则
pull/49/head^2 v1.0.0.20180314_beta
Karson 2018-03-14 16:51:56 +08:00
parent 97ee22dde6
commit f28089b1f0
34 changed files with 4820 additions and 5037 deletions

View File

@ -8,6 +8,7 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。
* 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置
* 支持单管理员多角色
* 支持目录和控制器结构一键生成权限节点
* 支持管理子级数据或个人数据
* 完善的前端功能组件开发
* 基于`AdminLTE`二次开发
* 基于`Bootstrap`开发自适应手机、平板、PC

90
application/admin/command/Api.php 100644 → 100755
View File

@ -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];

View File

@ -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()

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{$config.language}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">

711
application/admin/command/Crud.php 100644 → 100755

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -1,5 +1,5 @@
public function {%relationMethod%}()
{
return $this->{%relationMode%}('{%relationModelName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}')->setEagerlyType(0);
return $this->{%relationMode%}('{%relationName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0);
}

View 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%}
}

View File

@ -4,9 +4,9 @@ namespace {%modelNamespace%};
use think\Model;
class {%relationModelName%} extends Model
class {%relationName%} extends Model
{
// 表名
protected ${%relationModelTableType%} = '{%relationModelTableName%}';
protected ${%relationTableType%} = '{%relationTableTypeName%}';
}

View File

@ -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

View File

@ -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;
}
}

280
application/admin/common.php 100644 → 100755
View File

@ -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 '<div class="radio">' . implode(' ', $html) . '</div>';
}
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 '<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)
{
$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[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
return '<div class="radio">' . implode(' ', $html) . '</div>';
}
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 '<div class="checkbox">' . implode(' ', $html) . '</div>';
}
}
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[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
}
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 = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container) {
$result = '<div class="panel-heading">' . $result . '</div>';
}
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 = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container)
{
$result = '<div class="panel-heading">' . $result . '</div>';
}
return $result;
}

2
application/admin/lang/zh-cn.php 100644 → 100755
View File

@ -113,6 +113,8 @@ return [
'Set to normal' => '设为正常',
'Set to hidden' => '设为隐藏',
//提示
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
'Unknown data format' => '未知的数据格式!',

View File

@ -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' => '请安装后再访问插件前台页面!',

View File

@ -31,6 +31,7 @@ trait Backend
->limit($offset, $limit)
->select();
$list = collection($list)->toArray();
$result = array("total" => $total, "rows" => $list);
return json($result);

View File

@ -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>

View File

@ -85,6 +85,8 @@ return [
'Donation' => '捐赠',
'Forum' => '社区',
'Docs' => '文档',
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Please login first' => '请登录后再操作',
'Send verification code' => '发送验证码',
'Redirect now' => '立即跳转',

View File

@ -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);

View File

@ -1,12 +1,33 @@
<?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>

View File

@ -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',
],
];

View File

@ -0,0 +1,13 @@
<?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',
];

4
application/index/lang/zh-cn.php 100644 → 100755
View 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' => '立即跳转',

View File

@ -1,5 +1,13 @@
<?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' => '开始行动',
];

View File

@ -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' => '注销成功',

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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)" />

38
bower.json 100644 → 100755
View File

@ -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",

2
public/api.html 100644 → 100755
View File

@ -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>

5
public/assets/js/frontend/user.js 100644 → 100755
View File

@ -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);
});
}
});

File diff suppressed because one or more lines are too long

View File

@ -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 : '';