修复selectpage树状显示的bug

修复后台表格图片点击弹出新窗口的BUG
优化后台站点名称、用户昵称头像的显示渲染
优化后台宽度自适应
增强后台管理员安全鉴权
pull/142/head
Karson 2019-09-25 20:58:03 +08:00
parent ebf38b470c
commit 678be2f3a9
21 changed files with 783 additions and 735 deletions

View File

@ -23,6 +23,7 @@ CREATE TABLE `fa_admin` (
`email` varchar(100) NOT NULL DEFAULT '' COMMENT '电子邮箱',
`loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数',
`logintime` int(10) DEFAULT NULL COMMENT '登录时间',
`loginip` varchar(50) DEFAULT NULL COMMENT '登录IP',
`createtime` int(10) DEFAULT NULL COMMENT '创建时间',
`updatetime` int(10) DEFAULT NULL COMMENT '更新时间',
`token` varchar(59) NOT NULL DEFAULT '' COMMENT 'Session标识',

View File

@ -6,6 +6,7 @@ use app\common\controller\Backend;
use app\common\library\Email;
use app\common\model\Config as ConfigModel;
use think\Exception;
use think\Validate;
/**
* 系统配置
@ -50,8 +51,8 @@ class Config extends Backend
if (in_array($value['type'], ['select', 'selects', 'checkbox', 'radio'])) {
$value['value'] = explode(',', $value['value']);
}
$value['content'] = json_decode($value['content'], TRUE);
$value['tip'] = htmlspecialchars($value['tip']);
$value['content'] = json_decode($value['content'], true);
$value['tip'] = htmlspecialchars($value['tip']);
$siteList[$v['group']]['list'][] = $value;
}
$index = 0;
@ -106,7 +107,7 @@ class Config extends Backend
* 编辑
* @param null $ids
*/
public function edit($ids = NULL)
public function edit($ids = null)
{
if ($this->request->isPost()) {
$row = $this->request->post("row/a");
@ -136,11 +137,15 @@ class Config extends Backend
}
}
/**
* 删除
* @param string $ids
*/
public function del($ids = "")
{
$name = $this->request->request('name');
$name = $this->request->post('name');
$config = ConfigModel::getByName($name);
if ($config) {
if ($name && $config) {
try {
$config->delete();
$this->refreshFile();
@ -160,17 +165,19 @@ class Config extends Backend
{
$config = [];
foreach ($this->model->all() as $k => $v) {
$value = $v->toArray();
if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) {
$value['value'] = explode(',', $value['value']);
}
if ($value['type'] == 'array') {
$value['value'] = (array)json_decode($value['value'], TRUE);
$value['value'] = (array)json_decode($value['value'], true);
}
$config[$value['name']] = $value['value'];
}
file_put_contents(APP_PATH . 'extra' . DS . 'site.php', '<?php' . "\n\nreturn " . var_export($config, true) . ";");
file_put_contents(
APP_PATH . 'extra' . DS . 'site.php',
'<?php' . "\n\nreturn " . var_export($config, true) . ";"
);
}
/**
@ -181,7 +188,6 @@ class Config extends Backend
{
$params = $this->request->post("row/a");
if ($params) {
$config = $this->model->get($params);
if (!$config) {
return $this->success();
@ -200,19 +206,25 @@ class Config extends Backend
public function emailtest()
{
$row = $this->request->post('row/a');
\think\Config::set('site', array_merge(\think\Config::get('site'), $row));
$receiver = $this->request->request("receiver");
$email = new Email;
$result = $email
->to($receiver)
->subject(__("This is a test mail"))
->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . __('This is a test mail content') . '</div>')
->send();
if ($result) {
$this->success();
$receiver = $this->request->post("receiver");
if ($receiver) {
if (!Validate::is($receiver, "email")) {
$this->error(__('Please input correct email'));
}
\think\Config::set('site', array_merge(\think\Config::get('site'), $row));
$email = new Email;
$result = $email
->to($receiver)
->subject(__("This is a test mail"))
->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . __('This is a test mail content') . '</div>')
->send();
if ($result) {
$this->success();
} else {
$this->error($email->getError());
}
} else {
$this->error($email->getError());
return $this->error(__('Invalid parameters'));
}
}
}

View File

@ -22,23 +22,22 @@ class Profile extends Backend
{
//设置过滤方法
$this->request->filter(['strip_tags']);
if ($this->request->isAjax())
{
if ($this->request->isAjax()) {
$model = model('AdminLog');
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$total = $model
->where($where)
->where('admin_id', $this->auth->id)
->order($sort, $order)
->count();
->where($where)
->where('admin_id', $this->auth->id)
->order($sort, $order)
->count();
$list = $model
->where($where)
->where('admin_id', $this->auth->id)
->order($sort, $order)
->limit($offset, $limit)
->select();
->where($where)
->where('admin_id', $this->auth->id)
->order($sort, $order)
->limit($offset, $limit)
->select();
$result = array("total" => $total, "rows" => $list);
@ -52,18 +51,18 @@ class Profile extends Backend
*/
public function update()
{
if ($this->request->isPost())
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
$params = array_filter(array_intersect_key($params, array_flip(array('email', 'nickname', 'password', 'avatar'))));
$params = array_filter(array_intersect_key(
$params,
array_flip(array('email', 'nickname', 'password', 'avatar'))
));
unset($v);
if (isset($params['password']))
{
if (isset($params['password'])) {
$params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']);
}
if ($params)
{
if ($params) {
$admin = Admin::get($this->auth->id);
$admin->save($params);
//因为个人资料面板读取的Session显示修改自己资料后同时更新Session
@ -74,5 +73,4 @@ class Profile extends Backend
}
return;
}
}

View File

@ -44,5 +44,5 @@ return [
'Timezone' => '时区',
'Language' => '语言',
'View more' => '查看更多',
'Security tips' => '<i class="fa fa-warning"></i> 安全提示:你正在使用默认的后台登录入口,为了你的网站安全,建议你修改后台登录入口,<a href="https://forum.fastadmin.net/thread/7640" target="_blank">点击查看修改方法</a>',
'Security tips' => '<i class="fa fa-warning"></i> 安全提示:你正在使用默认的后台登录入口,为了你的网站安全,强烈建议你修改后台登录入口,<a href="https://forum.fastadmin.net/thread/7640" target="_blank">点击查看修改方法</a>',
];

View File

@ -57,4 +57,5 @@ return [
'This is a test mail content' => '这是一封来自FastAdmin校验邮件,用于校验邮件配置是否正常!',
'This is a test mail' => '这是一封来自FastAdmin的邮件',
'Please input your email' => '请输入测试接收者邮箱',
'Please input correct email' => '请输入正确的邮箱地址',
];

View File

@ -30,9 +30,9 @@ class Auth extends \fast\Auth
/**
* 管理员登录
*
* @param string $username 用户名
* @param string $password 密码
* @param int $keeptime 有效时长
* @param string $username 用户名
* @param string $password 密码
* @param int $keeptime 有效时长
* @return boolean
*/
public function login($username, $password, $keeptime = 0)
@ -58,6 +58,7 @@ class Auth extends \fast\Auth
}
$admin->loginfailure = 0;
$admin->logintime = time();
$admin->loginip = request()->ip(0, false);
$admin->token = Random::uuid();
$admin->save();
Session::set("admin", $admin->toArray());
@ -101,6 +102,11 @@ class Auth extends \fast\Auth
if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token)) {
return false;
}
$ip = request()->ip(0, false);
//IP有变动
if ($admin->loginip != $ip) {
return false;
}
Session::set("admin", $admin->toArray());
//刷新自动登录的时效
$this->keeplogin($keeptime);
@ -113,7 +119,7 @@ class Auth extends \fast\Auth
/**
* 刷新保持登录的Cookie
*
* @param int $keeptime
* @param int $keeptime
* @return boolean
*/
protected function keeplogin($keeptime = 0)
@ -178,6 +184,9 @@ class Auth extends \fast\Auth
return false;
}
}
if (!isset($admin['loginip']) || $admin['loginip'] != request()->ip(0, false)) {
return false;
}
$this->logined = true;
return true;
}
@ -258,10 +267,17 @@ class Auth extends \fast\Auth
foreach ($groups as $k => $v) {
$groupIds[] = $v['id'];
}
$originGroupIds = $groupIds;
foreach ($groups as $k => $v) {
if (in_array($v['pid'], $originGroupIds)) {
$groupIds = array_diff($groupIds, [$v['id']]);
unset($groups[$k]);
}
}
// 取出所有分组
$groupList = \app\admin\model\AuthGroup::where(['status' => 'normal'])->select();
$objList = [];
foreach ($groups as $K => $v) {
foreach ($groups as $k => $v) {
if ($v['rules'] === '*') {
$objList = $groupList;
break;
@ -295,7 +311,6 @@ class Auth extends \fast\Auth
field('uid,group_id')
->where('group_id', 'in', $groupIds)
->select();
foreach ($authGroupList as $k => $v) {
$childrenAdminIds[] = $v['uid'];
}
@ -340,7 +355,7 @@ class Auth extends \fast\Auth
/**
* 获取左侧和顶部菜单栏
*
* @param array $params URL对应的badge数据
* @param array $params URL对应的badge数据
* @param string $fixedPage 默认页
* @return array
*/
@ -435,18 +450,36 @@ class Auth extends \fast\Auth
$selectParentIds = $tree->getParentsIds($select_id, true);
}
foreach ($topList as $index => $item) {
$childList = Tree::instance()->getTreeMenu($item['id'], '<li class="@class" pid="@pid"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
$childList = Tree::instance()->getTreeMenu(
$item['id'],
'<li class="@class" pid="@pid"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>',
$select_id,
'',
'ul',
'class="treeview-menu"'
);
$current = in_array($item['id'], $selectParentIds);
$url = $childList ? 'javascript:;' : url($item['url']);
$addtabs = $childList || !$url ? "" : (stripos($url, "?") !== false ? "&" : "?") . "ref=addtabs";
$childList = str_replace('" pid="' . $item['id'] . '"', ' treeview ' . ($current ? '' : 'hidden') . '" pid="' . $item['id'] . '"', $childList);
$childList = str_replace(
'" pid="' . $item['id'] . '"',
' treeview ' . ($current ? '' : 'hidden') . '" pid="' . $item['id'] . '"',
$childList
);
$nav .= '<li class="' . ($current ? 'active' : '') . '"><a href="' . $url . $addtabs . '" addtabs="' . $item['id'] . '" url="' . $url . '"><i class="' . $item['icon'] . '"></i> <span>' . $item['title'] . '</span> <span class="pull-right-container"> </span></a> </li>';
$menu .= $childList;
}
} else {
// 构造菜单数据
Tree::instance()->init($ruleList);
$menu = Tree::instance()->getTreeMenu(0, '<li class="@class"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>', $select_id, '', 'ul', 'class="treeview-menu"');
$menu = Tree::instance()->getTreeMenu(
0,
'<li class="@class"><a href="@url@addtabs" addtabs="@id" url="@url" py="@py" pinyin="@pinyin"><i class="@icon"></i> <span>@title</span> <span class="pull-right-container">@caret @badge</span></a> @childlist</li>',
$select_id,
'',
'ul',
'class="treeview-menu"'
);
if ($selected) {
$nav .= '<li role="presentation" id="tab_' . $selected['id'] . '" class="' . ($referer ? '' : 'active') . '"><a href="#con_' . $selected['id'] . '" node-id="' . $selected['id'] . '" aria-controls="' . $selected['id'] . '" role="tab" data-toggle="tab"><i class="' . $selected['icon'] . ' fa-fw"></i> <span>' . $selected['title'] . '</span> </a></li>';
}

View File

@ -1,9 +1,9 @@
<!-- Logo -->
<a href="javascript:;" class="logo">
<!-- 迷你模式下Logo的大小为50X50 -->
<span class="logo-mini">{$site.name|mb_substr=0,4,'utf-8'|mb_strtoupper='utf-8'}</span>
<span class="logo-mini">{$site.name|mb_substr=0,4,'utf-8'|mb_strtoupper='utf-8'|htmlentities}</span>
<!-- 普通模式下Logo -->
<span class="logo-lg"><b>{$site.name|mb_substr=0,4,'utf-8'}</b>{$site.name|mb_substr=4,null,'utf-8'}</span>
<span class="logo-lg"><b>{$site.name|mb_substr=0,4,'utf-8'|htmlentities}</b>{$site.name|mb_substr=4,null,'utf-8'|htmlentities}</span>
</a>
<!-- 顶部通栏样式 -->
@ -72,16 +72,16 @@
<!-- 账号信息下拉框 -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="{$admin.avatar|cdnurl}" class="user-image" alt="{$admin.nickname}">
<span class="hidden-xs">{$admin.nickname}</span>
<img src="{$admin.avatar|cdnurl|htmlentities}" class="user-image" alt="{$admin.nickname|htmlentities}">
<span class="hidden-xs">{$admin.nickname|htmlentities}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="{$admin.avatar|cdnurl}" class="img-circle" alt="">
<img src="{$admin.avatar|cdnurl|htmlentities}" class="img-circle" alt="">
<p>
{$admin.nickname}
{$admin.nickname|htmlentities}
<small>{$admin.logintime|date="Y-m-d H:i:s",###}</small>
</p>
</li>

View File

@ -3,10 +3,10 @@
<!-- 管理员信息 -->
<div class="user-panel hidden-xs">
<div class="pull-left image">
<a href="general/profile" class="addtabsit"><img src="{$admin.avatar|cdnurl}" class="img-circle" /></a>
<a href="general/profile" class="addtabsit"><img src="{$admin.avatar|cdnurl|htmlentities}" class="img-circle" /></a>
</div>
<div class="pull-left info">
<p>{$admin.nickname}</p>
<p>{$admin.nickname|htmlentities}</p>
<i class="fa fa-circle text-success"></i> {:__('Online')}
</div>
</div>

View File

@ -1 +1 @@
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-backend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-backend{$Think.config.app_debug?'':'.min'}.js?v={$site.version|htmlentities}"></script>

View File

@ -46,18 +46,18 @@
<div class="panel-body">
<form id="update-form" role="form" data-toggle="validator" method="POST" action="{:url('general.profile/update')}">
<input type="hidden" id="c-avatar" name="row[avatar]" value="{$admin.avatar}"/>
<input type="hidden" id="c-avatar" name="row[avatar]" value="{$admin.avatar|htmlentities}"/>
<div class="box-body box-profile">
<div class="profile-avatar-container">
<img class="profile-user-img img-responsive img-circle plupload" src="{$admin.avatar|cdnurl}" alt="">
<img class="profile-user-img img-responsive img-circle plupload" src="{$admin.avatar|cdnurl|htmlentities}" alt="">
<div class="profile-avatar-text img-circle">{:__('Click to edit')}</div>
<button id="plupload-avatar" class="plupload" data-input-id="c-avatar"><i class="fa fa-upload"></i> {:__('Upload')}</button>
</div>
<h3 class="profile-username text-center">{$admin.username}</h3>
<h3 class="profile-username text-center">{$admin.username|htmlentities}</h3>
<p class="text-muted text-center">{$admin.email}</p>
<p class="text-muted text-center">{$admin.email|htmlentities}</p>
<div class="form-group">
<label for="username" class="control-label">{:__('Username')}:</label>
<input type="text" class="form-control" id="username" name="row[username]" value="{$admin.username|htmlentities}" disabled/>

View File

@ -490,7 +490,7 @@ class Backend extends Controller
'pid' => isset($item['pid']) ? $item['pid'] : 0
];
}
if ($istree) {
if ($istree && !$primaryvalue) {
$tree = Tree::instance();
$tree->init(collection($list)->toArray(), 'pid');
$list = $tree->getTreeList($tree->getTreeArray(0), $field);

View File

@ -1 +1 @@
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version|htmlentities}"></script>

View File

@ -61,7 +61,7 @@
<footer class="footer" style="clear:both">
<!-- FastAdmin是开源程序建议在您的网站底部保留一个FastAdmin的链接 -->
<p class="copyright">Copyright&nbsp;©&nbsp;2017-2019 Powered by <a href="https://www.fastadmin.net" target="_blank">FastAdmin</a> All Rights Reserved {$site.name} {:__('Copyrights')} <a href="http://www.miibeian.gov.cn" target="_blank">{$site.beian}</a></p>
<p class="copyright">Copyright&nbsp;©&nbsp;2017-2019 Powered by <a href="https://www.fastadmin.net" target="_blank">FastAdmin</a> All Rights Reserved {$site.name|htmlentities} {:__('Copyrights')} <a href="http://www.miibeian.gov.cn" target="_blank">{$site.beian|htmlentities}</a></p>
</footer>
{include file="common/script" /}

View File

@ -918,7 +918,7 @@ table.table-nowrap thead > tr > th {
}
}
/*平板样式*/
@media (max-width: 768px) {
@media (max-width: 767px) {
body .wrapper .main-header .logo {
background: none;
color: #fff;
@ -997,4 +997,4 @@ table.table-nowrap thead > tr > th {
.btn-switcher .text-gray {
color: #d2d6de !important;
}
/*# sourceMappingURL=../css/backend.css.map */
/*# sourceMappingURL=backend.css.map */

File diff suppressed because one or more lines are too long

View File

@ -533,7 +533,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
return '<a href="javascript:;" data-toggle="tooltip" title="' + __('Click to toggle status') + '" class="btn btn-toggle btn-' + (row.addon.state == 1 ? "disable" : "enable") + '" data-action="' + (row.addon.state == 1 ? "disable" : "enable") + '" data-name="' + row.name + '"><i class="fa ' + (row.addon.state == 0 ? 'fa-toggle-on fa-rotate-180 text-gray' : 'fa-toggle-on text-success') + ' fa-2x"></i></a>';
},
author: function (value, row, index) {
return '<a href="https://wpa.qq.com/msgrd?v=3&uin=' + row.qq + '&site=fastadmin.net&menu=yes" target="_blank" data-toggle="tooltip" title="' + __('Click to contact developer') + '" class="text-primary">' + value + '</a>';
var url = 'javascript:';
if (typeof row.homepage !== 'undefined') {
url = row.homepage;
} else if (typeof row.qq !== 'undefined') {
url = 'https://wpa.qq.com/msgrd?v=3&uin=' + row.qq + '&site=fastadmin.net&menu=yes';
}
return '<a href="' + url + '" target="_blank" data-toggle="tooltip" title="' + __('Click to contact developer') + '" class="text-primary">' + value + '</a>';
},
price: function (value, row, index) {
if (isNaN(value)) {

View File

@ -67,8 +67,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
var that = this;
Layer.prompt({title: __('Please input your email'), formType: 0}, function (value, index) {
Backend.api.ajax({
url: "general/config/emailtest?receiver=" + value,
data: $(that).closest("form").serialize()
url: "general/config/emailtest",
data: $(that).closest("form").serialize() + "&receiver=" + value
});
});
@ -79,7 +79,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
var that = this;
Layer.confirm(__('Are you sure you want to delete this item?'), {icon: 3, title:'提示'}, function (index) {
Backend.api.ajax({
url: "general/config/del?receiver=" + value,
url: "general/config/del",
data: {name: $(that).data("name")}
}, function () {
$(that).closest("tr").remove();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -434,7 +434,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
image: function (value, row, index) {
value = value ? value : '/assets/img/blank.gif';
var classname = typeof this.classname !== 'undefined' ? this.classname : 'img-sm img-center';
return '<a href="javascript:void(0)" target="_blank"><img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" /></a>';
return '<a href="javascript:"><img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" /></a>';
},
images: function (value, row, index) {
value = value === null ? '' : value.toString();
@ -443,10 +443,14 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
var html = [];
$.each(arr, function (i, value) {
value = value ? value : '/assets/img/blank.gif';
html.push('<a href="javascript:void(0)" target="_blank"><img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" /></a>');
html.push('<a href="javascript:"><img class="' + classname + '" src="' + Fast.api.cdnurl(value) + '" /></a>');
});
return html.join(' ');
},
content: function (value, row, index) {
var width = this.width != undefined ? this.width : 250;
return "<div style='white-space: nowrap; text-overflow:ellipsis; overflow: hidden; max-width:" + width + "px;'>" + value + "</div>";
},
status: function (value, row, index) {
var custom = {normal: 'success', hidden: 'gray', deleted: 'danger', locked: 'info'};
if (typeof this.custom !== 'undefined') {

View File

@ -16,6 +16,7 @@
@import url("../libs/nice-validator/dist/jquery.validator.css");
@import url("../libs/bootstrap-select/dist/css/bootstrap-select.min.css");
@import url("../libs/fastadmin-selectpage/selectpage.css");
@import url("../libs/bootstrap-slider/slider.css");
@main-bg: #f1f4f6;
@panel-intro-bg: darken(@main-bg, 3%);
@ -954,7 +955,7 @@ table.table-nowrap {
}
//样式二关闭按钮
.layui-layer-close2, .layui-layer-close2:hover {
background: url('../libs/layer/dist/theme/default/icon.png') no-repeat -149px -31px !important;
background: url('../libs/fastadmin-layer/dist/theme/default/icon.png') no-repeat -149px -31px !important;
top: -30px;
right: -30px;
&:after {
@ -1045,7 +1046,7 @@ table.table-nowrap {
}
/*平板样式*/
@media (max-width: @screen-tablet) {
@media (max-width: @screen-xs-max) {
body .wrapper .main-header .logo {
background: none;