Merge branch '1.x' into develop

# Conflicts:
#	README.md
#	application/admin/command/Install/fastadmin.sql
#	application/admin/controller/Index.php
#	application/admin/controller/auth/Adminlog.php
#	application/common.php
#	application/common/controller/Backend.php
#	application/common/library/Email.php
#	application/config.php
#	application/index/controller/User.php
#	bower.json
#	composer.json
#	extend/fast/Http.php
#	public/assets/css/backend.min.css
#	public/assets/css/frontend.min.css
#	public/assets/js/backend/addon.js
#	public/assets/js/bootstrap-table-commonsearch.js
#	public/assets/js/fast.js
#	public/assets/js/frontend/user.js
#	public/assets/js/jquery.drag.min.js
#	public/assets/js/require-backend.js
#	public/assets/js/require-backend.min.js
#	public/assets/js/require-form.js
#	public/assets/js/require-frontend.min.js
#	public/assets/js/require-table.js
pull/463/MERGE
Karson 2024-04-01 15:22:15 +08:00
commit 8caeeb8a78
71 changed files with 754 additions and 295 deletions

View File

@ -4,7 +4,7 @@ namespace app\admin\behavior;
class AdminLog
{
public function run(&$params)
public function run(&$response)
{
//只记录POST请求的日志
if (request()->isPost() && config('fastadmin.auto_record_log')) {

View File

@ -15,7 +15,6 @@ use think\exception\PDOException;
class Addon extends Command
{
protected function configure()
{
$this
@ -33,6 +32,7 @@ class Addon extends Command
protected function execute(Input $input, Output $output)
{
\think\Config::load(dirname(dirname(__FILE__)) . DS . 'config.php');
$name = $input->getOption('name') ?: '';
$action = $input->getOption('action') ?: '';
if (stripos($name, 'addons' . DS) !== false) {
@ -82,7 +82,6 @@ class Addon extends Command
$createTableSql = $result[0]['Create Table'];
}
} catch (PDOException $e) {
}
$data = [
@ -177,12 +176,12 @@ class Addon extends Command
if (!$info) {
throw new Exception(__('Addon info file data incorrect'));
}
$infoname = isset($info['name']) ? $info['name'] : '';
$infoname = $info['name'] ?? '';
if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) {
throw new Exception(__('Addon info name incorrect'));
}
$infoversion = isset($info['version']) ? $info['version'] : '';
$infoversion = $info['version'] ?? '';
if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) {
throw new Exception(__('Addon info version incorrect'));
}
@ -340,5 +339,4 @@ class Addon extends Command
{
return __DIR__ . '/Addon/stubs/' . $name . '.stub';
}
}

View File

@ -152,7 +152,7 @@ class Crud extends Command
/**
* JSON后缀
*/
protected $jsonSuffix = ['json'];
protected $jsonSuffix = ['json', 'array'];
/**
* 标签后缀
@ -466,7 +466,7 @@ class Crud extends Command
}
}
$relationTableInfo = $relationTableInfo[0];
$relationModel = isset($relationModels[$index]) ? $relationModels[$index] : '';
$relationModel = $relationModels[$index] ?? '';
list($relationNamespace, $relationName, $relationFile) = $this->getModelData($modelModuleName, $relationModel, $relationName);
@ -666,8 +666,8 @@ class Crud extends Command
//如果是关联模型
foreach ($relations as $index => &$relation) {
if ($relation['relationMode'] == 'hasone') {
$relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey;
$relationForeignKey = $relation['relationForeignKey'] ?: $table . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ?: $priKey;
if (!in_array($relationForeignKey, $relation['relationFieldList'])) {
throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']');
@ -676,8 +676,8 @@ class Crud extends Command
throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']');
}
} elseif ($relation['relationMode'] == 'belongsto') {
$relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : Loader::parseName($relation['relationName']) . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $relation['relationPriKey'];
$relationForeignKey = $relation['relationForeignKey'] ?: Loader::parseName($relation['relationName']) . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ?: $relation['relationPriKey'];
if (!in_array($relationForeignKey, $fieldArr)) {
throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationForeignKey . ']');
}
@ -685,8 +685,8 @@ class Crud extends Command
throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationPrimaryKey . ']');
}
} elseif ($relation['relationMode'] == 'hasmany') {
$relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey;
$relationForeignKey = $relation['relationForeignKey'] ?: $table . "_id";
$relationPrimaryKey = $relation['relationPrimaryKey'] ?: $priKey;
if (!in_array($relationForeignKey, $relation['relationFieldList'])) {
throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']');
}
@ -879,7 +879,7 @@ class Crud extends Command
$formEditElement = Form::input('text', $fieldName, $editValue, $attrArr);
} elseif ($inputType == 'fieldlist') {
$itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']);
$templateName = !isset($itemArr['key']) && !isset($itemArr['value']) && count($itemArr) > 0 ? 'fieldlist-template' : 'fieldlist';
$templateName = !isset($itemArr['key']) && count($itemArr) > 0 ? (isset($itemArr['value']) && count($itemArr) === 1 ? 'fieldlist-array' : 'fieldlist-template') : 'fieldlist';
$itemKey = isset($itemArr['key']) ? ucfirst($itemArr['key']) : 'Key';
$itemValue = isset($itemArr['value']) ? ucfirst($itemArr['value']) : 'Value';
$theadListArr = $tbodyListArr = [];
@ -901,6 +901,12 @@ class Crud extends Command
$cssClassArr[] = 'selectpage';
$selectpageTable = substr($field, 0, strripos($field, '_'));
$selectpageField = '';
foreach ($relations as $index => $relation) {
if ($relation['relationForeignKey'] === $field) {
$selectpageTable = substr($relation['relationTableName'], strlen($prefix));
break;
}
}
$selectpageController = str_replace('_', '/', $selectpageTable);
$attrArr['data-source'] = $selectpageController . "/index";
//如果是类型表需要特殊处理下
@ -931,7 +937,6 @@ class Crud extends Command
}
}
} catch (\Exception $e) {
}
if (!$selectpageField) {
foreach ($this->fieldSelectpageMap as $m => $n) {
@ -993,7 +998,7 @@ class Crud extends Command
}
if (!$fields || in_array($field, explode(',', $fields))) {
//构造JS列信息
$javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr);
$javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr, $v);
}
if ($this->headingFilterField && $this->headingFilterField == $field && $itemArr) {
$headingHtml = $this->getReplacedStub('html/heading-html', ['field' => $field, 'fieldName' => Loader::parseName($field, 1, false)]);
@ -1048,7 +1053,7 @@ class Crud extends Command
//过滤text类型字段
if ($v['DATA_TYPE'] != 'text') {
//构造JS列信息
$javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE']);
$javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE'], '', [], $v);
}
}
}
@ -1537,7 +1542,7 @@ EOD;
{
$itemArr = [];
$comment = str_replace('', ',', $comment);
if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) {
if (stripos($comment, ':') !== false && stripos($comment, '=') !== false) {
list($fieldLang, $item) = explode(':', $comment);
$itemArr = [];
foreach (explode(',', $item) as $k => $v) {
@ -1699,9 +1704,10 @@ EOD;
* @param string $datatype
* @param string $extend
* @param array $itemArr
* @param array $fieldConfig
* @return string
*/
protected function getJsColumn($field, $datatype = '', $extend = '', $itemArr = [])
protected function getJsColumn($field, $datatype = '', $extend = '', $itemArr = [], $fieldConfig = [])
{
$lang = mb_ucfirst($field);
$formatter = '';
@ -1739,7 +1745,7 @@ EOD;
$noSearchFiles = ['file$', 'files$', 'image$', 'images$', '^weigh$'];
if (preg_match("/" . implode('|', $noSearchFiles) . "/i", $field)) {
$html .= ", operate: false";
} else if (in_array($datatype, ['varchar'])) {
} elseif (in_array($datatype, ['varchar'])) {
$html .= ", operate: 'LIKE'";
}
@ -1751,6 +1757,10 @@ EOD;
if (in_array($datatype, ['set'])) {
$html .= ", operate:'FIND_IN_SET'";
}
if (isset($fieldConfig['CHARACTER_MAXIMUM_LENGTH']) && $fieldConfig['CHARACTER_MAXIMUM_LENGTH'] >= 255 && in_array($datatype, ['varchar']) && !$formatter) {
$formatter = 'content';
$html .= ", table: table, class: 'autocontent'";
}
if (in_array($formatter, ['image', 'images'])) {
$html .= ", events: Table.api.events.image";
}

View File

@ -5,7 +5,6 @@
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -5,7 +5,6 @@
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,21 @@
<dl class="list-unstyled fieldlist" data-name="{%fieldName%}" data-template="{%fieldName%}tpl">
<dd>
<ins>{:__('{%itemValue%}')}</ins>
</dd>
<dd>
<ins><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></ins>
</dd>
</dl>
<textarea name="{%fieldName%}" class="form-control hide" cols="30" rows="5">{%fieldValue%}</textarea>
<script id="{%fieldName%}tpl" type="text/html">
<dd class="form-inline">
<ins><input type="text" name="<%=name%>[<%=index%>][value]" class="form-control" size="15" value="<%=row%>"/></ins>
<ins>
<span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
<span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
</ins>
</dd>
</script>

View File

@ -336,16 +336,16 @@ INSERT INTO `fa_config` VALUES (4, 'version', 'basic', 'Version', '如果静态
INSERT INTO `fa_config` VALUES (5, 'timezone', 'basic', 'Timezone', '', 'string', '', 'Asia/Shanghai', '', 'required', '', '');
INSERT INTO `fa_config` VALUES (6, 'forbiddenip', 'basic', 'Forbidden ip', '一行一条记录', 'text', '', '', '', '', '', '');
INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array', '', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', '', '');
INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '尽量输入左侧菜单栏存在的链接', 'string', '', 'dashboard', '', 'required', '', '');
INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '输入左侧菜单栏存在的链接', 'string', '', 'dashboard', '', 'required', '', '');
INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '', '');
INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '', '');
INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '', '1', '[\"请选择\",\"SMTP\"]', '', '', '');
INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', '', 'smtp.qq.com', '', '', '', '');
INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '', '465', '', '', '', '');
INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '', '10000', '', '', '', '');
INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码或授权码)', 'password', '', 'password', '', '', '', '');
INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '', '', '', '', '', '');
INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码或授权码)', 'password', '', '', '', '', '', '');
INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', 'SMTP验证方式[推荐SSL]', 'select', '', '2', '[\"无\",\"TLS\",\"SSL\"]', '', '', '');
INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '', '10000@qq.com', '', '', '', '');
INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '', '', '', '', '', '');
INSERT INTO `fa_config` VALUES (18, 'attachmentcategory', 'dictionary', 'Attachment category', '', 'array', '', '{\"category1\":\"Category1\",\"category2\":\"Category2\",\"custom\":\"Custom\"}', '', '', '', '');
COMMIT;
@ -399,7 +399,8 @@ CREATE TABLE `fa_test` (
`keywords` varchar(255) DEFAULT '' COMMENT '关键字',
`description` varchar(255) DEFAULT '' COMMENT '描述',
`city` varchar(100) DEFAULT '' COMMENT '省市',
`json` varchar(255) DEFAULT NULL COMMENT '配置:key=名称,value=值',
`array` varchar(255) DEFAULT '' COMMENT '数组:value=值',
`json` varchar(255) DEFAULT '' COMMENT '配置:key=名称,value=值',
`multiplejson` varchar(1500) DEFAULT '' COMMENT '二维数组:title=标题,intro=介绍,author=作者,age=年龄',
`price` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '价格',
`views` int(10) unsigned DEFAULT '0' COMMENT '点击',
@ -423,7 +424,7 @@ CREATE TABLE `fa_test` (
-- Records of fa_test
-- ----------------------------
BEGIN;
INSERT INTO `fa_test` VALUES (1, 1, 1, 12, '12,13', '互联网,计算机', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '<p>我是测试内容</p>', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '我是一篇测试文章描述,内容过多时将自动隐藏', '广西壮族自治区/百色市/平果县', '{\"a\":\"1\",\"b\":\"2\"}', '[{\"title\":\"标题一\",\"intro\":\"介绍一\",\"author\":\"小明\",\"age\":\"21\"}]', 0.00, 0, '2020-10-01 00:00:00 - 2021-10-31 23:59:59', '2017-07-10', '2017-07-10 18:24:45', 2017, '18:24:45', 1491635035, 1491635035, 1491635035, NULL, 0, 1, 'normal', '1');
INSERT INTO `fa_test` VALUES (1, 1, 1, 12, '12,13', '互联网,计算机', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '<p>我是测试内容</p>', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '我是一篇测试文章描述,内容过多时将自动隐藏', '广西壮族自治区/百色市/平果县', '[\"a\",\"b\"]', '{\"a\":\"1\",\"b\":\"2\"}', '[{\"title\":\"标题一\",\"intro\":\"介绍一\",\"author\":\"小明\",\"age\":\"21\"}]', 0.00, 0, '2020-10-01 00:00:00 - 2021-10-31 23:59:59', '2017-07-10', '2017-07-10 18:24:45', 2017, '18:24:45', 1491635035, 1491635035, 1491635035, NULL, 0, 1, 'normal', '1');
COMMIT;
-- ----------------------------
@ -468,7 +469,7 @@ CREATE TABLE `fa_user` (
-- Records of fa_user
-- ----------------------------
BEGIN;
INSERT INTO `fa_user` VALUES (1, 1, 'admin', 'admin', '', '', 'admin@163.com', '13888888888', '', 0, 0, '2017-04-08', '', 0, 0, 1, 1, 1491635035, 1491635035, '127.0.0.1', 0, '127.0.0.1', 1491635035, 0, 1491635035, '', 'normal','');
INSERT INTO `fa_user` VALUES (1, 1, 'admin', 'admin', '', '', 'admin@163.com', '13000000000', '', 0, 0, '2017-04-08', '', 0, 0, 1, 1, 1491635035, 1491635035, '127.0.0.1', 0, '127.0.0.1', 1491635035, 0, 1491635035, '', 'normal','');
COMMIT;
-- ----------------------------

View File

@ -95,19 +95,24 @@ class Addon extends Backend
}
$tips = [];
$groupList = [];
$ungroupList = [];
foreach ($config as $index => &$item) {
//如果有设置分组
if (isset($item['group']) && $item['group']) {
if (!in_array($item['group'], $groupList)) {
$groupList["custom" . (count($groupList) + 1)] = $item['group'];
}
} elseif ($item['name'] != '__tips__') {
$ungroupList[] = $item['name'];
}
if ($item['name'] == '__tips__') {
$tips = $item;
unset($config[$index]);
}
}
$groupList['other'] = '其它';
if ($ungroupList) {
$groupList['other'] = '其它';
}
$this->view->assign("groupList", $groupList);
$this->view->assign("addon", ['info' => $info, 'config' => $config, 'tips' => $tips]);
$configFile = ADDON_PATH . $name . DS . 'config.html';
@ -230,6 +235,7 @@ class Addon extends Backend
$uid = $this->request->post("uid");
$token = $this->request->post("token");
$faversion = $this->request->post("faversion");
$force = $this->request->post("force");
if (!$uid || !$token) {
throw new Exception(__('Please login and try to install'));
}
@ -238,7 +244,7 @@ class Addon extends Backend
'token' => $token,
'faversion' => $faversion
];
$info = Service::local($file, $extend);
$info = Service::local($file, $extend, $force);
} catch (AddonException $e) {
$this->result($e->getData(), $e->getCode(), __($e->getMessage()));
} catch (Exception $e) {
@ -441,8 +447,11 @@ class Addon extends Backend
} catch (\Exception $e) {
}
$rows = isset($json['rows']) ? $json['rows'] : [];
$rows = $json['rows'] ?? [];
foreach ($rows as $index => $row) {
if (!isset($row['name'])) {
continue;
}
$onlineaddons[$row['name']] = $row;
}
Cache::set("onlineaddons", $onlineaddons, 600);

