diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php index 80d5ee4d..611f9fe6 100644 --- a/application/admin/controller/Index.php +++ b/application/admin/controller/Index.php @@ -105,7 +105,8 @@ class Index extends Backend { $this->redirect($url); } - $background = cdnurl(Config::get('fastadmin.login_background')); + $background = Config::get('fastadmin.login_background'); + $background = stripos($background, 'http')===0 ? $background : config('site.cdnurl') . $background; $this->view->assign('background', $background); $this->view->assign('title', __('Login')); Hook::listen("login_init", $this->request); diff --git a/application/admin/controller/general/Config.php b/application/admin/controller/general/Config.php index b594fda1..573a04e3 100644 --- a/application/admin/controller/general/Config.php +++ b/application/admin/controller/general/Config.php @@ -25,6 +25,9 @@ class Config extends Backend $this->model = model('Config'); } + /** + * 查看 + */ public function index() { $siteList = []; @@ -48,10 +51,6 @@ class Config extends Backend { $value['value'] = explode(',', $value['value']); } - if ($value['type'] == 'array') - { - $value['value'] = (array) json_decode($value['value'], TRUE); - } $value['content'] = json_decode($value['content'], TRUE); $siteList[$v['group']]['list'][] = $value; } @@ -119,6 +118,10 @@ class Config extends Backend return $this->view->fetch(); } + /** + * 编辑 + * @param null $ids + */ public function edit($ids = NULL) { if ($this->request->isPost()) diff --git a/application/admin/view/general/config/index.html b/application/admin/view/general/config/index.html index 9013cd0b..9875aab4 100644 --- a/application/admin/view/general/config/index.html +++ b/application/admin/view/general/config/index.html @@ -54,20 +54,13 @@ {/case} {case array} -
+
{:__('Array key')} {:__('Array value')}
- {foreach $item.value as $key => $vo} -
- - - - -
- {/foreach} -
{:__('Append')}
+
{:__('Append')}
+
{/case} {case datetime} diff --git a/application/common/library/Token.php b/application/common/library/Token.php index d42679cf..2751f644 100644 --- a/application/common/library/Token.php +++ b/application/common/library/Token.php @@ -2,84 +2,158 @@ namespace app\common\library; +use app\common\library\token\Driver; +use think\App; +use think\Config; +use think\Log; + /** * Token操作类 */ class Token { + /** + * @var array Token的实例 + */ + public static $instance = []; /** - * 存储Token - * @param string $token Token - * @param int $user_id 会员ID - * @param int $expire 过期时长,0表示无限,单位秒 + * @var object 操作句柄 */ - public static function set($token, $user_id, $expire = 0) + public static $handler; + + /** + * 连接Token驱动 + * @access public + * @param array $options 配置数组 + * @param bool|string $name Token连接标识 true 强制重新连接 + * @return Driver + */ + public static function connect(array $options = [], $name = false) { - $expiretime = $expire ? time() + $expire : 0; - \app\common\model\Token::create(['token' => $token, 'user_id' => $user_id, 'expiretime' => $expiretime]); - return TRUE; + $type = !empty($options['type']) ? $options['type'] : 'File'; + + if (false === $name) { + $name = md5(serialize($options)); + } + + if (true === $name || !isset(self::$instance[$name])) { + $class = false === strpos($type, '\\') ? + '\\app\\common\\library\\token\\driver\\' . ucwords($type) : + $type; + + // 记录初始化信息 + App::$debug && Log::record('[ TOKEN ] INIT ' . $type, 'info'); + + if (true === $name) { + return new $class($options); + } + + self::$instance[$name] = new $class($options); + } + + return self::$instance[$name]; } /** - * 获取Token内的信息 - * @param string $token - * @return array + * 自动初始化Token + * @access public + * @param array $options 配置数组 + * @return Driver */ - public static function get($token) + public static function init(array $options = []) { - $data = \app\common\model\Token::get($token); - if ($data) - { - if (!$data['expiretime'] || $data['expiretime'] > time()) - { - return $data; - } - else - { - self::delete($token); + if (is_null(self::$handler)) { + if (empty($options) && 'complex' == Config::get('token.type')) { + $default = Config::get('token.default'); + // 获取默认Token配置,并连接 + $options = Config::get('token.' . $default['type']) ?: $default; + } elseif (empty($options)) { + $options = Config::get('token'); } + + self::$handler = self::connect($options); } - return []; + + return self::$handler; + } + + /** + * 判断Token是否可用(check别名) + * @access public + * @param string $token Token标识 + * @return bool + */ + public static function has($token, $user_id) + { + return self::check($token, $user_id); } /** * 判断Token是否可用 - * @param string $token Token - * @param int $user_id 会员ID - * @return boolean + * @param string $token Token标识 + * @return bool */ public static function check($token, $user_id) { - $data = self::get($token); - return $data && $data['user_id'] == $user_id ? true : false; + return self::init()->check($token, $user_id); + } + + /** + * 读取Token + * @access public + * @param string $token Token标识 + * @param mixed $default 默认值 + * @return mixed + */ + public static function get($token, $default = false) + { + return self::init()->get($token, $default); + } + + /** + * 写入Token + * @access public + * @param string $token Token标识 + * @param mixed $user_id 存储数据 + * @param int|null $expire 有效时间 0为永久 + * @return boolean + */ + public static function set($token, $user_id, $expire = null) + { + return self::init()->set($token, $user_id, $expire); + } + + /** + * 删除Token(delete别名) + * @access public + * @param string $token Token标识 + * @return boolean + */ + public static function rm($token) + { + return self::delete($token); } /** * 删除Token - * @param string $token - * @return boolean + * @param string $token 标签名 + * @return bool */ public static function delete($token) { - $data = \app\common\model\Token::get($token); - if ($data) - { - $data->delete(); - return true; - } - return false; + return self::init()->delete($token); } /** - * 删除指定用户的所有Token - * @param int $user_id - * @return boolean + * 清除Token + * @access public + * @param string $token Token标记 + * @return boolean */ - public static function clear($user_id) + public static function clear($user_id = null) { - \app\common\model\Token::where('user_id', $user_id)->delete(); - return true; + return self::init()->clear($user_id); } } diff --git a/application/common/library/token/Driver.php b/application/common/library/token/Driver.php new file mode 100644 index 00000000..7e2dfa77 --- /dev/null +++ b/application/common/library/token/Driver.php @@ -0,0 +1,91 @@ + +// +---------------------------------------------------------------------- + +namespace app\common\library\token; + +/** + * Token基础类 + */ +abstract class Driver +{ + protected $handler = null; + protected $options = []; + + /** + * 存储Token + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 + * @return bool + */ + abstract function set($token, $user_id, $expire = 0); + + /** + * 获取Token内的信息 + * @param string $token + * @return array + */ + abstract function get($token); + + /** + * 判断Token是否可用 + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean + */ + abstract function check($token, $user_id); + + /** + * 删除Token + * @param string $token + * @return boolean + */ + abstract function delete($token); + + /** + * 删除指定用户的所有Token + * @param int $user_id + * @return boolean + */ + abstract function clear($user_id); + + /** + * 返回句柄对象,可执行其它高级方法 + * + * @access public + * @return object + */ + public function handler() + { + return $this->handler; + } + + /** + * 获取加密后的Token + * @param string $token Token标识 + * @return string + */ + protected function getEncryptedToken($token) + { + $config = \think\Config::get('token'); + return hash_hmac($config['hashalgo'], $token, $config['key']); + } + + /** + * 获取过期剩余时长 + * @param $expiretime + * @return float|int|mixed + */ + protected function getExpiredIn($expiretime) + { + return $expiretime ? max(0, $expiretime - time()) : 365 * 86400; + } +} diff --git a/application/common/library/token/driver/Mysql.php b/application/common/library/token/driver/Mysql.php new file mode 100644 index 00000000..c61fcd6f --- /dev/null +++ b/application/common/library/token/driver/Mysql.php @@ -0,0 +1,112 @@ + 'user_token', + 'expire' => 2592000, + 'connection' => [], + ]; + + + /** + * 构造函数 + * @param array $options 参数 + * @access public + */ + public function __construct($options = []) + { + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + if ($this->options['connection']) { + $this->handler = \think\Db::connect($this->options['connection'])->name($this->options['table']); + } else { + $this->handler = \think\Db::name('user_token'); + } + } + + /** + * 存储Token + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 + * @return bool + */ + public function set($token, $user_id, $expire = null) + { + $expiretime = !is_null($expire) ? time() + $expire : ($expire === 0 ? 0 : time() + $this->options['expire']); + $token = $this->getEncryptedToken($token); + $this->handler->insert(['token' => $token, 'user_id' => $user_id, 'createtime' => time(), 'expiretime' => $expiretime]); + return TRUE; + } + + /** + * 获取Token内的信息 + * @param string $token + * @return array + */ + public function get($token) + { + $data = $this->handler->where('token', $this->getEncryptedToken($token))->find(); + if ($data) { + if (!$data['expiretime'] || $data['expiretime'] > time()) { + //返回未加密的token给客户端使用 + $data['token'] = $token; + //返回剩余有效时间 + $data['expired_in'] = $this->getExpiredIn($data['expiretime']); + return $data; + } else { + self::delete($token); + } + } + return []; + } + + /** + * 判断Token是否可用 + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean + */ + public function check($token, $user_id) + { + $data = $this->get($token); + return $data && $data['user_id'] == $user_id ? true : false; + } + + /** + * 删除Token + * @param string $token + * @return boolean + */ + public function delete($token) + { + $this->handler->where('token', $this->getEncryptedToken($token))->delete(); + return true; + } + + /** + * 删除指定用户的所有Token + * @param int $user_id + * @return boolean + */ + public function clear($user_id) + { + $this->handler->where('user_id', $user_id)->delete(); + return true; + } + +} diff --git a/application/common/library/token/driver/Redis.php b/application/common/library/token/driver/Redis.php new file mode 100644 index 00000000..877da4c5 --- /dev/null +++ b/application/common/library/token/driver/Redis.php @@ -0,0 +1,166 @@ + '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'select' => 0, + 'timeout' => 0, + 'expire' => 0, + 'persistent' => false, + 'userprefix' => 'up:', + 'tokenprefix' => 'tp:', + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @throws \BadFunctionCallException + * @access public + */ + public function __construct($options = []) + { + if (!extension_loaded('redis')) { + throw new \BadFunctionCallException('not support: redis'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $this->handler = new \Redis; + if ($this->options['persistent']) { + $this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']); + } else { + $this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']); + } + + if ('' != $this->options['password']) { + $this->handler->auth($this->options['password']); + } + + if (0 != $this->options['select']) { + $this->handler->select($this->options['select']); + } + } + + /** + * 获取加密后的Token + * @param string $token Token标识 + * @return string + */ + protected function getEncryptedToken($token) + { + $config = \think\Config::get('token'); + return $this->options['tokenprefix'] . hash_hmac($config['hashalgo'], $token, $config['key']); + } + + /** + * 获取会员的key + * @param $user_id + * @return string + */ + protected function getUserKey($user_id) + { + return $this->options['userprefix'] . $user_id; + } + + /** + * 存储Token + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 + * @return bool + */ + public function set($token, $user_id, $expire = 0) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + $key = $this->getEncryptedToken($token); + if ($expire) { + $result = $this->handler->setex($key, $expire, $user_id); + } else { + $result = $this->handler->set($key, $user_id); + } + //写入会员关联的token + $this->handler->sAdd($this->getUserKey($user_id), $key); + return $result; + } + + /** + * 获取Token内的信息 + * @param string $token + * @return array + */ + public function get($token) + { + $key = $this->getEncryptedToken($token); + $value = $this->handler->get($key); + if (is_null($value) || false === $value) { + return []; + } + //获取有效期 + $expire = $this->handler->ttl($key); + $expire = $expire < 0 ? 365 * 86400 : $expire; + $expiretime = time() + $expire; + $result = ['token' => $token, 'user_id' => $value, 'expiretime' => $expiretime, 'expired_in' => $expire]; + + return $result; + } + + /** + * 判断Token是否可用 + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean + */ + public function check($token, $user_id) + { + $data = self::get($token); + return $data && $data['user_id'] == $user_id ? true : false; + } + + /** + * 删除Token + * @param string $token + * @return boolean + */ + public function delete($token) + { + $data = $this->get($token); + if ($data) { + $key = $this->getEncryptedToken($token); + $user_id = $data['user_id']; + $this->handler->del($key); + $this->handler->sRem($this->getUserKey($user_id), $key); + } + return true; + + } + + /** + * 删除指定用户的所有Token + * @param int $user_id + * @return boolean + */ + public function clear($user_id) + { + $keys = $this->handler->sMembers($this->getUserKey($user_id)); + $this->handler->del($this->getUserKey($user_id)); + $this->handler->del($keys); + return true; + } + +} diff --git a/application/common/model/Token.php b/application/common/model/Token.php deleted file mode 100644 index 978d362f..00000000 --- a/application/common/model/Token.php +++ /dev/null @@ -1,36 +0,0 @@ - true ], + // +---------------------------------------------------------------------- + // | Token设置 + // +---------------------------------------------------------------------- + 'token' => [ + // 驱动方式 + 'type' => 'Redis', + // 缓存前缀 + 'key' => 'i3d6o32wo8fvs1fvdpwens', + // 加密方式 + 'hashalgo' => 'ripemd160', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, + ], //FastAdmin配置 'fastadmin' => [ + //是否开启前台会员中心 + 'usercenter' => true, //登录验证码 'login_captcha' => true, //是否同一账号同一时间只能在一个地方登录 @@ -253,7 +268,8 @@ return [ //自动检测更新 'checkupdate' => false, //版本号 - 'version' => '1.0.0.20180327_beta', + 'version' => '1.0.0.20180401_beta', + //API接口地址 'api_url' => 'https://api.fastadmin.net', ], ]; diff --git a/application/index/controller/Index.php b/application/index/controller/Index.php index 78f23599..ba5aaf4d 100755 --- a/application/index/controller/Index.php +++ b/application/index/controller/Index.php @@ -3,6 +3,7 @@ namespace app\index\controller; use app\common\controller\Frontend; +use app\common\library\Token; class Index extends Frontend { diff --git a/application/index/controller/User.php b/application/index/controller/User.php index 73e66abc..04ed25bd 100644 --- a/application/index/controller/User.php +++ b/application/index/controller/User.php @@ -3,6 +3,7 @@ namespace app\index\controller; use app\common\controller\Frontend; +use think\Config; use think\Cookie; use think\Hook; use think\Session; @@ -23,27 +24,30 @@ class User extends Frontend parent::_initialize(); $auth = $this->auth; + if (!Config::get('fastadmin.usercenter')) { + $this->error(__('User center already closed')); + } + $ucenter = get_addon_info('ucenter'); - if ($ucenter && $ucenter['state']) - { + if ($ucenter && $ucenter['state']) { include ADDON_PATH . 'ucenter' . DS . 'uc.php'; } //监听注册登录注销的事件 - Hook::add('user_login_successed', function($user) use($auth) { + Hook::add('user_login_successed', function ($user) use ($auth) { $expire = input('post.keeplogin') ? 30 * 86400 : 0; Cookie::set('uid', $user->id, $expire); Cookie::set('token', $auth->getToken(), $expire); }); - Hook::add('user_register_successed', function($user) use($auth) { + Hook::add('user_register_successed', function ($user) use ($auth) { Cookie::set('uid', $user->id); Cookie::set('token', $auth->getToken()); }); - Hook::add('user_delete_successed', function($user) use($auth) { + Hook::add('user_delete_successed', function ($user) use ($auth) { Cookie::delete('uid'); Cookie::delete('token'); }); - Hook::add('user_logout_successed', function($user) use($auth) { + Hook::add('user_logout_successed', function ($user) use ($auth) { Cookie::delete('uid'); Cookie::delete('token'); }); @@ -66,8 +70,7 @@ class User extends Frontend $url = $this->request->request('url', url('user/index')); if ($this->auth->id) $this->success(__('You\'ve logged in, do not login again'), $url); - if ($this->request->isPost()) - { + if ($this->request->isPost()) { $username = $this->request->post('username'); $password = $this->request->post('password'); $email = $this->request->post('email'); @@ -103,23 +106,18 @@ class User extends Frontend ]; $validate = new Validate($rule, $msg); $result = $validate->check($data); - if (!$result) - { + if (!$result) { $this->error(__($validate->getError())); } - if ($this->auth->register($username, $password, $email, $mobile)) - { + if ($this->auth->register($username, $password, $email, $mobile)) { $synchtml = ''; ////////////////同步到Ucenter//////////////// - if (defined('UC_STATUS') && UC_STATUS) - { + if (defined('UC_STATUS') && UC_STATUS) { $uc = new \addons\ucenter\library\client\Client(); $synchtml = $uc->uc_user_synregister($this->auth->id, $password); } $this->success(__('Sign up successful') . $synchtml, $url); - } - else - { + } else { $this->error($this->auth->getError()); } } @@ -136,11 +134,10 @@ class User extends Frontend $url = $this->request->request('url', url('user/index')); if ($this->auth->id) $this->success(__('You\'ve logged in, do not login again'), $url); - if ($this->request->isPost()) - { + if ($this->request->isPost()) { $account = $this->request->post('account'); $password = $this->request->post('password'); - $keeplogin = (int) $this->request->post('keeplogin'); + $keeplogin = (int)$this->request->post('keeplogin'); $token = $this->request->post('__token__'); $rule = [ 'account' => 'require|length:3,50', @@ -161,24 +158,19 @@ class User extends Frontend ]; $validate = new Validate($rule, $msg); $result = $validate->check($data); - if (!$result) - { + if (!$result) { $this->error(__($validate->getError())); return FALSE; } - if ($this->auth->login($account, $password)) - { + if ($this->auth->login($account, $password)) { $synchtml = ''; ////////////////同步到Ucenter//////////////// - if (defined('UC_STATUS') && UC_STATUS) - { + if (defined('UC_STATUS') && UC_STATUS) { $uc = new \addons\ucenter\library\client\Client(); $synchtml = $uc->uc_user_synlogin($this->auth->id); } $this->success(__('Logged in successful') . $synchtml, $url); - } - else - { + } else { $this->error($this->auth->getError()); } } @@ -195,8 +187,7 @@ class User extends Frontend $this->auth->logout(); $synchtml = ''; ////////////////同步到Ucenter//////////////// - if (defined('UC_STATUS') && UC_STATUS) - { + if (defined('UC_STATUS') && UC_STATUS) { $uc = new \addons\ucenter\library\client\Client(); $synchtml = $uc->uc_user_synlogout(); } @@ -217,8 +208,7 @@ class User extends Frontend */ public function changepwd() { - if ($this->request->isPost()) - { + if ($this->request->isPost()) { $oldpassword = $this->request->post("oldpassword"); $newpassword = $this->request->post("newpassword"); $renewpassword = $this->request->post("renewpassword"); @@ -245,26 +235,21 @@ class User extends Frontend ]; $validate = new Validate($rule, $msg, $field); $result = $validate->check($data); - if (!$result) - { + if (!$result) { $this->error(__($validate->getError())); return FALSE; } $ret = $this->auth->changepwd($newpassword, $oldpassword); - if ($ret) - { + if ($ret) { $synchtml = ''; ////////////////同步到Ucenter//////////////// - if (defined('UC_STATUS') && UC_STATUS) - { + if (defined('UC_STATUS') && UC_STATUS) { $uc = new \addons\ucenter\library\client\Client(); $synchtml = $uc->uc_user_synlogout(); } $this->success(__('Reset password successful') . $synchtml, url('user/login')); - } - else - { + } else { $this->error($this->auth->getError()); } } diff --git a/application/index/lang/zh-cn.php b/application/index/lang/zh-cn.php index e2e141f2..9c5c85c3 100755 --- a/application/index/lang/zh-cn.php +++ b/application/index/lang/zh-cn.php @@ -81,6 +81,7 @@ return [ 'Github' => 'Github', 'QQ group' => 'QQ群', 'Go to Dashboard' => '登录后台', + 'Go to Member center' => '会员中心', 'Contribution' => '为FastAdmin贡献代码!', 'Copyrights' => '版权所有', 'Responsive' => '响应式开发', diff --git a/application/index/lang/zh-cn/user.php b/application/index/lang/zh-cn/user.php index 0f1fcf08..1ca3c238 100755 --- a/application/index/lang/zh-cn/user.php +++ b/application/index/lang/zh-cn/user.php @@ -59,6 +59,7 @@ return [ 'Sign up successful' => '注册成功', 'Logged in successful' => '登录成功', 'Logout successful' => '注销成功', + 'User center already closed' => '会员中心已经关闭', 'Operation failed' => '操作失败', 'Invalid parameters' => '参数不正确', 'Change password failure' => '修改密码失败', diff --git a/application/index/view/index/index.html b/application/index/view/index/index.html index 0e4571fb..0ff5b96d 100755 --- a/application/index/view/index/index.html +++ b/application/index/view/index/index.html @@ -62,7 +62,8 @@

FastAdmin

{:__('The fastest framework based on ThinkPHP5 and Bootstrap')}

- {:__('Go to Dashboard')} + {:__('Go to Dashboard')} + {:__('Go to Member center')}
diff --git a/public/assets/js/require-backend.min.js b/public/assets/js/require-backend.min.js index 1f5d6107..8896312c 100644 --- a/public/assets/js/require-backend.min.js +++ b/public/assets/js/require-backend.min.js @@ -11148,6 +11148,7 @@ define('validator',['validator-core', 'validator-lang'], function (Validator, un define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, Upload, Validator) { var Form = { config: { + fieldlisttpl: '
' }, events: { validator: function (form, success, error, submit) { @@ -11173,7 +11174,7 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef }, target: function (input) { var $formitem = $(input).closest('.form-group'), - $msgbox = $formitem.find('span.msg-box'); + $msgbox = $formitem.find('span.msg-box'); if (!$msgbox.length) { return []; } @@ -11269,7 +11270,8 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef citypicker: function (form) { //绑定城市远程插件 if ($("[data-toggle='city-picker']", form).size() > 0) { - require(['citypicker'], function () {}); + require(['citypicker'], function () { + }); } }, datetimepicker: function (form) { @@ -11387,27 +11389,90 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef } }, fieldlist: function (form) { + //绑定fieldlist if ($(".fieldlist", form).size() > 0) { - $(".fieldlist", form).on("click", ".append", function () { - var rel = parseInt($(this).closest("dl").attr("rel")) + 1; - var name = $(this).closest("dl").data("name"); - $(this).closest("dl").attr("rel", rel); - $('
').insertBefore($(this).parent()); - }); - $(".fieldlist", form).on("click", "dd .btn-remove", function () { - $(this).parent().remove(); - }); - //拖拽排序 - require(['dragsort'], function () { - //绑定拖动排序 + require(['dragsort', 'template'], function (undefined, Template) { + //刷新隐藏textarea的值 + var refresh = function (name) { + var data = {}; + var textarea = $("textarea[name='" + name + "']", form); + var container = textarea.closest("dl"); + var template = container.data("template"); + console.log(name, container); + $.each($("input,select", container).serializeArray(), function (i, j) { + var reg = /\[(\w+)\]\[(\w+)\]$/g; + var match = reg.exec(j.name); + if (!match) + return true; + match[1] = "x" + parseInt(match[1]); + if (typeof data[match[1]] == 'undefined') { + data[match[1]] = {}; + } + data[match[1]][match[2]] = j.value; + }); + var result = template ? [] : {}; + $.each(data, function (i, j) { + if (j) { + if (!template) { + if (j.key != '') { + result[j.key] = j.value; + } + } else { + result.push(j); + } + } + }); + textarea.val(JSON.stringify(result)); + }; + //监听文本框改变事件 + $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () { + refresh($(this).closest("dl").data("name")); + }); + //追加控制 + $(".fieldlist", form).on("click", ".btn-append", function (e, row) { + var container = $(this).closest("dl"); + var index = container.data("index"); + var name = container.data("name"); + var template = container.data("template"); + var data = container.data(); + index = index ? parseInt(index) : 0; + container.data("index", index + 1); + var row = row ? row : {}; + var vars = {index: index, name: name, data: data, row: row}; + var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars); + $(html).insertBefore($(this).closest("dd")); + $(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev()); + }); + //移除控制 + $(".fieldlist", form).on("click", "dd .btn-remove", function () { + var container = $(this).closest("dl"); + $(this).closest("dd").remove(); + refresh(container.data("name")); + }); + //拖拽排序 $("dl.fieldlist", form).dragsort({ itemSelector: 'dd', - dragSelector: ".btn-dragsort", + dragSelector: ".btn-fdragsort", dragEnd: function () { - + refresh($(this).closest("dl").data("name")); }, placeHolderTemplate: "
" }); + //渲染数据 + $(".fieldlist", form).each(function () { + var container = this; + var textarea = $("textarea[name='" + $(this).data("name") + "']", form); + if (textarea.val() == '') { + return true; + } + var template = $(this).data("template"); + $.each(JSON.parse(textarea.val()), function (i, j) { + $(".btn-append", container).trigger('click', template ? j : { + key: i, + value: j + }); + }); + }); }); } }, diff --git a/public/assets/js/require-form.js b/public/assets/js/require-form.js index 0747213b..c1714de8 100755 --- a/public/assets/js/require-form.js +++ b/public/assets/js/require-form.js @@ -1,6 +1,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, Upload, Validator) { var Form = { config: { + fieldlisttpl: '
' }, events: { validator: function (form, success, error, submit) { @@ -26,7 +27,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }, target: function (input) { var $formitem = $(input).closest('.form-group'), - $msgbox = $formitem.find('span.msg-box'); + $msgbox = $formitem.find('span.msg-box'); if (!$msgbox.length) { return []; } @@ -122,7 +123,8 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U citypicker: function (form) { //绑定城市远程插件 if ($("[data-toggle='city-picker']", form).size() > 0) { - require(['citypicker'], function () {}); + require(['citypicker'], function () { + }); } }, datetimepicker: function (form) { @@ -240,27 +242,90 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U } }, fieldlist: function (form) { + //绑定fieldlist if ($(".fieldlist", form).size() > 0) { - $(".fieldlist", form).on("click", ".append", function () { - var rel = parseInt($(this).closest("dl").attr("rel")) + 1; - var name = $(this).closest("dl").data("name"); - $(this).closest("dl").attr("rel", rel); - $('
').insertBefore($(this).parent()); - }); - $(".fieldlist", form).on("click", "dd .btn-remove", function () { - $(this).parent().remove(); - }); - //拖拽排序 - require(['dragsort'], function () { - //绑定拖动排序 + require(['dragsort', 'template'], function (undefined, Template) { + //刷新隐藏textarea的值 + var refresh = function (name) { + var data = {}; + var textarea = $("textarea[name='" + name + "']", form); + var container = textarea.closest("dl"); + var template = container.data("template"); + console.log(name, container); + $.each($("input,select", container).serializeArray(), function (i, j) { + var reg = /\[(\w+)\]\[(\w+)\]$/g; + var match = reg.exec(j.name); + if (!match) + return true; + match[1] = "x" + parseInt(match[1]); + if (typeof data[match[1]] == 'undefined') { + data[match[1]] = {}; + } + data[match[1]][match[2]] = j.value; + }); + var result = template ? [] : {}; + $.each(data, function (i, j) { + if (j) { + if (!template) { + if (j.key != '') { + result[j.key] = j.value; + } + } else { + result.push(j); + } + } + }); + textarea.val(JSON.stringify(result)); + }; + //监听文本框改变事件 + $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () { + refresh($(this).closest("dl").data("name")); + }); + //追加控制 + $(".fieldlist", form).on("click", ".btn-append", function (e, row) { + var container = $(this).closest("dl"); + var index = container.data("index"); + var name = container.data("name"); + var template = container.data("template"); + var data = container.data(); + index = index ? parseInt(index) : 0; + container.data("index", index + 1); + var row = row ? row : {}; + var vars = {index: index, name: name, data: data, row: row}; + var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars); + $(html).insertBefore($(this).closest("dd")); + $(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev()); + }); + //移除控制 + $(".fieldlist", form).on("click", "dd .btn-remove", function () { + var container = $(this).closest("dl"); + $(this).closest("dd").remove(); + refresh(container.data("name")); + }); + //拖拽排序 $("dl.fieldlist", form).dragsort({ itemSelector: 'dd', - dragSelector: ".btn-dragsort", + dragSelector: ".btn-fdragsort", dragEnd: function () { - + refresh($(this).closest("dl").data("name")); }, placeHolderTemplate: "
" }); + //渲染数据 + $(".fieldlist", form).each(function () { + var container = this; + var textarea = $("textarea[name='" + $(this).data("name") + "']", form); + if (textarea.val() == '') { + return true; + } + var template = $(this).data("template"); + $.each(JSON.parse(textarea.val()), function (i, j) { + $(".btn-append", container).trigger('click', template ? j : { + key: i, + value: j + }); + }); + }); }); } }, diff --git a/public/install.php b/public/install.php index 971c9682..c407b0e4 100644 --- a/public/install.php +++ b/public/install.php @@ -4,7 +4,7 @@ * * 安装完成后建议删除此文件 * @author Karson - * @website http://www.fastadmin.net + * @website https://www.fastadmin.net */ // error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE); // ini_set('display_errors', '1'); @@ -41,9 +41,9 @@ $sitename = "FastAdmin"; $link = array( 'qqun' => "https://jq.qq.com/?_wv=1027&k=487PNBb", 'gitee' => 'https://gitee.com/karson/fastadmin/attach_files', - 'home' => 'http://www.fastadmin.net?ref=install', - 'forum' => 'http://forum.fastadmin.net?ref=install', - 'doc' => 'http://doc.fastadmin.net?ref=install', + 'home' => 'https://www.fastadmin.net?ref=install', + 'forum' => 'https://forum.fastadmin.net?ref=install', + 'doc' => 'https://doc.fastadmin.net?ref=install', ); // 检测目录是否存在 @@ -77,7 +77,7 @@ else if (!extension_loaded("PDO")) } else if (!is_really_writable($dbConfigFile)) { - $errInfo = '当前权限不足,无法写入配置文件application/database.php
点击查看解决办法'; + $errInfo = '当前权限不足,无法写入配置文件application/database.php
点击查看解决办法'; } else { @@ -86,7 +86,7 @@ else { if (!is_dir(ROOT_PATH . $v)) { - $errInfo = '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装,立即前往下载'; + $errInfo = '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装,立即前往下载'; break; } }