View File

@ -44,7 +44,6 @@ class Index extends Backend
'dashboard' => 'hot',
'addon' => ['new', 'red', 'badge'],
'auth/rule' => __('Menu'),
'general' => ['new', 'purple'],
], $this->view->site['fixedpage']);
$action = $this->request->request('action');
if ($this->request->isPost()) {
@ -66,7 +65,8 @@ class Index extends Backend
*/
public function login()
{
$url = $this->request->get('url', 'index/index', 'url_clean');
$url = $this->request->get('url', '', 'url_clean');
$url = $url ?: 'index/index';
if ($this->auth->isLogin()) {
$this->success(__("You've logged in, do not login again"), $url);
}
@ -74,7 +74,7 @@ class Index extends Backend
$keeyloginhours = 24;
if ($this->request->isPost()) {
$username = $this->request->post('username');
$password = $this->request->post('password');
$password = $this->request->post('password', '', null);
$keeplogin = $this->request->post('keeplogin');
$token = $this->request->post('__token__');
$rule = [

View File

@ -53,6 +53,7 @@ class Adminlog extends Backend
$query->where('admin_id', 'in', $childrenAdminIds);
}
})
->field('content,useragent', true)
->order($sort, $order)
->paginate($limit);

View File

@ -74,6 +74,7 @@ class Profile extends Backend
$admin->save($params);
//因为个人资料面板读取的Session显示修改自己资料后同时更新Session
Session::set("admin", $admin->toArray());
Session::set("admin.safecode", $this->auth->getEncryptSafecode($admin));
$this->success();
}
$this->error();

View File

@ -24,7 +24,7 @@ class User extends Backend
public function _initialize()
{
parent::_initialize();
$this->model = model('User');
$this->model = new \app\admin\model\User;
}
/**

View File

@ -4,6 +4,8 @@ return [
'User id' => '会员ID',
'Username' => '用户名',
'Nickname' => '昵称',
'Mobile' => '手机',
'Email' => '邮箱',
'Password' => '密码',
'Mobile' => '手机号',
'Sign up' => '注 册',

View File

@ -14,11 +14,8 @@ return [
'Refresh addon cache' => '刷新插件缓存',
'Userinfo' => '会员信息',
'Reload authorization' => '刷新授权',
'Online store' => '在线商店',
'Local addon' => '本地插件',
'Conflict tips' => '此插件中发现和现有系统中部分文件发现冲突!以下文件将会被影响,请备份好相关文件后再继续操作',
'Login tips' => '此处登录账号为<a href="https://www.fastadmin.net" target="_blank">FastAdmin官网账号</a>',
'Logined tips' => '你好!%s<br />当前你已经登录,将同步保存你的购买记录',
'Pay tips' => '扫码支付后如果仍然无法安装,请不要重复支付,请稍后再重试安装!',
'Pay successful tips' => '购买成功!请点击继续安装按钮完成安装!',
'Pay click tips' => '请点击这里在新窗口中进行支付!',
@ -26,8 +23,7 @@ return [
'Upgrade tips' => '确认升级<b>《%s》</b><p class="text-danger">1、请务必做好代码和数据库备份备份备份<br>2、升级后如出现冗余数据请根据需要移除即可!<br>3、不建议在生产环境升级请在本地完成升级测试</p>如有重要数据请备份后再操作!',
'Offline installed tips' => '安装成功!清除浏览器缓存和框架缓存后生效!',
'Online installed tips' => '安装成功!清除浏览器缓存和框架缓存后生效!',
'Not login tips' => '你当前未登录FastAdmin请登录后操作',
'Please login and try to install' => '请登录FastAdmin后再进行离线安装',
'Please login and try to install' => '请登录FastAdmin后再进行本地安装',
'Not installed tips' => '请安装后再访问插件前台页面!',
'Not enabled tips' => '插件已经禁用,请启用后再访问插件前台页面!',
'New version tips' => '发现新版本:%s 点击查看更新日志',
@ -37,6 +33,7 @@ return [
'Store not available tips' => '插件市场暂不可用,是否切换到本地插件?',
'Switch to the local' => '切换到本地插件',
'try to reload' => '重新尝试加载',
'Please disable addon first' => '请先禁用插件再进行操作',
'Please disable the add before trying to upgrade' => '请先禁用插件再进行升级',
'Please disable the add before trying to uninstall' => '请先禁用插件再进行卸载',
'Login now' => '立即登录',
@ -80,7 +77,6 @@ return [
'Enable' => '启用',
'Your username or email' => '你的手机号、用户名或邮箱',
'Your password' => '你的密码',
'Login FastAdmin' => '登录',
'Login' => '登录',
'Logout' => '退出登录',
'Register' => '注册账号',

View File

@ -339,7 +339,7 @@ class Auth extends \fast\Auth
}
}
// 取出所有分组
$groupList = \app\admin\model\AuthGroup::where(['status' => 'normal'])->select();
$groupList = \app\admin\model\AuthGroup::where($this->isSuperAdmin() ? '1=1' : ['status' => 'normal'])->select();
$objList = [];
foreach ($groups as $k => $v) {
if ($v['rules'] === '*') {
@ -371,8 +371,7 @@ class Auth extends \fast\Auth
$childrenAdminIds = [];
if (!$this->isSuperAdmin()) {
$groupIds = $this->getChildrenGroupIds(false);
$authGroupList = \app\admin\model\AuthGroupAccess::
field('uid,group_id')
$authGroupList = \app\admin\model\AuthGroupAccess::field('uid,group_id')
->where('group_id', 'in', $groupIds)
->select();
foreach ($authGroupList as $k => $v) {
@ -418,7 +417,6 @@ class Auth extends \fast\Auth
$titleArr[$pathArr[$rule['name']]] = $rule['title'];
$menuArr[$pathArr[$rule['name']]] = $rule;
}
}
ksort($menuArr);
$this->breadcrumb = $menuArr;
@ -444,9 +442,9 @@ class Auth extends \fast\Auth
foreach ($params as $k => $v) {
$url = $k;
if (is_array($v)) {
$nums = isset($v[0]) ? $v[0] : 0;
$color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = isset($v[2]) ? $v[2] : 'label';
$nums = $v[0] ?? 0;
$color = $v[1] ?? $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = $v[2] ?? 'label';
} else {
$nums = $v;
$color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
@ -485,7 +483,7 @@ class Auth extends \fast\Auth
}
$v['icon'] = $v['icon'] . ' fa-fw';
$v['url'] = isset($v['url']) && $v['url'] ? $v['url'] : '/' . $module . '/' . $v['name'];
$v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : '';
$v['badge'] = $badgeList[$v['name']] ?? '';
$v['title'] = __($v['title']);
$v['url'] = preg_match("/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i", $v['url']) ? $v['url'] : url($v['url']);
$v['menuclass'] = in_array($v['menutype'], ['dialog', 'ajax']) ? 'btn-' . $v['menutype'] : '';

View File

@ -460,7 +460,7 @@ trait Backend
if ($has_admin_id) {
$auth = Auth::instance();
foreach ($insert as &$val) {
if (!isset($val['admin_id']) || empty($val['admin_id'])) {
if (empty($val['admin_id'])) {
$val['admin_id'] = $auth->isLogin() ? $auth->id : 0;
}
}

View File

@ -41,8 +41,8 @@ class AdminLog extends Model
/**
* 记录日志
* @param string $title
* @param string $content
* @param string $title 日志标题
* @param string $content 日志内容
*/
public static function record($title = '', $content = '')
{
@ -50,6 +50,9 @@ class AdminLog extends Model
$admin_id = $auth->isLogin() ? $auth->id : 0;
$username = $auth->isLogin() ? $auth->username : __('Unknown');
// 设置过滤函数
request()->filter('trim,strip_tags,htmlspecialchars');
$controllername = Loader::parseName(request()->controller());
$actionname = strtolower(request()->action());
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
@ -60,12 +63,12 @@ class AdminLog extends Model
}
}
}
$content = $content ? $content : self::$content;
$content = $content ?: self::$content;
if (!$content) {
$content = request()->param('', null, 'trim,strip_tags,htmlspecialchars');
$content = request()->param('') ?: file_get_contents("php://input");
$content = self::getPureContent($content);
}
$title = $title ? $title : self::$title;
$title = $title ?: self::$title;
if (!$title) {
$title = [];
$breadcrumb = Auth::instance()->getBreadcrumb($path);
@ -77,18 +80,18 @@ class AdminLog extends Model
self::create([
'title' => $title,
'content' => !is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content,
'url' => substr(request()->url(), 0, 1500),
'url' => substr(xss_clean(strip_tags(request()->url())), 0, 1500),
'admin_id' => $admin_id,
'username' => $username,
'useragent' => substr(request()->server('HTTP_USER_AGENT'), 0, 255),
'ip' => request()->ip()
'ip' => xss_clean(strip_tags(request()->ip()))
]);
}
/**
* 获取已屏蔽关键信息的数据
* @param $content
* @return false|string
* @return array
*/
protected static function getPureContent($content)
{

View File

@ -105,6 +105,12 @@
<span class="msg-box n-right" for="c-{$item.name}"></span>
</div>
{/case}
{case switch}
<input id="c-{$item.name}" name="row[{$item.name}]" type="hidden" value="{:$item.value?1:0}">
<a href="javascript:;" data-toggle="switcher" class="btn-switcher" data-input-id="c-{$item.name}" data-yes="1" data-no="0">
<i class="fa fa-toggle-on text-success {if !$item.value}fa-flip-horizontal text-gray{/if} fa-2x"></i>
</a>
{/case}
{case bool}
<label for="row[{$item.name}]-yes"><input id="row[{$item.name}]-yes" name="row[{$item.name}]" type="radio" value="1" {$item.value?'checked':''} data-tip="{$item.tip}" /> {:__('Yes')}</label>
<label for="row[{$item.name}]-no"><input id="row[{$item.name}]-no" name="row[{$item.name}]" type="radio" value="0" {$item.value?'':'checked'} data-tip="{$item.tip}" /> {:__('No')}</label>

View File

@ -160,13 +160,18 @@
<div class="">
<div class=""><%=#__("Are you sure you want to unstall %s?", addon['title'])%>
<p class="text-danger">{:__('Delete all the addon file and cannot be recovered!')} </p>
{if config('app_debug')}
{if config('app_debug')}
<p class="text-danger"><input type="checkbox" name="droptables" id="droptables" data-name="<%=addon['name']%>"/> {:__('Delete all the addon database and cannot be recovered!')} </p>
{/if}
{/if}
<p class="text-danger">{:__('Please backup important data manually before uninstall!')}</p>
</div>
</div>
</script>
<script id="upgradetpl" type="text/html">
<div class="">
<div class=""><%=#__("Upgrade tips", addon['title'])%></div>
</div>
</script>
<script id="conflicttpl" type="text/html">
<div class="alert alert-dismissable alert-danger">
<button type="button" class="close" data-dismiss="alert">×</button>

View File

@ -137,6 +137,8 @@ class Common extends Api
$attachment = $upload->upload();
} catch (UploadException $e) {
$this->error($e->getMessage());
} catch (\Exception $e) {
$this->error($e->getMessage());
}
$this->success(__('Uploaded successful'), ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]);

View File

@ -45,6 +45,12 @@ class Ems extends Api
if ($last && time() - $last['createtime'] < 60) {
$this->error(__('发送频繁'));
}
$ipSendTotal = \app\common\model\Ems::where(['ip' => $this->request->ip()])->whereTime('createtime', '-1 hours')->count();
if ($ipSendTotal >= 5) {
$this->error(__('发送频繁'));
}
if ($event) {
$userinfo = User::getByEmail($email);
if ($event == 'register' && $userinfo) {

View File

@ -2,7 +2,6 @@
// 公共助手函数
use Symfony\Component\VarExporter\VarExporter;
use think\exception\HttpResponseException;
use think\Response;
@ -88,8 +87,8 @@ if (!function_exists('cdnurl')) {
function cdnurl($url, $domain = false)
{
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
if (is_bool($domain)) {
$cdnurl = \think\Config::get('upload.cdnurl');
$cdnurl = \think\Config::get('upload.cdnurl');
if (is_bool($domain) || stripos($cdnurl, '/') === 0) {
$url = preg_match($regex, $url) || ($cdnurl && stripos($url, $cdnurl) === 0) ? $url : $cdnurl . $url;
}
if ($domain && !preg_match($regex, $url)) {
@ -249,9 +248,10 @@ if (!function_exists('addtion')) {
if ($v['model']) {
$model = new $v['model'];
} else {
$model = $v['name'] ? \think\Db::name($v['name']) : \think\Db::table($v['table']);
// 优先判断使用table的配置
$model = $v['table'] ? \think\Db::table($v['table']) : \think\Db::name($v['name']);
}
$primary = $v['primary'] ? $v['primary'] : $model->getPk();
$primary = $v['primary'] ?: $model->getPk();
$result[$v['field']] = isset($ids[$v['field']]) ? $model->where($primary, 'in', $ids[$v['field']])->column($v['column'], $primary) : [];
}
@ -280,60 +280,6 @@ if (!function_exists('var_export_short')) {
function var_export_short($data, $return = true)
{
return var_export($data, $return);
$replaced = [];
$count = 0;
//判断是否是对象
if (is_resource($data) || is_object($data)) {
return var_export($data, $return);
}
//判断是否有特殊的键名
$specialKey = false;
array_walk_recursive($data, function (&$value, &$key) use (&$specialKey) {
if (is_string($key) && (stripos($key, "\n") !== false || stripos($key, "array (") !== false)) {
$specialKey = true;
}
});
if ($specialKey) {
return var_export($data, $return);
}
array_walk_recursive($data, function (&$value, &$key) use (&$replaced, &$count, &$stringcheck) {
if (is_object($value) || is_resource($value)) {
$replaced[$count] = var_export($value, true);
$value = "##<{$count}>##";
} else {
if (is_string($value) && (stripos($value, "\n") !== false || stripos($value, "array (") !== false)) {
$index = array_search($value, $replaced);
if ($index === false) {
$replaced[$count] = var_export($value, true);
$value = "##<{$count}>##";
} else {
$value = "##<{$index}>##";
}
}
}
$count++;
});
$dump = var_export($data, true);
$dump = preg_replace('#(?:\A|\n)([ ]*)array \(#i', '[', $dump); // Starts
$dump = preg_replace('#\n([ ]*)\),#', "\n$1],", $dump); // Ends
$dump = preg_replace('#=> \[\n\s+\],\n#', "=> [],\n", $dump); // Empties
$dump = preg_replace('#\)$#', "]", $dump); //End
if ($replaced) {
$dump = preg_replace_callback("/'##<(\d+)>##'/", function ($matches) use ($replaced) {
return $replaced[$matches[1]] ?? "''";
}, $dump);
}
if ($return === true) {
return $dump;
} else {
echo $dump;
}
}
}
@ -429,7 +375,7 @@ if (!function_exists('check_cors_request')) {
*/
function check_cors_request()
{
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN']) {
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] && config('fastadmin.cors_request_domain')) {
$info = parse_url($_SERVER['HTTP_ORIGIN']);
$domainArr = explode(',', config('fastadmin.cors_request_domain'));
$domainArr[] = request()->host(true);
@ -504,20 +450,24 @@ if (!function_exists('check_url_allowed')) {
* @param string $url URL
* @return bool
*/
function check_url_allowed($url = null)
function check_url_allowed($url = '')
{
//允许的主机列表
$allowedHostArr = [
strtolower(request()->host())
];
if (empty($url)) {
return true;
}
//如果是站内相对链接则允许
if (preg_match("/^[\/a-z][a-z0-9][a-z0-9\.\/]+\$/i", $url) && substr($url, 0, 2) !== '//') {
if (preg_match("/^[\/a-z][a-z0-9][a-z0-9\.\/]+((\?|#).*)?\$/i", $url) && substr($url, 0, 2) !== '//') {
return true;
}
//如果是站外链接则需要判断HOST是否允许
if (preg_match("/((http[s]?:\/\/)+(?>[a-z\-0-9]{2,}\.){1,}[a-z]{2,8})(?:\s|\/)/i", $url)) {
if (preg_match("/((http[s]?:\/\/)+((?>[a-z\-0-9]{2,}\.)+[a-z]{2,8}|((?>([0-9]{1,3}\.)){3}[0-9]{1,3}))(:[0-9]{1,5})?)(?:\s|\/)/i", $url)) {
$chkHost = parse_url(strtolower($url), PHP_URL_HOST);
if ($chkHost && in_array($chkHost, $allowedHostArr)) {
return true;

View File

@ -204,8 +204,8 @@ class Api
'time' => Request::instance()->server('REQUEST_TIME'),
'data' => $data,
];
// 如果未设置类型则自动判断
$type = $type ? $type : ($this->request->param(config('var_jsonp_handler')) ? 'jsonp' : $this->responseType);
// 如果未设置类型则使用默认类型判断
$type = $type ? : $this->responseType;
if (isset($header['statuscode'])) {
$code = $header['statuscode'];

View File

@ -123,10 +123,10 @@ class Backend extends Controller
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
// 定义是否Addtabs请求
!defined('IS_ADDTABS') && define('IS_ADDTABS', input("addtabs") ? true : false);
!defined('IS_ADDTABS') && define('IS_ADDTABS', (bool)input("addtabs"));
// 定义是否Dialog请求
!defined('IS_DIALOG') && define('IS_DIALOG', input("dialog") ? true : false);
!defined('IS_DIALOG') && define('IS_DIALOG', (bool)input("dialog"));
// 定义是否AJAX请求
!defined('IS_AJAX') && define('IS_AJAX', $this->request->isAjax());
@ -269,7 +269,8 @@ class Backend extends Controller
$sort = $this->request->get("sort", !empty($this->model) && $this->model->getPk() ? $this->model->getPk() : 'id');
$order = $this->request->get("order", "DESC");
$offset = $this->request->get("offset/d", 0);
$limit = $this->request->get("limit/d", 999999);
$limit = $this->request->get("limit/d", 0);
$limit = $limit ?: 999999;
//新增自动计算页码
$page = $limit ? intval($offset / $limit) + 1 : 1;
if ($this->request->has("page")) {
@ -397,7 +398,7 @@ class Backend extends Controller
$arr = $arr[0];
}
$tableArr = explode('.', $k);
if (count($tableArr) > 1 && $tableArr[0] != $name && !in_array($tableArr[0], $alias)
if (count($tableArr) > 1 && $tableArr[0] != $name && !in_array($tableArr[0], $alias)
&& !empty($this->model) && $this->relationSearch) {
//修复关联模型下时间无法搜索的BUG
$relation = Loader::parseName($tableArr[0], 1, false);

View File

@ -164,7 +164,7 @@ class Auth
'avatar' => '',
];
$params = array_merge($data, [
'nickname' => preg_match("/^1[3-9]{1}\d{9}$/",$username) ? substr_replace($username,'****',3,4) : $username,
'nickname' => preg_match("/^1[3-9]{1}\d{9}$/", $username) ? substr_replace($username, '****', 3, 4) : $username,
'salt' => Random::alnum(),
'jointime' => $time,
'joinip' => $ip,
@ -356,7 +356,7 @@ class Auth
}
$url = ($module ? $module : request()->module()) . '/' . (is_null($path) ? $this->getRequestUri() : $path);
$url = strtolower(str_replace('.', '/', $url));
return in_array($url, $rules) ? true : false;
return in_array($url, $rules);
}
/**
@ -394,7 +394,7 @@ class Auth
/**
* 获取会员组别规则列表
* @return array
* @return array|bool|\PDOStatement|string|\think\Collection
*/
public function getRuleList()
{

View File

@ -182,14 +182,14 @@ class Menu
} else {
$pid = $parent;
}
$allow = array_flip(['file', 'name', 'title', 'url', 'icon', 'condition', 'remark', 'ismenu', 'menutype', 'extend', 'weigh']);
$allow = array_flip(['file', 'name', 'title', 'url', 'icon', 'condition', 'remark', 'ismenu', 'menutype', 'extend', 'weigh', 'status']);
foreach ($newMenu as $k => $v) {
$hasChild = isset($v['sublist']) && $v['sublist'];
$data = array_intersect_key($v, $allow);
$data['ismenu'] = $data['ismenu'] ?? ($hasChild ? 1 : 0);
$data['icon'] = $data['icon'] ?? ($hasChild ? 'fa fa-list' : 'fa fa-circle-o');
$data['pid'] = $pid;
$data['status'] = 'normal';
$data['status'] = $data['status'] ?? 'normal';
if (!isset($oldMenu[$data['name']])) {
$menu = AuthRule::create($data);
} else {

View File

@ -16,7 +16,6 @@ use Exception;
*/
class Security
{
protected static $instance = null;
/**
@ -420,7 +419,7 @@ class Security
*/
public function get_random_bytes($length)
{
if (empty($length) OR !ctype_digit((string)$length)) {
if (empty($length) or !ctype_digit((string)$length)) {
return false;
}
@ -485,8 +484,8 @@ class Security
static $_entities;
isset($charset) OR $charset = $this->charset;
isset($_entities) OR $_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML5, $charset));
isset($charset) or $charset = $this->charset;
isset($_entities) or $_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML5, $charset));
do {
$str_compare = $str;
@ -698,7 +697,7 @@ class Security
// Is it indeed an "evil" attribute?
preg_match($is_evil_pattern, $attribute['name'][0])
// Or does it have an equals sign, but no value and not quoted? Strip that too!
OR (trim($attribute['value'][0]) === '')
or (trim($attribute['value'][0]) === '')
) {
$attributes[] = 'xss=removed';
} else {
@ -870,5 +869,4 @@ class Security
return $str;
}
}

View File

@ -175,7 +175,7 @@ class Config extends Model
if (!preg_match("/^((?:[a-z]+:)?\/\/)(.*)/i", $uploadurl) && substr($uploadurl, 0, 1) !== '/') {
$uploadurl = url($uploadurl, '', false);
}
$uploadcfg['fullmode'] = isset($uploadcfg['fullmode']) && $uploadcfg['fullmode'] ? true : false;
$uploadcfg['fullmode'] = isset($uploadcfg['fullmode']) && $uploadcfg['fullmode'];
$uploadcfg['thumbstyle'] = $uploadcfg['thumbstyle'] ?? '';
$upload = [

View File

@ -316,7 +316,7 @@ return [
//允许跨域的域名,多个以,分隔
'cors_request_domain' => 'localhost,127.0.0.1',
//版本号
'version' => '1.4.0.20230315',
'version' => '2.0.0.20240401',
//API接口地址
'api_url' => 'https://api.fastadmin.net',
],

View File

@ -33,8 +33,8 @@ return [
'mail_type' => '1',
'mail_smtp_host' => 'smtp.qq.com',
'mail_smtp_port' => '465',
'mail_smtp_user' => '10000',
'mail_smtp_pass' => 'password',
'mail_smtp_user' => '',
'mail_smtp_pass' => '',
'mail_verify_type' => '2',
'mail_from' => '10000@qq.com',
'mail_from' => '',
];

View File

@ -66,13 +66,13 @@ class User extends Frontend
*/
public function register()
{
$url = $this->request->request('url', '', 'trim,xss_clean');
$url = $this->request->request('url', '', 'url_clean');
if ($this->auth->id) {
$this->success(__('You\'ve logged in, do not login again'), $url ? $url : url('user/index'));
}
if ($this->request->isPost()) {
$username = $this->request->post('username');
$password = $this->request->post('password');
$password = $this->request->post('password', '', null);
$email = $this->request->post('email');
$mobile = $this->request->post('mobile', '');
$captcha = $this->request->post('captcha');
@ -144,13 +144,13 @@ class User extends Frontend
*/
public function login()
{
$url = $this->request->request('url', '', 'trim,xss_clean');
$url = $this->request->request('url', '', 'url_clean');
if ($this->auth->id) {
$this->success(__('You\'ve logged in, do not login again'), $url ?: url('user/index'));
}
if ($this->request->isPost()) {
$account = $this->request->post('account');
$password = $this->request->post('password');
$password = $this->request->post('password', '', null);
$keeplogin = (int)$this->request->post('keeplogin');
$token = $this->request->post('__token__');
$rule = [
@ -270,9 +270,9 @@ class User extends Frontend
public function changepwd()
{
if ($this->request->isPost()) {
$oldpassword = $this->request->post("oldpassword");
$newpassword = $this->request->post("newpassword");
$renewpassword = $this->request->post("renewpassword");
$oldpassword = $this->request->post("oldpassword", '', null);
$newpassword = $this->request->post("newpassword", '', null);
$renewpassword = $this->request->post("renewpassword", '', null);
$token = $this->request->post('__token__');
$rule = [
'oldpassword' => 'require|regex:\S{6,30}',
@ -299,7 +299,6 @@ class User extends Frontend
$result = $validate->check($data);
if (!$result) {
$this->error(__($validate->getError()), null, ['token' => $this->request->token()]);
return false;
}
$ret = $this->auth->changepwd($newpassword, $oldpassword);

View File

@ -24,26 +24,21 @@
<a href="{:url('user/profile')}" class="btn btn-primary pull-right"><i class="fa fa-pencil"></i> {:__('Profile')}</a>
</h2>
<div class="row user-baseinfo">
<div class="col-md-3 col-sm-3 col-xs-2 text-center user-center">
<div class="col-md-3 col-sm-3 col-xs-3 text-center user-center">
<a href="{:url('user/profile')}" title="{:__('Click to edit')}">
<span class="avatar-img"><img src="{$user.avatar|htmlentities|cdnurl}" alt=""></span>
</a>
</div>
<div class="col-md-9 col-sm-9 col-xs-10">
<!-- Content -->
<div class="col-md-9 col-sm-9 col-xs-9">
<div class="ui-content">
<!-- Heading -->
<h4><a href="{:url('user/profile')}">{$user.nickname|htmlentities}</a></h4>
<!-- Paragraph -->
<p class="text-muted">
{$user.bio|default=__("This guy hasn't written anything yet")|htmlentities}
</p>
<!-- Success -->
</div>
</div>
<div class="col-md-9 col-sm-9 col-xs-12">
<!-- Content -->
<div class="ui-content">
<div class="basicinfo">
<div class="row">
@ -56,12 +51,6 @@
<a href="javascript:;" class="viewscore">{$user.score}</a>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-md-2">{:__('Successions')}</div>
<div class="col-xs-8 col-md-4">{$user.successions} {:__('Day')}</div>
<div class="col-xs-4 col-md-2">{:__('Maxsuccessions')}</div>
<div class="col-xs-8 col-md-4">{$user.maxsuccessions} {:__('Day')}</div>
</div>
<div class="row">
<div class="col-xs-4 col-md-2">{:__('Logintime')}</div>
<div class="col-xs-8 col-md-4">{$user.logintime|date="Y-m-d H:i:s",###}</div>

View File

@ -118,8 +118,7 @@
</div>
</div>
<div class="form-group form-footer">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<div class="col-xs-12 col-sm-8 col-sm-offset-3">
<button type="submit" class="btn btn-md btn-primary">{:__('Ok')}</button>
</div>
</div>

View File

@ -132,8 +132,7 @@
</div>
<div class="form-footer">
<div class="form-group" style="margin-bottom:0;">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<div class="col-xs-12 col-sm-8 col-sm-offset-3">
<button type="submit" class="btn btn-md btn-primary">{:__('Submit')}</button>
</div>
</div>
@ -166,8 +165,7 @@
</div>
<div class="form-footer">
<div class="form-group" style="margin-bottom:0;">
<label class="control-label col-xs-12 col-sm-3"></label>
<div class="col-xs-12 col-sm-8">
<div class="col-xs-12 col-sm-8 col-sm-offset-3">
<button type="submit" class="btn btn-md btn-primary">{:__('Submit')}</button>
</div>
</div>

View File

@ -21,7 +21,7 @@
"topthink/think-installer": "^1.0.14",
"topthink/think-queue": "1.1.6",
"topthink/think-helper": "^1.0.7",
"karsonzhang/fastadmin-addons": "~1.3.2",
"karsonzhang/fastadmin-addons": "~1.4.0",
"overtrue/pinyin": "^3.0",
"phpoffice/phpspreadsheet": "1.19.0",
"overtrue/wechat": "^4.6.0",
@ -32,7 +32,11 @@
"txthinking/mailer": "^2.0"
},
"config": {
"preferred-install": "dist"
"preferred-install": "dist",
"allow-plugins": {
"topthink/think-installer": true,
"easywechat-composer/easywechat-composer": true
}
},
"repositories": [
{

View File

@ -60,15 +60,15 @@ class Http
} else {
$defaults[CURLOPT_CUSTOMREQUEST] = $method;
}
$defaults[CURLOPT_POSTFIELDS] = $params;
$defaults[CURLOPT_POSTFIELDS] = is_array($params) && count(array_filter($params, 'is_array')) > 0 ? $query_string : $params;
}
$defaults[CURLOPT_HEADER] = false;
$defaults[CURLOPT_USERAGENT] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.98 Safari/537.36";
$defaults[CURLOPT_FOLLOWLOCATION] = true;
$defaults[CURLOPT_RETURNTRANSFER] = true;
$defaults[CURLOPT_CONNECTTIMEOUT] = 3;
$defaults[CURLOPT_TIMEOUT] = 3;
$defaults[CURLOPT_CONNECTTIMEOUT] = 10;
$defaults[CURLOPT_TIMEOUT] = 10;
// disable 100-continue
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
@ -133,12 +133,12 @@ class Http
}
$parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : '';
//发送socket请求,获得连接句柄
$fp = fsockopen($parts['host'], $parts['port'] ?? 80, $errno, $errstr, 3);
$fp = fsockopen($parts['host'], $parts['port'] ?? 80, $errno, $errstr, 10);
if (!$fp) {
return false;
}
//设置超时时间
stream_set_timeout($fp, 3);
stream_set_timeout($fp, 10);
$out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n";
$out .= "Host: {$parts['host']}\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";

View File

@ -970,6 +970,9 @@ form.form-horizontal .control-label {
.fixed-table-container .bs-checkbox {
min-width: 36px;
}
.fixed-table-container tr[data-origpos] > td > .tooltip.in {
display: none !important;
}
/*修复nice-validator新版下的一处BUG*/
.nice-validator input,
.nice-validator select,
@ -1519,6 +1522,14 @@ table.table-nowrap thead > tr > th {
border-bottom: 1px solid #eee;
border-radius: 0;
}
.sidebar-menu li.active > a > .fa-angle-left,
.sidebar-menu li.active > a > .pull-right-container > .fa-angle-left {
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
.sidebar-menu li.treeview-open > a > .fa-angle-left,
.sidebar-menu li.treeview-open > a > .pull-right-container > .fa-angle-left {
-webkit-transform: rotate(-90deg);
@ -1610,4 +1621,25 @@ table.table-nowrap thead > tr > th {
display: block;
border-bottom: 1px solid #ddd;
}
.autocontent {
position: relative;
}
.autocontent .autocontent-caret {
position: absolute;
right: 0;
top: 0;
height: 100%;
line-height: 1;
background: #eee;
color: #ddd;
vertical-align: middle;
padding: 0 5px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.autocontent .autocontent-caret:hover {
color: #ccc;
}
/*# sourceMappingURL=backend.css.map */

File diff suppressed because one or more lines are too long

View File

@ -317,6 +317,10 @@ a:focus {
height: 60px;
line-height: 27px;
}
.navbar-white .navbar-brand {
height: 60px;
line-height: 27px;
}
.navbar-white .navbar-nav > li > a {
height: 60px;
line-height: 27px;
@ -652,6 +656,20 @@ form.form-horizontal .control-label {
padding: 15px;
min-height: 300px;
}
.n-bootstrap .n-right {
margin-top: 0;
top: -20px;
position: absolute;
left: 0;
text-align: right;
width: 100%;
}
.n-bootstrap .n-right .msg-wrap {
position: relative;
}
.n-bootstrap .col-xs-12 > .n-right .msg-wrap {
margin-right: 15px;
}
}
.nav-pills > li {
margin-right: 5px;
@ -1094,4 +1112,25 @@ main.content {
display: block;
border-bottom: 1px solid #ddd;
}
.autocontent {
position: relative;
}
.autocontent .autocontent-caret {
position: absolute;
right: 0;
top: 0;
height: 100%;
line-height: 1;
background: #eee;
color: #ddd;
vertical-align: middle;
padding: 0 5px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.autocontent .autocontent-caret:hover {
color: #ccc;
}
/*# sourceMappingURL=frontend.css.map */

File diff suppressed because one or more lines are too long

View File

@ -2481,6 +2481,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-blue .sidebar-menu li.treeview.active > a,
.skin-black-blue .sidebar-menu li.treeview.treeview-open > a {
background-color: #555299;
border-left-color: #555299;
}
.skin-black-blue .sidebar-menu .treeview-menu {
padding-left: 0;
}
@ -2717,6 +2722,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-purple .sidebar-menu li.treeview.active > a,
.skin-black-purple .sidebar-menu li.treeview.treeview-open > a {
background-color: #555299;
border-left-color: #555299;
}
.skin-black-purple .sidebar-menu .treeview-menu {
padding-left: 0;
}
@ -2953,6 +2963,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-green .sidebar-menu li.treeview.active > a,
.skin-black-green .sidebar-menu li.treeview.treeview-open > a {
background-color: #555299;
border-left-color: #555299;
}
.skin-black-green .sidebar-menu .treeview-menu {
padding-left: 0;
}
@ -3189,6 +3204,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-red .sidebar-menu li.treeview.active > a,
.skin-black-red .sidebar-menu li.treeview.treeview-open > a {
background-color: #555299;
border-left-color: #555299;
}
.skin-black-red .sidebar-menu .treeview-menu {
padding-left: 0;
}
@ -3425,6 +3445,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-yellow .sidebar-menu li.treeview.active > a,
.skin-black-yellow .sidebar-menu li.treeview.treeview-open > a {
background-color: #555299;
border-left-color: #555299;
}
.skin-black-yellow .sidebar-menu .treeview-menu {
padding-left: 0;
}
@ -3661,6 +3686,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-pink .sidebar-menu li.treeview.active > a,
.skin-black-pink .sidebar-menu li.treeview.treeview-open > a {
background-color: #555299;
border-left-color: #555299;
}
.skin-black-pink .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -198,6 +198,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-blue .sidebar-menu li.treeview.active > a,
.skin-black-blue .sidebar-menu li.treeview.treeview-open > a {
background-color: #181f23;
border-left-color: #181f23;
}
.skin-black-blue .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -198,6 +198,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-green .sidebar-menu li.treeview.active > a,
.skin-black-green .sidebar-menu li.treeview.treeview-open > a {
background-color: #181f23;
border-left-color: #181f23;
}
.skin-black-green .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -198,6 +198,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-pink .sidebar-menu li.treeview.active > a,
.skin-black-pink .sidebar-menu li.treeview.treeview-open > a {
background-color: #181f23;
border-left-color: #181f23;
}
.skin-black-pink .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -198,6 +198,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-purple .sidebar-menu li.treeview.active > a,
.skin-black-purple .sidebar-menu li.treeview.treeview-open > a {
background-color: #181f23;
border-left-color: #181f23;
}
.skin-black-purple .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -198,6 +198,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-red .sidebar-menu li.treeview.active > a,
.skin-black-red .sidebar-menu li.treeview.treeview-open > a {
background-color: #181f23;
border-left-color: #181f23;
}
.skin-black-red .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -198,6 +198,11 @@
background: transparent;
border-left-color: transparent;
}
.skin-black-yellow .sidebar-menu li.treeview.active > a,
.skin-black-yellow .sidebar-menu li.treeview.treeview-open > a {
background-color: #181f23;
border-left-color: #181f23;
}
.skin-black-yellow .sidebar-menu .treeview-menu {
padding-left: 0;
}

View File

@ -468,7 +468,7 @@ function _init() {
//Fix the layout in case the sidebar stretches over the height of the window
//_this.layout.fix();
});
checkElement.parent("li").removeClass("active");
// checkElement.parent("li").removeClass("active");
checkElement.parent("li").removeClass('treeview-open');
}
//If the menu is not visible

View File

@ -242,7 +242,7 @@ define(['fast', 'template', 'moment'], function (Fast, Template, Moment) {
}
//tooltip和popover
if (!('ontouchstart' in document.documentElement)) {
$('body').tooltip({selector: '[data-toggle="tooltip"]'});
$('body').tooltip({selector: '[data-toggle="tooltip"]', trigger: 'hover'});
}
$('body').popover({selector: '[data-toggle="popover"]'});
}

View File

@ -73,12 +73,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
Template.helper("Moment", Moment);
Template.helper("addons", Config['addons']);
$("#faupload-addon").data("params", function () {
$("#faupload-addon").data("params", function (files, xhr) {
var userinfo = Controller.api.userinfo.get();
return {
uid: userinfo ? userinfo.id : '',
token: userinfo ? userinfo.token : '',
version: Config.faversion
version: Config.faversion,
force: (files[0].force || false) ? 1 : 0
};
});
@ -92,7 +93,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
uid: userinfo ? userinfo.id : '',
token: userinfo ? userinfo.token : '',
domain: Config.domain,
version: Config.faversion
version: Config.faversion,
sid: Controller.api.sid()
});
return params;
},
@ -113,7 +115,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
align: 'left',
formatter: Controller.api.formatter.title
},
{field: 'intro', title: __('Intro'), operate: 'LIKE', align: 'left', class: 'visible-lg'},
{
field: 'intro',
title: __('Intro'),
operate: 'LIKE',
align: 'left',
class: 'visible-lg',
formatter: Controller.api.formatter.intro
},
{
field: 'author',
title: __('Author'),
@ -187,7 +196,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
// 离线安装
require(['upload'], function (Upload) {
Upload.api.upload("#faupload-addon", function (data, ret) {
Upload.api.upload("#faupload-addon", function (data, ret, up, file) {
Config['addons'][data.addon.name] = data.addon;
var addon = data.addon;
var testdata = data.addon.testdata;
@ -215,7 +224,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
});
});
return false;
}, function (data, ret) {
}, function (data, ret, up, file) {
if (ret.msg && ret.msg.match(/(login|登录)/g)) {
return Layer.alert(ret.msg, {
title: __('Warning'),
@ -224,6 +233,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
$(".btn-userinfo").trigger("click");
}
});
} else if (ret.code === -1) {
Layer.confirm(__('Upgrade tips', data.title), {title: __('Warmtips')}, function (index, layero) {
up.removeFile(file);
file.force = true;
up.uploadFile(file);
Layer.close(index);
});
return false;
}
});
@ -231,10 +248,16 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
$(document).on("mousedown", "#faupload-addon", function (e) {
var userinfo = Controller.api.userinfo.get();
var uid = userinfo ? userinfo.id : 0;
var uploadBtn = Upload.list['faupload-addon'];
if (parseInt(uid) === 0) {
uploadBtn.disable();
$(".btn-userinfo").trigger("click");
return false;
} else {
if (uploadBtn.disabled) {
uploadBtn.enable();
}
}
});
});
@ -299,14 +322,64 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
Fast.api.ajax({
url: Config.api_url + '/user/logintpl',
type: 'post',
loading: false,
data: {
version: Config.faversion
version: Config.faversion,
sid: Controller.api.sid()
}
}, function (tpldata, ret) {
Layer.open({
content: Template.render(tpldata, {}),
zIndex: 99,
area: area,
title: __('Login'),
resize: false,
btn: [__('Login')],
yes: function (index, layero) {
var data = $("form", layero).serializeArray();
data.push({name: "faversion", value: Config.faversion});
data.push({name: "sid", value: Controller.api.sid()});
Fast.api.ajax({
url: Config.api_url + '/user/login',
type: 'post',
data: data
}, function (data, ret) {
Controller.api.userinfo.set(data);
Layer.closeAll();
Layer.alert(ret.msg, {title: __('Warning'), icon: 1});
return false;
}, function (data, ret) {
});
},
success: function (layero, index) {
this.checkEnterKey = function (event) {
if (event.keyCode === 13) {
$(".layui-layer-btn0").trigger("click");
return false;
}
};
$(document).on('keydown', this.checkEnterKey);
},
end: function () {
$(document).off('keydown', this.checkEnterKey);
}
});
return false;
});
} else {
Fast.api.ajax({
url: Config.api_url + '/user/userinfotpl',
type: 'post',
data: {
uid: userinfo.id,
token: userinfo.token,
version: Config.faversion,
sid: Controller.api.sid()
}
}, function (tpldata, ret) {
Layer.open({
content: Template.render(tpldata, userinfo),
area: area,
title: __('Login FastAdmin'),
resize: false,
btn: [__('Login')],
@ -314,9 +387,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
var data = $("form", layero).serializeArray();
data.push({name: "faversion", value: Config.faversion});
Fast.api.ajax({
url: Config.api_url + '/user/login',
type: 'post',
data: data
url: Config.api_url + '/user/logout',
data: {
uid: userinfo.id,
token: userinfo.token,
version: Config.faversion,
sid: Controller.api.sid()
}
}, function (data, ret) {
Controller.api.userinfo.set(data);
Layer.closeAll();
@ -610,16 +687,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
var uid = userinfo ? userinfo.id : 0;
if (parseInt(uid) === 0) {
return Layer.alert(__('Not login tips'), {
title: __('Warning'),
btn: [__('Login now')],
yes: function (index, layero) {
$(".btn-userinfo").trigger("click", name, version);
},
btn2: function () {
install(name, version, false);
}
});
$(".btn-userinfo").trigger("click", name, version);
return false;
}
install(name, version, false);
});
@ -632,6 +701,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
return false;
}
Template.helper("__", __);
tables = [];
Layer.confirm(Template("uninstalltpl", {addon: Config['addons'][name]}), {focusBtn: false, title: __("Warning")}, function (index, layero) {
uninstall(name, false, $("input[name='droptables']", layero).prop("checked"));
});
@ -659,7 +729,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
}
var version = $(this).data("version");
Layer.confirm(__('Upgrade tips', Config['addons'][name].title), function (index, layero) {
Layer.confirm(__('Upgrade tips', Config['addons'][name].title), {title: __('Warmtips')}, function (index, layero) {
upgrade(name, version);
});
});
@ -709,12 +779,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
if ($(".btn-switch.active").data("type") == "local") {
// return value;
}
var title = '<a class="title" href="' + row.url + '" data-toggle="tooltip" title="' + __('View addon home page') + '" target="_blank">' + value + '</a>';
var title = '<a class="title" href="' + row.url + '" data-toggle="tooltip" title="' + __('View addon home page') + '" target="_blank"><span class="' + Fast.api.escape(row.color) + '">' + value + '</span></a>';
if (row.screenshots && row.screenshots.length > 0) {
title += ' <a href="javascript:;" data-index="' + index + '" class="view-screenshots text-success" title="' + __('View addon screenshots') + '" data-toggle="tooltip"><i class="fa fa-image"></i></a>';
}
return title;
},
intro: function (value, row, index) {
return row.intro + (row.extend ? "<a href='" + Fast.api.escape(row.extend[1]) + "' class='" + Fast.api.escape(row.extend[2]) + "'>" + Fast.api.escape(row.extend[0]) + "</a>" : "");
},
operate: function (value, row, index) {
return Template("operatetpl", {item: row, index: index});
},
@ -777,6 +850,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'cookie']
}
}
},
sid: function () {
var sid = $.cookie('fastadmin_sid');
if (!sid) {
sid = Math.random().toString(20).substr(2, 12);
$.cookie('fastadmin_sid', sid);
}
return sid;
},
refresh: function (table, name) {
//刷新左侧边栏
Fast.api.refreshmenu();

View File

@ -29,7 +29,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
{field: 'title', title: __('Title'), operate: 'LIKE %...%', placeholder: '模糊搜索'},
{field: 'url', title: __('Url'), formatter: Table.api.formatter.url},
{field: 'ip', title: __('IP'), events: Table.api.events.ip, formatter: Table.api.formatter.search},
{field: 'browser', title: __('Browser'), operate: false, formatter: Controller.api.formatter.browser},
{field: 'createtime', title: __('Create time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
{
field: 'operate', title: __('Operate'), table: table,

View File

@ -33,7 +33,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
if (val != '') {
$("ul.sidebar-menu li a[addtabs]:not([href^='javascript:;'])").each(function () {
if ($("span:first", this).text().indexOf(val) > -1 || $(this).attr("py").indexOf(val) > -1 || $(this).attr("pinyin").indexOf(val) > -1) {
html.push('<a data-url="' + $(this).attr("href") + '" href="javascript:;">' + $("span:first", this).text() + '</a>');
html.push('<a data-url="' + ($(this).attr("url") || $(this).attr("href")) + '" href="javascript:;">' + $("span:first", this).text() + '</a>');
if (html.length >= 100) {
return false;
}
@ -67,6 +67,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
var visible = nextul.is(":visible");
if (nextul.length == 0) {
$(this).parents("li").addClass("active");
$(this).closest(".treeview").addClass("treeview-open");
} else {
}
e.stopPropagation();
@ -333,11 +334,13 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
// 切换菜单栏
$(document).on("click", ".sidebar-toggle", function () {
var value = $("body").hasClass("sidebar-collapse") ? 1 : 0;
setTimeout(function () {
$(window).trigger("resize");
}, 300);
createCookie('sidebar_collapse', value);
setTimeout(function(){
var value = $("body").hasClass("sidebar-collapse") ? 1 : 0;
setTimeout(function () {
$(window).trigger("resize");
}, 300);
createCookie('sidebar_collapse', value);
}, 0);
});
// 切换简洁模式菜单

View File

@ -43,7 +43,7 @@
setTimeout(function () {
that.onCommonSearch();
}, 10);
}, 1);
});
};
@ -61,7 +61,7 @@
htmlForm.push('<div class="row">');
for (var i in pColumns) {
var vObjCol = pColumns[i];
if (!vObjCol.checkbox && vObjCol.field !== 'operate' && vObjCol.searchable && vObjCol.operate !== false) {
if (!vObjCol.checkbox && !vObjCol.radio && vObjCol.field && vObjCol.field !== 'operate' && vObjCol.searchable && vObjCol.operate !== false) {
var query = Fast.api.query(vObjCol.field);
var operate = Fast.api.query(vObjCol.field + "-operate");

View File

@ -103,8 +103,8 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
//获取修复后可访问的cdn链接
cdnurl: function (url, domain) {
var rule = new RegExp("^((?:[a-z]+:)?\\/\\/|data:image\\/)", "i");
if(typeof domain === 'undefined'){
var cdnurl = Config.upload.cdnurl;
var cdnurl = Config.upload.cdnurl;
if (typeof domain === 'undefined' || domain === true || cdnurl.indexOf("/") === 0) {
url = rule.test(url) || (cdnurl && url.indexOf(cdnurl) === 0) ? url : cdnurl + url;
}
if (domain && !rule.test(url)) {
@ -118,6 +118,8 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
if (!url) {
url = window.location.href;
}
if (!name)
return '';
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&/]" + name + "([=/]([^&#/?]*)|&|#|$)"),
results = regex.exec(url);
@ -276,6 +278,18 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
time: 2000
}, callback);
},
escape: function (text) {
if (typeof text === 'string') {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
.replace(/`/g, '&#x60;');
}
return text;
},
toastr: Toastr,
layer: Layer
},

View File

@ -27,7 +27,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
Layer.open({
type: 1,
title: __('Reset password'),
area: [Math.min($(window).width(), 450) + "px", "355px"],
area: [$(window).width() < 450 ? ($(window).width() - 10) + "px" : "450px", "355px"],
content: content,
success: function (layero) {
var rule = $("#resetpwd-form input[name='captcha']").data("rule");
@ -98,7 +98,7 @@ define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, und
Layer.open({
type: 1,
title: "修改",
area: [Math.min($(window).width(), 400) + "px", "250px"],
area: [$(window).width() < 450 ? ($(window).width() - 10) + "px" : "450px", "355px"],
content: content,
success: function (layero) {
var form = $("form", layero);

File diff suppressed because one or more lines are too long

View File

@ -12,8 +12,6 @@ require.config({
'form': 'require-form',
'table': 'require-table',
'upload': 'require-upload',
'drag': 'jquery.drag.min',
'drop': 'jquery.drop.min',
'dropzone': 'dropzone.min',
'echarts': 'echarts.min',
'echarts-theme': 'echarts-theme',

View File

@ -133,7 +133,11 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
});
$(form).on("reset", function () {
setTimeout(function () {
$('.selectpage', form).selectPageClear();
$(".selectpage", form).each(function () {
var selectpage = $(this).data("selectPageObject");
selectpage.elem.hidden.val($(this).val());
$(this).selectPageRefresh();
});
}, 1);
});
}
@ -321,9 +325,14 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
var result = template ? [] : {};
$.each(data, function (i, j) {
if (j) {
if (!template) {
if (j.key !== '') {
result['__PLACEHOLDKEY__' + j.key] = j.value;
var keys = Object.keys(j);
if (keys.indexOf("value") > -1 && (keys.length === 1 || (keys.length === 2 && keys.indexOf("key") > -1))) {
if (keys.length === 2) {
if (j.key != '') {
result['__PLACEHOLDKEY__' + j.key] = j.value;
}
} else {
result.push(j.value);
}
} else {
result.push(j);
@ -366,6 +375,12 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
return obj;
};
var fieldlist = $(".fieldlist", form);
//表单重置
form.on("reset", function () {
setTimeout(function () {
fieldlist.trigger("fa.event.refreshfieldlist");
});
});
//监听文本框改变事件
$(document).on('change keyup changed', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
var container = $(this).closest(".fieldlist");
@ -375,7 +390,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
fieldlist.on("click", ".btn-append,.append", function (e, row) {
var container = $(this).closest(".fieldlist");
append(container, row);
// refresh(container);
refresh(container);
});
//移除控制(点击按钮)
fieldlist.on("click", ".btn-remove", function () {
@ -467,9 +482,9 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
},
slider: function (form) {
if ($(".slider", form).length > 0) {
if ($("[data-role='slider'],input.slider", form).length > 0) {
require(['bootstrap-slider'], function () {
$('.slider').removeClass('hidden').css('width', function (index, value) {
$("[data-role='slider'],input.slider").removeClass('hidden').css('width', function (index, value) {
return $(this).parents('.form-control').width();
}).slider().on('slide', function (ev) {
var data = $(this).data();
@ -484,6 +499,11 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
if ($("[data-role='tagsinput']", form).length > 0) {
require(['tagsinput', 'autocomplete'], function () {
$("[data-role='tagsinput']").tagsinput();
form.on("reset", function () {
setTimeout(function () {
$("[data-role='tagsinput']").tagsinput('reset');
}, 0);
});
});
}
},
@ -504,13 +524,27 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
var baseregex = /^([a-z0-9\_]+)([>|<|=|\!]=?)(.*)$/i, strregex = /^('|")(.*)('|")$/, regregex = /^regex:(.*)$/;
// @formatter:off
var operator_result = {
'>': function(a, b) { return a > b; },
'>=': function(a, b) { return a >= b; },
'<': function(a, b) { return a < b; },
'<=': function(a, b) { return a <= b; },
'==': function(a, b) { return a == b; },
'!=': function(a, b) { return a != b; },
'in': function(a, b) { return b.split(/\,/).indexOf(a) > -1; },
'>': function (a, b) {
return a > b;
},
'>=': function (a, b) {
return a >= b;
},
'<': function (a, b) {
return a < b;
},
'<=': function (a, b) {
return a <= b;
},
'==': function (a, b) {
return a == b;
},
'!=': function (a, b) {
return a != b;
},
'in': function (a, b) {
return b.split(/\,/).indexOf(a) > -1;
},
'regex': function (a, b) {
var regParts = b.match(/^\/(.*?)\/([gim]*)$/);
var regexp = regParts ? new RegExp(regParts[1], regParts[2]) : new RegExp(b);
@ -558,10 +592,10 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
});
return success === conditionArr.length;
};
form.on("keyup change click configchange", "input,select", function () {
form.on("keyup change click configchange", "input,textarea,select", function () {
$("[data-favisible][data-favisible!='']", form).each(function () {
var visible = $(this).data("favisible");
var groupArr = visible.split(/\|\|/);
var groupArr = visible ? visible.toString().split(/\|\|/) : [];
var success = 0;
$.each(groupArr, function (i, j) {
if (checkCondition(j)) {
@ -578,10 +612,13 @@ define(['jquery', 'bootstrap', 'upload', 'validator', 'validator-lang'], functio
//追加上忽略元素
setTimeout(function () {
form.data('validator').options.ignore += ((form.data('validator').options.ignore ? ',' : '') + '[data-favisible] :hidden,[data-favisible]:hidden');
var validator = form.data('validator');
if (validator) {
validator.options.ignore += ((validator.options.ignore ? ',' : '') + '.hidden[data-favisible] :hidden,.hidden[data-favisible]:hidden');
}
}, 0);
$("input,select", form).trigger("configchange");
$("input,textarea,select", form).trigger("configchange");
}
},
api: {

View File

@ -6,14 +6,12 @@ require.config({
main: 'moment'
}],
//在打包压缩时将会把include中的模块合并到主文件中
include: ['css', 'layer', 'toastr', 'fast', 'frontend', 'frontend-init', 'table', 'form', 'dragsort', 'drag', 'drop', 'selectpage'],
include: ['css', 'layer', 'toastr', 'fast', 'frontend', 'frontend-init', 'table', 'form', 'dragsort', 'selectpage'],
paths: {
'lang': "empty:",
'form': 'require-form',
'table': 'require-table',
'upload': 'require-upload',
'drag': 'jquery.drag.min',
'drop': 'jquery.drop.min',
'dropzone': 'dropzone.min',
'echarts': 'echarts.min',
'echarts-theme': 'echarts-theme',

View File

@ -14,7 +14,7 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
titleForm: '', //为空则不显示标题,不定义默认显示:普通搜索
idTable: 'commonTable',
showExport: true,
exportDataType: "auto",
exportDataType: "auto", //支持auto,selected,all 当设定为auto时自动时有选中则导出选中没有选中则导出全部
exportTypes: ['json', 'xml', 'csv', 'txt', 'doc', 'excel'],
exportOptions: {
fileName: 'export_' + Moment().format("YYYY-MM-DD"),
@ -51,6 +51,7 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
checkOnInit: true, //是否在初始化时判断
escape: true, //是否对内容进行转义
fixDropdownPosition: true, //是否修复下拉的定位
dragCheckboxMultiselect: true, //拖拽时复选框是否多选模式
selectedIds: [],
selectedData: [],
extend: {
@ -90,14 +91,14 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
name: 'edit',
icon: 'fa fa-pencil',
title: __('Edit'),
extend: 'data-toggle="tooltip"',
extend: 'data-toggle="tooltip" data-container="body"',
classname: 'btn btn-xs btn-success btn-editone'
},
del: {
name: 'del',
icon: 'fa fa-trash',
title: __('Del'),
extend: 'data-toggle="tooltip"',
extend: 'data-toggle="tooltip" data-container="body"',
classname: 'btn btn-xs btn-danger btn-delone'
},
dragsort: {
@ -197,6 +198,8 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
//当刷新表格时
table.on('refresh.bs.table', function (e, settings, data) {
$(Table.config.refreshbtn, toolbar).find(".fa").addClass("fa-spin");
//移除指定浮动弹窗
$(".layui-layer-autocontent").remove();
});
//当执行搜索时
table.on('search.bs.table common-search.bs.table', function (e, settings, data) {
@ -220,39 +223,82 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
table.on('post-body.bs.table', function (e, data) {
$(Table.config.refreshbtn, toolbar).find(".fa").removeClass("fa-spin");
if ($(Table.config.checkboxtd + ":first", table).find("input[type='checkbox'][data-index]").length > 0) {
// 拖拽选择,需要重新绑定事件
require(['drag', 'drop'], function () {
var checkboxtd = $(Table.config.checkboxtd, table);
checkboxtd.drag("start", function (ev, dd) {
return $('<div class="selection" />').css('opacity', .65).appendTo(document.body);
}).drag(function (ev, dd) {
setTimeout(function () {
$(dd.proxy).css({
top: Math.min(ev.pageY, dd.startY),
left: Math.min(ev.pageX, dd.startX),
height: Math.abs(ev.pageY - dd.startY),
width: Math.abs(ev.pageX - dd.startX)
});
}, 0);
}).drag("end", function (ev, dd) {
$(dd.proxy).remove();
});
checkboxtd.drop("start", function () {
Table.api.toggleattr(this);
}).drop(function () {
// Table.api.toggleattr(this);
}).drop("end", function (e) {
var that = this;
setTimeout(function () {
if (e.type === 'mousemove') {
Table.api.toggleattr(that);
//拖拽选择复选框
var posx, posy, dragdiv, drag = false, prepare = false;
var mousemove = function (e) {
if (drag) {
var left = Math.min(e.pageX, posx);
var top = Math.min(e.pageY, posy);
var width = Math.abs(posx - e.pageX);
var height = Math.abs(posy - e.pageY);
dragdiv.css({left: left + "px", top: top + "px", width: width + "px", height: height + "px"});
var dragrect = {x: left, y: top, width: width, height: height};
$(Table.config.checkboxtd, table).each(function () {
var checkbox = $("input:checkbox", this);
var tdrect = this.getBoundingClientRect();
tdrect.x += document.documentElement.scrollLeft;
tdrect.y += document.documentElement.scrollTop;
var td_min_x = tdrect.x;
var td_min_y = tdrect.y;
var td_max_x = tdrect.x + tdrect.width;
var td_max_y = tdrect.y + tdrect.height;
var drag_min_x = dragrect.x;
var drag_min_y = dragrect.y;
var drag_max_x = dragrect.x + dragrect.width;
var drag_max_y = dragrect.y + dragrect.height;
var overlapped = td_min_x <= drag_max_x && td_max_x >= drag_min_x && td_min_y <= drag_max_y && td_max_y >= drag_min_y;
if (overlapped) {
if (!$(this).hasClass("overlaped")) {
$(this).addClass("overlaped");
checkbox.trigger("click");
}
} else {
if ($(this).hasClass("overlaped")) {
$(this).removeClass("overlaped");
checkbox.trigger("click");
}
}
}, 0);
});
$.drop({
multi: true
});
});
}
};
var selectstart = function () {
return false;
};
var mouseup = function () {
if (drag) {
$(document).off("mousemove", mousemove);
$(document).off("selectstart", selectstart);
dragdiv.remove();
}
drag = false;
prepare = false;
$(document.body).css({'MozUserSelect': '', 'webkitUserSelect': ''}).attr('unselectable', 'off');
};
$(Table.config.checkboxtd, table).on("mousedown", function (e) {
//禁止鼠标右键事件和文本框
if (e.button === 2 || $(e.target).is("input")) {
return false;
}
posx = e.pageX;
posy = e.pageY;
prepare = true;
}).on("mousemove", function (e) {
if (prepare && !drag) {
drag = true;
dragdiv = $("<div />");
dragdiv.css({position: 'absolute', width: 0, height: 0, border: "1px dashed blue", background: "#0029ff", left: e.pageX + "px", top: e.pageY + "px", opacity: .1});
dragdiv.appendTo(document.body);
$(document.body).css({'MozUserSelect': 'none', 'webkitUserSelect': 'none'}).attr('unselectable', 'on');
$(document).on("mousemove", mousemove).on("mouseup", mouseup).on("selectstart", selectstart);
if (options.dragCheckboxMultiselect) {
$(Table.config.checkboxtd, table).removeClass("overlaped");
}
}
});
}
});
var exportDataType = options.exportDataType;
@ -282,23 +328,40 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
options.selectedIds = selectedIds;
options.selectedData = selectedData;
}
//如果导出类型为auto时则自动判断
if (exportDataType === 'auto') {
options.exportDataType = selectedIds.length > 0 ? 'selected' : 'all';
if ($(".export .exporttips").length === 0) {
$(".export .dropdown-menu").prepend("<li class='exporttips alert alert-warning-light mb-0 no-border p-2'></li>")
}
$(".export .exporttips").html("导出记录:" + (selectedIds.length > 0 ? "选中" : "全部"));
}
$(Table.config.disabledbtn, toolbar).toggleClass('disabled', !options.selectedIds.length);
});
// 提交通用搜索时判断是否和Tabs筛选一致
table.on('common-search.bs.table', function (e, setting, query) {
var tabs = $('.panel-heading [data-field]', table.closest(".panel-intro"));
var field = tabs.data("field");
var value = $("li.active > a", tabs).data("value");
if (query.filter && typeof query.filter[field] !== 'undefined' && query.filter[field] != value) {
$("li", tabs).removeClass("active");
$("li > a[data-value='" + query.filter[field] + "']", tabs).parent().addClass("active");
}
});
// 绑定TAB事件
$('.panel-heading [data-field] a[data-toggle="tab"]', table.closest(".panel-intro")).on('shown.bs.tab', function (e) {
var field = $(this).closest("[data-field]").data("field");
var value = $(this).data("value");
var object = $("[name='" + field + "']", table.closest(".bootstrap-table").find(".commonsearch-table"));
if (object.prop('tagName') == "SELECT") {
if (object.prop('tagName') === "SELECT") {
$("option[value='" + value + "']", object).prop("selected", true);
} else {
object.val(value);
}
table.trigger("uncheckbox");
table.bootstrapTable('getOptions').totalRows = 0;
table.bootstrapTable('refresh', {pageNumber: 1});
return false;
});
@ -677,25 +740,25 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
images: function (value, row, index) {
value = value == null || value.length === 0 ? '' : value.toString();
var classname = typeof this.classname !== 'undefined' ? this.classname : 'img-sm img-center';
var arr = value !== '' ? value.split(',') : [];
var arr = value !== '' ? (value.indexOf('data:image/') === -1 ? value.split(',') : [value]) : [];
var html = [];
var url;
$.each(arr, function (i, value) {
value = value ? value : '/assets/img/blank.gif';
url = Fast.api.cdnurl(value, true);
url = url.match(/^(\/|data:image\\)/) ? url : url + Config.upload.thumbstyle;
html.push('<a href="javascript:"><img class="' + classname + '" src="' + url + '" width="30" height="30" /></a>');
//匹配本地、data:image、或已包含标识符首字符
url = !Config.upload.thumbstyle || url.match(/^(\/|data:image\/)/) || url.indexOf(Config.upload.thumbstyle.substring(0, 1)) > -1 ? url : url + Config.upload.thumbstyle;
html.push('<a href="javascript:"><img class="' + classname + '" src="' + url + '" /></a>');
});
return html.join(' ');
},
file: function (value, row, index) {
return Table.api.formatter.files.call(this, value, row, index);
Table.api.formatter.files.call(this, value, row, index);
},
files: function (value, row, index) {
value = value == null || value.length === 0 ? '' : value.toString();
var classname = typeof this.classname !== 'undefined' ? this.classname : 'img-sm img-center';
var arr = value !== '' ? value.split(',') : [];
var arr = value !== '' ? (value.indexOf('data:image/') === -1 ? value.split(',') : [value]) : [];
var html = [];
var suffix, url;
$.each(arr, function (i, value) {
@ -708,8 +771,9 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
return html.join(' ');
},
content: function (value, row, index) {
var width = this.width != undefined ? (this.width.match(/^\d+$/) ? this.width + "px" : this.width) : "250px";
return "<div style='white-space: nowrap; text-overflow:ellipsis; overflow: hidden; max-width:" + width + ";'>" + value + "</div>";
var width = this.width != undefined ? (this.width.toString().match(/^\d+$/) ? this.width + "px" : this.width) : "250px";
var hover = this.hover != undefined && this.hover ? "autocontent-hover" : "";
return "<div class='autocontent-item " + hover + "' style='white-space: nowrap; text-overflow:ellipsis; overflow: hidden; max-width:" + width + ";'>" + value + "</div>";
},
status: function (value, row, index) {
var custom = {normal: 'success', hidden: 'gray', deleted: 'danger', locked: 'info'};
@ -932,7 +996,7 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
$.each(dropdowns, function (i, j) {
dropdownHtml.push('<div class="btn-group"><button type="button" class="btn btn-primary dropdown-toggle btn-xs" data-toggle="dropdown">' + i + '</button><button type="button" class="btn btn-primary dropdown-toggle btn-xs" data-toggle="dropdown"><span class="caret"></span></button><ul class="dropdown-menu dropdown-menu-right"><li>' + j.join('</li><li>') + '</li></ul></div>');
});
html.unshift(dropdownHtml);
html.unshift(dropdownHtml.join(' '));
}
return html.join(' ');
},
@ -943,7 +1007,7 @@ define(['jquery', 'bootstrap'], function ($, undefined) {
row.ids = ids ? ids : (typeof row.ids !== 'undefined' ? row.ids : 0);
url = url == null || url.length === 0 ? '' : url.toString();
//自动添加ids参数
url = !url.match(/(?=([?&]ids=)|(\/ids\/)|(\{ids}))/i) ?
url = !url.match(/(?=([?&]ids=)|(\/ids\/)|(\{ids}))/i) ?
url + (url.match(/(\?|&)+/) ? "&ids=" : "/ids/") + '{ids}' : url;
url = url.replace(/\{(.*?)\}/gi, function (matched) {
matched = matched.substring(1, matched.length - 1);

View File

@ -38,7 +38,7 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
onDomUploadSuccess = Upload.api.custom[onDomUploadSuccess];
}
if (typeof onDomUploadSuccess === 'function') {
var result = onDomUploadSuccess.call(button, data, ret);
var result = onDomUploadSuccess.call(button, data, ret, up, file);
if (result === false)
return;
}
@ -46,7 +46,7 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
}
if (typeof onUploadSuccess === 'function') {
var result = onUploadSuccess.call(button, data, ret);
var result = onUploadSuccess.call(button, data, ret, up, file);
if (result === false)
return;
}
@ -63,7 +63,7 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
onDomUploadError = Upload.api.custom[onDomUploadError];
}
if (typeof onDomUploadError === 'function') {
var result = onDomUploadError.call(button, data, ret);
var result = onDomUploadError.call(button, data, ret, up, file);
if (result === false)
return;
}
@ -71,7 +71,7 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
}
if (typeof onUploadError === 'function') {
var result = onUploadError.call(button, data, ret);
var result = onUploadError.call(button, data, ret, up, file);
if (result === false) {
return;
}
@ -398,6 +398,11 @@ define(['jquery', 'bootstrap', 'dropzone', 'template'], function ($, undefined,
});
}
if (input_id) {
$("#" + input_id).closest("form").on("reset", function () {
setTimeout($.proxy(function () {
$("#" + input_id, this).trigger("change");
}, this), 0);
});
//粘贴上传、拖拽上传
$("body").on('paste drop', "#" + input_id, function (event) {
var originEvent = event.originalEvent;

File diff suppressed because one or more lines are too long

View File

@ -273,6 +273,15 @@
});
},
/**
* reset the items
*/
reset: function () {
var val = this.$element.val();
this.removeAll();
this.add(val);
},
/**
* Returns the items added as tags
*/

View File

@ -888,6 +888,11 @@ form.form-horizontal .control-label {
.bs-checkbox {
min-width: 36px;
}
//拖拽时隐藏tooltip避免出现错位
tr[data-origpos] > td > .tooltip.in {
display: none !important;
}
}
/*修复nice-validator新版下的一处BUG*/
@ -1547,6 +1552,10 @@ table.table-nowrap {
}
}
.sidebar-menu li.active > a > .fa-angle-left, .sidebar-menu li.active > a > .pull-right-container > .fa-angle-left {
.rotate(0deg);
}
.sidebar-menu li.treeview-open > a > .fa-angle-left, .sidebar-menu li.treeview-open > a > .pull-right-container > .fa-angle-left {
.rotate(-90deg);
}
@ -1658,3 +1667,27 @@ table.table-nowrap {
}
}
.autocontent {
position: relative;
.autocontent-caret {
position: absolute;
right: 0;
top: 0;
height: 100%;
line-height: 1;
background: #eee;
color: #ddd;
vertical-align: middle;
padding: 0 5px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover {
color: #ccc;
}
}
}

View File

@ -87,6 +87,12 @@ a {
}
}
.navbar-white {
.navbar-brand {
height: 60px;
line-height: 27px;
}
}
.navbar-white .navbar-nav {
> li > a {
height: 60px;
@ -475,6 +481,27 @@ form.form-horizontal .control-label {
padding: 15px;
min-height: 300px;
}
.n-bootstrap {
.n-right {
margin-top: 0;
top: -20px;
position: absolute;
left: 0;
text-align: right;
width: 100%;
.msg-wrap {
position: relative;
}
}
.col-xs-12 > .n-right {
.msg-wrap {
margin-right: 15px;
}
}
}
}
.nav-pills > li {
@ -948,3 +975,27 @@ main.content {
}
}
.autocontent {
position: relative;
.autocontent-caret {
position: absolute;
right: 0;
top: 0;
height: 100%;
line-height: 1;
background: #eee;
color: #ddd;
vertical-align: middle;
padding: 0 5px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover {
color: #ccc;
}
}
}

View File

@ -99,6 +99,13 @@
border-left-color: transparent;
}
li.treeview {
&.active > a,&.treeview-open > a {
background-color: @sidebar-dark-submenu-bg;
border-left-color: @sidebar-dark-submenu-bg;
}
}
.treeview-menu {
padding-left: 0;

View File

@ -99,6 +99,13 @@
border-left-color: transparent;
}
li.treeview {
&.active > a,&.treeview-open > a {
background-color: @sidebar-dark-submenu-bg;
border-left-color: @sidebar-dark-submenu-bg;
}
}
.treeview-menu {
padding-left: 0;

View File

@ -99,6 +99,13 @@
border-left-color: transparent;
}
li.treeview {
&.active > a,&.treeview-open > a {
background-color: @sidebar-dark-submenu-bg;
border-left-color: @sidebar-dark-submenu-bg;
}
}
.treeview-menu {
padding-left: 0;

View File

@ -99,6 +99,13 @@
border-left-color: transparent;
}
li.treeview {
&.active > a,&.treeview-open > a {
background-color: @sidebar-dark-submenu-bg;
border-left-color: @sidebar-dark-submenu-bg;
}
}
.treeview-menu {
padding-left: 0;

View File

@ -99,6 +99,13 @@
border-left-color: transparent;
}
li.treeview {
&.active > a,&.treeview-open > a {
background-color: @sidebar-dark-submenu-bg;
border-left-color: @sidebar-dark-submenu-bg;
}
}
.treeview-menu {
padding-left: 0;

View File

@ -99,6 +99,13 @@
border-left-color: transparent;
}
li.treeview {
&.active > a,&.treeview-open > a {
background-color: @sidebar-dark-submenu-bg;
border-left-color: @sidebar-dark-submenu-bg;
}
}
.treeview-menu {
padding-left: 0;