1.框架增加用户端,有user模块和userend脚本,文件结构和功能类似于后台admin模块和backend脚本,便于进行用户端快速开发;

2.增加user_rule表字段,修改相关记录,详见fastadmin.sql文件底部;
3.增加用户日志功能;
4.在application下增加三端(前端、后端、用户端)共用语言包,并修改语言包调用函数;
5.修改cmd命令CRUD,快速建立admin、user模块的controller和view文件、共用语言包,以及Backend和Userend脚本。
pull/81/head
raorf 2018-10-31 22:27:06 +08:00
parent 5f623e7fe1
commit 6651879bef
49 changed files with 19855 additions and 213 deletions

View File

@ -124,7 +124,7 @@ class Crud extends Command
->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null)
->addOption('fields', 'i', Option::VALUE_OPTIONAL, 'model visible fields', null) ->addOption('fields', 'i', Option::VALUE_OPTIONAL, 'model visible fields', null)
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override or force delete,without tips', null) ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override or force delete,without tips', null)
->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 0)
->addOption('relation', 'r', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table name without prefix', null) ->addOption('relation', 'r', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table name without prefix', null)
->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation model name', null) ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation model name', null)
->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation foreign key', null) ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation foreign key', null)
@ -241,6 +241,8 @@ class Crud extends Command
//模块 //模块
$moduleName = 'admin'; $moduleName = 'admin';
$moduleNameUser = 'user';
$userPath = $adminPath . '../' . $moduleNameUser . DS;
$modelModuleName = $local ? $moduleName : 'common'; $modelModuleName = $local ? $moduleName : 'common';
$validateModuleName = $local ? $moduleName : 'common'; $validateModuleName = $local ? $moduleName : 'common';
@ -316,6 +318,7 @@ class Crud extends Command
//控制器 //控制器
list($controllerNamespace, $controllerName, $controllerFile, $controllerArr) = $this->getControllerData($moduleName, $controller, $table); list($controllerNamespace, $controllerName, $controllerFile, $controllerArr) = $this->getControllerData($moduleName, $controller, $table);
list($controllerNamespace, $controllerName, $controllerFileUser, $controllerArrUser) = $this->getControllerData($moduleNameUser, $controller, $table);
//模型 //模型
list($modelNamespace, $modelName, $modelFile, $modelArr) = $this->getModelData($modelModuleName, $model, $table); list($modelNamespace, $modelName, $modelFile, $modelArr) = $this->getModelData($modelModuleName, $model, $table);
//验证器 //验证器
@ -323,21 +326,29 @@ class Crud extends Command
$controllerUrl = strtolower(implode('/', $controllerArr)); $controllerUrl = strtolower(implode('/', $controllerArr));
$controllerBaseName = strtolower(implode(DS, $controllerArr)); $controllerBaseName = strtolower(implode(DS, $controllerArr));
$controllerUrlUser = strtolower(implode('/', $controllerArrUser));
$controllerBaseNameUser = strtolower(implode(DS, $controllerArrUser));
//视图文件 //视图文件
$viewDir = $adminPath . 'view' . DS . $controllerBaseName . DS; $viewDir = $adminPath . 'view' . DS . $controllerBaseName . DS;
$viewDirUser = $userPath . 'view' . DS . $controllerBaseNameUser . DS;
//最终将生成的文件路径 //最终将生成的文件路径
$javascriptFile = ROOT_PATH . 'public' . DS . 'assets' . DS . 'js' . DS . 'backend' . DS . $controllerBaseName . '.js'; $javascriptFile = ROOT_PATH . 'public' . DS . 'assets' . DS . 'js' . DS . 'backend' . DS . $controllerBaseName . '.js';
$javascriptFileUser = ROOT_PATH . 'public' . DS . 'assets' . DS . 'js' . DS . 'userend' . DS . $controllerBaseName . '.js';
$addFile = $viewDir . 'add.html'; $addFile = $viewDir . 'add.html';
$editFile = $viewDir . 'edit.html'; $editFile = $viewDir . 'edit.html';
$indexFile = $viewDir . 'index.html'; $indexFile = $viewDir . 'index.html';
$langFile = $adminPath . 'lang' . DS . Lang::detect() . DS . $controllerBaseName . '.php'; $addFileUser = $viewDirUser . 'add.html';
$editFileUser = $viewDirUser . 'edit.html';
$indexFileUser = $viewDirUser . 'index.html';
//语言包共用
$langFile = APP_PATH . 'lang' . DS . Lang::detect() . DS . $controllerBaseName . '.php';
//是否为删除模式 //是否为删除模式
$delete = $input->getOption('delete'); $delete = $input->getOption('delete');
if ($delete) { if ($delete) {
$readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $langFile, $javascriptFile]; $readyFiles = [$controllerFile,$controllerFileUser, $modelFile, $validateFile, $addFile, $editFile, $indexFile,$addFileUser, $editFileUser, $indexFileUser, $langFile, $javascriptFile, $javascriptFileUser];
foreach ($readyFiles as $k => $v) { foreach ($readyFiles as $k => $v) {
$output->warning($v); $output->warning($v);
} }
@ -369,7 +380,10 @@ class Crud extends Command
if (is_file($controllerFile) && !$force) { if (is_file($controllerFile) && !$force) {
throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true "); throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true ");
} }
if (is_file($controllerFileUser) && !$force) {
throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true ");
}
//非覆盖模式时如果存在模型文件则报错 //非覆盖模式时如果存在模型文件则报错
if (is_file($modelFile) && !$force) { if (is_file($modelFile) && !$force) {
throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true "); throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true ");
@ -381,7 +395,7 @@ class Crud extends Command
} }
require $adminPath . 'common.php'; require $adminPath . 'common.php';
//从数据库中获取表字段信息 //从数据库中获取表字段信息
$sql = "SELECT * FROM `information_schema`.`columns` " $sql = "SELECT * FROM `information_schema`.`columns` "
. "WHERE TABLE_SCHEMA = ? AND table_name = ? " . "WHERE TABLE_SCHEMA = ? AND table_name = ? "
@ -478,6 +492,7 @@ class Crud extends Command
$appendAttrList = []; $appendAttrList = [];
$controllerAssignList = []; $controllerAssignList = [];
$headingHtml = '{:build_heading()}'; $headingHtml = '{:build_heading()}';
$headingJs = '';
//循环所有字段,开始构造视图的HTML和JS信息 //循环所有字段,开始构造视图的HTML和JS信息
foreach ($columnList as $k => $v) { foreach ($columnList as $k => $v) {
@ -695,6 +710,7 @@ class Crud extends Command
} }
if ($this->headingFilterField && $this->headingFilterField == $field && $itemArr) { if ($this->headingFilterField && $this->headingFilterField == $field && $itemArr) {
$headingHtml = $this->getReplacedStub('html/heading-html', ['field' => $field]); $headingHtml = $this->getReplacedStub('html/heading-html', ['field' => $field]);
$headingJs = $this->getReplacedStub('html/heading-js', ['field' => $field]);
} }
//排序方式,如果有指定排序字段,否则按主键排序 //排序方式,如果有指定排序字段,否则按主键排序
$order = $field == $this->sortField ? $this->sortField : $order; $order = $field == $this->sortField ? $this->sortField : $order;
@ -770,6 +786,7 @@ class Crud extends Command
'relationMethodList' => '', 'relationMethodList' => '',
'controllerIndex' => '', 'controllerIndex' => '',
'headingHtml' => $headingHtml, 'headingHtml' => $headingHtml,
'headingJs' => $headingJs,
'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '', 'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(explode(',', $fields))) . "']);" : '',
'appendAttrList' => implode(",\n", $appendAttrList), 'appendAttrList' => implode(",\n", $appendAttrList),
'getEnumList' => implode("\n\n", $getEnumArr), 'getEnumList' => implode("\n\n", $getEnumArr),
@ -826,6 +843,7 @@ class Crud extends Command
// 生成控制器文件 // 生成控制器文件
$result = $this->writeToFile('controller', $data, $controllerFile); $result = $this->writeToFile('controller', $data, $controllerFile);
$result = $this->writeToFile('controller', $data, $controllerFileUser);
// 生成模型文件 // 生成模型文件
$result = $this->writeToFile('model', $data, $modelFile); $result = $this->writeToFile('model', $data, $modelFile);
@ -845,8 +863,12 @@ class Crud extends Command
$result = $this->writeToFile('add', $data, $addFile); $result = $this->writeToFile('add', $data, $addFile);
$result = $this->writeToFile('edit', $data, $editFile); $result = $this->writeToFile('edit', $data, $editFile);
$result = $this->writeToFile('index', $data, $indexFile); $result = $this->writeToFile('index', $data, $indexFile);
$result = $this->writeToFile('add', $data, $addFileUser);
$result = $this->writeToFile('edit', $data, $editFileUser);
$result = $this->writeToFile('index', $data, $indexFileUser);
// 生成JS文件 // 生成JS文件
$result = $this->writeToFile('javascript', $data, $javascriptFile); $result = $this->writeToFile('javascript', $data, $javascriptFile);
$result = $this->writeToFile('javascript', $data, $javascriptFileUser);
// 生成语言文件 // 生成语言文件
if ($langList) { if ($langList) {
$result = $this->writeToFile('lang', $data, $langFile); $result = $this->writeToFile('lang', $data, $langFile);
@ -858,6 +880,7 @@ class Crud extends Command
//继续生成菜单 //继续生成菜单
if ($menu) { if ($menu) {
exec("php think menu -c {$controllerUrl}"); exec("php think menu -c {$controllerUrl}");
exec("php think usermenu -c {$controllerUrlUser}");
} }
$output->info("Build Successed"); $output->info("Build Successed");
@ -1264,7 +1287,7 @@ EOD;
$selectfilter = ' data-mimetype="image/*"'; $selectfilter = ' data-mimetype="image/*"';
} }
$multiple = substr($field, -1) == 's' ? ' data-multiple="true"' : ' data-multiple="false"'; $multiple = substr($field, -1) == 's' ? ' data-multiple="true"' : ' data-multiple="false"';
$preview = ' data-preview-id="p-' . $field . '"'; $preview = $uploadfilter ? ' data-preview-id="p-' . $field . '"' : '';
$previewcontainer = $preview ? '<ul class="row list-inline plupload-preview" id="p-' . $field . '"></ul>' : ''; $previewcontainer = $preview ? '<ul class="row list-inline plupload-preview" id="p-' . $field . '"></ul>' : '';
return <<<EOD return <<<EOD
<div class="input-group"> <div class="input-group">

View File

@ -568,4 +568,48 @@ BEGIN;
INSERT INTO `fa_version` (`id`, `oldversion`, `newversion`, `packagesize`, `content`, `downloadurl`, `enforce`, `createtime`, `updatetime`, `weigh`, `status`) VALUES INSERT INTO `fa_version` (`id`, `oldversion`, `newversion`, `packagesize`, `content`, `downloadurl`, `enforce`, `createtime`, `updatetime`, `weigh`, `status`) VALUES
(1, '1.1.1,2', '1.2.1', '20M', '更新内容', 'https://www.fastadmin.net/download.html', 1, 1520425318, 0, 0, 'normal'); (1, '1.1.1,2', '1.2.1', '20M', '更新内容', 'https://www.fastadmin.net/download.html', 1, 1520425318, 0, 0, 'normal');
COMMIT; COMMIT;
#
# 增加用户日志表
#
DROP TABLE IF EXISTS `fa_user_log`;
CREATE TABLE `fa_user_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`username` varchar(30) NOT NULL DEFAULT '' COMMENT '管理员名字',
`url` varchar(1500) NOT NULL DEFAULT '' COMMENT '操作页面',
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '日志标题',
`content` text NOT NULL COMMENT '内容',
`ip` varchar(50) NOT NULL DEFAULT '' COMMENT 'IP',
`useragent` varchar(255) NOT NULL DEFAULT '' COMMENT 'User-Agent',
`createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '操作时间',
PRIMARY KEY (`id`),
KEY `name` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=322 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户日志';
#
# 增加用户规则表字段
#
ALTER TABLE IF EXISTS `fa_user_rule`
add `icon` char(30) NOT NULL DEFAULT 'fa fa-circle-o' COMMENT '图标',
add `condition` varchar(255) NOT NULL DEFAULT '' COMMENT '条件',
add `type` enum('menu','file') NOT NULL DEFAULT 'file' COMMENT 'menu为菜单,file为权限节点';
#
# 修改用户规则表相应记录
#
BEGIN;
REPLACE INTO `fa_user_rule` (`id`,`pid`,`name`,`title`,`remark`,`ismenu`,`createtime`,`updatetime`,`weigh`,`status`,`icon`,`condition`,`type`) VALUES
(1,0,'dashboard','Dashboard','',1,1516168079,1540951909,99,'normal','fa fa-dashboard','','file'),
(2,0,'api','API接口','',1,1516168062,1540953038,2,'hidden','fa fa-circle-o','','file'),
(3,0,'user','会员中心','',1,1515386221,1540952296,12,'normal','fa fa-user-o','','file'),
(4,2,'api/user','会员模块','',1,1515386221,1516168092,11,'hidden','fa fa-circle-o','','file'),
(5,3,'user/changepwd','修改密码','',0,1515386262,1516015236,7,'normal','fa fa-circle-o','','file'),
(6,3,'user/scorelog','积分日志','',0,1515386247,1515386247,5,'normal','fa fa-mobile-o','','file'),
(7,3,'user/index','View','',0,1516015012,1516015012,10,'normal','fa fa-circle-o','','file');
INSERT INTO `fa_user_rule` (`id`,`pid`,`name`,`title`,`remark`,`ismenu`,`createtime`,`updatetime`,`weigh`,`status`,`icon`,`condition`,`type`) VALUES
(13,1,'dashboard/index','View','',0,1540952067,1540952079,13,'normal','fa fa-circle-o','','file'),
(14,3,'user/userlog','用户日志','',0,1515386247,1515386247,5,'normal','fa fa-email-o','','file');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,289 @@
<?php
namespace app\admin\command;
use app\common\model\UserRule;
use ReflectionClass;
use ReflectionMethod;
use think\Cache;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
class Usermenu extends Command
{
protected $model = null;
protected function configure()
{
$this
->setName('usermenu')
->addOption('controller', 'c', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name,use \'all-controller\' when build all menu', null)
->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '')
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force delete menu,without tips', null)
->addOption('equal', 'e', Option::VALUE_OPTIONAL, 'the controller must be equal', null)
->setDescription('Build auth menu from controller');
//要执行的controller必须一样不适用模糊查询
}
protected function execute(Input $input, Output $output)
{
$this->model = new UserRule();
$userPath = dirname(__DIR__) . '../user' . DS;
//控制器名
$controller = $input->getOption('controller') ?: '';
if (!$controller) {
throw new Exception("please input controller name");
}
$force = $input->getOption('force');
//是否为删除模式
$delete = $input->getOption('delete');
//是否控制器完全匹配
$equal = $input->getOption('equal');
if ($delete) {
if (in_array('all-controller', $controller)) {
throw new Exception("could not delete all menu");
}
$ids = [];
$list = $this->model->where(function ($query) use ($controller, $equal) {
foreach ($controller as $index => $item) {
if ($equal)
$query->whereOr('name', 'eq', $item);
else
$query->whereOr('name', 'like', strtolower($item) . "%");
}
})->select();
foreach ($list as $k => $v) {
$output->warning($v->name);
$ids[] = $v->id;
}
if (!$ids) {
throw new Exception("There is no menu to delete");
}
if (!$force) {
$output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: ");
$line = fgets(STDIN);
if (trim($line) != 'yes') {
throw new Exception("Operation is aborted!");
}
}
UserRule::destroy($ids);
Cache::rm("__usermenu__");
$output->info("Delete Successed");
return;
}
if (!in_array('all-controller', $controller)) {
foreach ($controller as $index => $item) {
$controllerArr = explode('/', $item);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
$userPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php';
if (!is_file($userPath)) {
$output->error("controller not found");
return;
}
$this->importRule($item);
}
} else {
$this->model->where('id', '>', 0)->delete();
$controllerDir = $userPath . 'controller' . DS;
// 扫描新的节点信息并导入
$treelist = $this->import($this->scandir($controllerDir));
}
Cache::rm("__usermenu__");
$output->info("Build Successed!");
}
/**
* 递归扫描文件夹
* @param string $dir
* @return array
*/
public function scandir($dir)
{
$result = [];
$cdir = scandir($dir);
foreach ($cdir as $value) {
if (!in_array($value, array(".", ".."))) {
if (is_dir($dir . DS . $value)) {
$result[$value] = $this->scandir($dir . DS . $value);
} else {
$result[] = $value;
}
}
}
return $result;
}
/**
* 导入规则节点
* @param array $dirarr
* @param array $parentdir
* @return array
*/
public function import($dirarr, $parentdir = [])
{
$menuarr = [];
foreach ($dirarr as $k => $v) {
if (is_array($v)) {
//当前是文件夹
$nowparentdir = array_merge($parentdir, [$k]);
$this->import($v, $nowparentdir);
} else {
//只匹配PHP文件
if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) {
continue;
}
//导入文件
$controller = ($parentdir ? implode('/', $parentdir) . '/' : '') . $matchone[1];
$this->importRule($controller);
}
}
return $menuarr;
}
protected function importRule($controller)
{
$controller = str_replace('\\', '/', $controller);
$controllerArr = explode('/', $controller);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
$classSuffix = Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
$className = "\\app\\user\\controller\\" . implode("\\", $controllerArr) . $classSuffix;
$pathArr = $controllerArr;
array_unshift($pathArr, '', 'application', 'user', 'controller');
$classFile = ROOT_PATH . implode(DS, $pathArr) . $classSuffix . ".php";
$classContent = file_get_contents($classFile);
$uniqueName = uniqid("FastAdmin") . $classSuffix;
$classContent = str_replace("class " . $controllerArr[$key] . $classSuffix . " ", 'class ' . $uniqueName . ' ', $classContent);
$classContent = preg_replace("/namespace\s(.*);/", 'namespace ' . __NAMESPACE__ . ";", $classContent);
//临时的类文件
$tempClassFile = __DIR__ . DS . $uniqueName . ".php";
file_put_contents($tempClassFile, $classContent);
$className = "\\app\\admin\\command\\" . $uniqueName;
//反射机制调用类的注释和方法名
$reflector = new ReflectionClass($className);
if (isset($tempClassFile)) {
//删除临时文件
@unlink($tempClassFile);
}
//只匹配公共的方法
$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
$classComment = $reflector->getDocComment();
//判断是否有启用软删除
$softDeleteMethods = ['destroy', 'restore', 'recyclebin'];
$withSofeDelete = false;
preg_match_all("/\\\$this\->model\s*=\s*model\('(\w+)'\);/", $classContent, $matches);
if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) {
\think\Request::instance()->module('user');
$model = model($matches[1][0]);
if (in_array('trashed', get_class_methods($model))) {
$withSofeDelete = true;
}
}
//忽略的类
if (stripos($classComment, "@internal") !== FALSE) {
return;
}
preg_match_all('#(@.*?)\n#s', $classComment, $annotations);
$controllerIcon = 'fa fa-circle-o';
$controllerRemark = '';
//判断注释中是否设置了icon值
if (isset($annotations[1])) {
foreach ($annotations[1] as $tag) {
if (stripos($tag, '@icon') !== FALSE) {
$controllerIcon = substr($tag, stripos($tag, ' ') + 1);
}
if (stripos($tag, '@remark') !== FALSE) {
$controllerRemark = substr($tag, stripos($tag, ' ') + 1);
}
}
}
//过滤掉其它字符
$controllerTitle = trim(preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $classComment));
//导入中文语言包
\think\Lang::load(dirname(__DIR__) . DS . 'lang/zh-cn.php');
//先导入菜单的数据
$pid = 0;
foreach ($controllerArr as $k => $v) {
$key = $k + 1;
$name = strtolower(implode('/', array_slice($controllerArr, 0, $key)));
$title = (!isset($controllerArr[$key]) ? $controllerTitle : '');
$icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list');
$remark = (!isset($controllerArr[$key]) ? $controllerRemark : '');
$title = $title ? $title : $v;
$rulemodel = $this->model->get(['name' => $name]);
if (!$rulemodel) {
$this->model
->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal'])
->isUpdate(false)
->save();
$pid = $this->model->id;
} else {
$pid = $rulemodel->id;
}
}
$ruleArr = [];
foreach ($methods as $m => $n) {
//过滤特殊的类
if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') {
continue;
}
//未启用软删除时过滤相关方法
if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) {
continue;
}
//只匹配符合的方法
if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) {
unset($methods[$m]);
continue;
}
$comment = $reflector->getMethod($n->name)->getDocComment();
//忽略的方法
if (stripos($comment, "@internal") !== FALSE) {
continue;
}
//过滤掉其它字符
$comment = preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $comment);
$title = $comment ? $comment : ucfirst($n->name);
//获取主键作为UserRule更新依据
$id = $this->getUserRulePK($name . "/" . strtolower($n->name));
$ruleArr[] = array('id' => $id, 'pid' => $pid, 'name' => $name . "/" . strtolower($n->name), 'icon' => 'fa fa-circle-o', 'title' => $title, 'ismenu' => 0, 'status' => 'normal');
}
$this->model->isUpdate(false)->saveAll($ruleArr);
}
//获取主键
protected function getUserRulePK($name)
{
if (!empty($name)) {
$id = $this->model
->where('name', $name)
->value('id');
return $id ? $id : null;
}
}
}

View File

@ -13,6 +13,7 @@
return [ return [
'app\admin\command\Crud', 'app\admin\command\Crud',
'app\admin\command\Menu', 'app\admin\command\Menu',
'app\admin\command\Usermenu',
'app\admin\command\Install', 'app\admin\command\Install',
'app\admin\command\Min', 'app\admin\command\Min',
'app\admin\command\Addon', 'app\admin\command\Addon',

View File

@ -12,7 +12,7 @@ use think\Session;
/** /**
* 后台控制器基类 * 后台控制器基类
*/ */
class Backend extends Controller class Backend extends Base
{ {
/** /**
@ -211,163 +211,6 @@ class Backend extends Controller
$this->assign('admin', Session::get('admin')); $this->assign('admin', Session::get('admin'));
} }
/**
* 加载语言文件
* @param string $name
*/
protected function loadlang($name)
{
Lang::load(APP_PATH . $this->request->module() . '/lang/' . $this->request->langset() . '/' . str_replace('.', '/', $name) . '.php');
}
/**
* 渲染配置信息
* @param mixed $name 键名或数组
* @param mixed $value
*/
protected function assignconfig($name, $value = '')
{
$this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]);
}
/**
* 生成查询所需要的条件,排序方式
* @param mixed $searchfields 快速查询的字段
* @param boolean $relationSearch 是否关联查询
* @return array
*/
protected function buildparams($searchfields = null, $relationSearch = null)
{
$searchfields = is_null($searchfields) ? $this->searchFields : $searchfields;
$relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
$search = $this->request->get("search", '');
$filter = $this->request->get("filter", '');
$op = $this->request->get("op", '', 'trim');
$sort = $this->request->get("sort", "id");
$order = $this->request->get("order", "DESC");
$offset = $this->request->get("offset", 0);
$limit = $this->request->get("limit", 0);
$filter = (array)json_decode($filter, TRUE);
$op = (array)json_decode($op, TRUE);
$filter = $filter ? $filter : [];
$where = [];
$tableName = '';
if ($relationSearch) {
if (!empty($this->model)) {
$name = \think\Loader::parseName(basename(str_replace('\\', '/', get_class($this->model))));
$tableName = $name . '.';
}
$sortArr = explode(',', $sort);
foreach ($sortArr as $index => & $item) {
$item = stripos($item, ".") === false ? $tableName . trim($item) : $item;
}
unset($item);
$sort = implode(',', $sortArr);
}
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$where[] = [$tableName . $this->dataLimitField, 'in', $adminIds];
}
if ($search) {
$searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
foreach ($searcharr as $k => &$v) {
$v = stripos($v, ".") === false ? $tableName . $v : $v;
}
unset($v);
$where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
}
foreach ($filter as $k => $v) {
$sym = isset($op[$k]) ? $op[$k] : '=';
if (stripos($k, ".") === false) {
$k = $tableName . $k;
}
$v = !is_array($v) ? trim($v) : $v;
$sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
switch ($sym) {
case '=':
case '!=':
$where[] = [$k, $sym, (string)$v];
break;
case 'LIKE':
case 'NOT LIKE':
case 'LIKE %...%':
case 'NOT LIKE %...%':
$where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{$v}%"];
break;
case '>':
case '>=':
case '<':
case '<=':
$where[] = [$k, $sym, intval($v)];
break;
case 'FINDIN':
case 'FINDINSET':
case 'FIND_IN_SET':
$where[] = "FIND_IN_SET('{$v}', " . ($relationSearch ? $k : '`' . str_replace('.', '`.`', $k) . '`') . ")";
break;
case 'IN':
case 'IN(...)':
case 'NOT IN':
case 'NOT IN(...)':
$where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)];
break;
case 'BETWEEN':
case 'NOT BETWEEN':
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr))
continue;
//当出现一边为空时改变操作符
if ($arr[0] === '') {
$sym = $sym == 'BETWEEN' ? '<=' : '>';
$arr = $arr[1];
} else if ($arr[1] === '') {
$sym = $sym == 'BETWEEN' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, $sym, $arr];
break;
case 'RANGE':
case 'NOT RANGE':
$v = str_replace(' - ', ',', $v);
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr))
continue;
//当出现一边为空时改变操作符
if ($arr[0] === '') {
$sym = $sym == 'RANGE' ? '<=' : '>';
$arr = $arr[1];
} else if ($arr[1] === '') {
$sym = $sym == 'RANGE' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' time', $arr];
break;
case 'LIKE':
case 'LIKE %...%':
$where[] = [$k, 'LIKE', "%{$v}%"];
break;
case 'NULL':
case 'IS NULL':
case 'NOT NULL':
case 'IS NOT NULL':
$where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
break;
default:
break;
}
}
$where = function ($query) use ($where) {
foreach ($where as $k => $v) {
if (is_array($v)) {
call_user_func_array([$query, 'where'], $v);
} else {
$query->where($v);
}
}
};
return [$where, $sort, $order, $offset, $limit];
}
/** /**
* 获取数据限制的管理员ID * 获取数据限制的管理员ID
* 禁用数据限制时返回的是null * 禁用数据限制时返回的是null

View File

@ -0,0 +1,234 @@
<?php
namespace app\common\controller;
use think\Controller;
use think\Lang;
/**
* 项目公共控制器
* @package app\common\controller
*/
class Base extends Controller
{
public function __construct()
{
parent::__construct();
}
/**
* 初始化
*/
protected function _initialize()
{
parent::_initialize();
define('M', request()->module());
define('C', request()->controller());
define('A', request()->action());
// 编辑器图片上传路径
//defined('UPLOAD_PATH') or define('UPLOAD_PATH', 'upload/');
//根据首页的主题通过cookie传递参数设置其它页面的主题
$p = strripos(A,'index');
if($p !== false){
$N = substr(A,$p+1);
//config('template.tpl_replace_string.THEME',$n);
cookie('theme',A);
}else{
cookie('theme','');
}
// 后台公共模板
//$this->assign('_admin_base_layout', config('admin_base_layout'));
// 当前配色方案
$this->assign('system_color', config('site.system_color'));
// 输出弹出层参数
$this->assign('_pop', $this->request->param('_pop'));
}
/**
* 空的请求
*/
public function _empty()
{
$this->error("我们正在努力,敬请期待……");
//return $this->view->fetch();
}
/**
* 加载语言文件
* @param string $name
*/
protected function loadlang($name)
{
Lang::load(APP_PATH . $this->request->module() . '/lang/' . $this->request->langset() . '/' . str_replace('.', '/', $name) . '.php');
//加载共用语言包
Lang::load(APP_PATH . 'lang/' . $this->request->langset() . '/' . str_replace('.', '/', $name) . '.php');
}
/**
* 渲染配置信息
* @param mixed $name 键名或数组
* @param mixed $value
*/
protected function assignconfig($name, $value = '')
{
$this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]);
}
/**
* 生成查询所需要的条件,排序方式
* @param mixed $searchfields 快速查询的字段
* @param boolean $relationSearch 是否关联查询
* @return array
*/
protected function buildparams($searchfields = null, $relationSearch = null)
{
$searchfields = is_null($searchfields) ? $this->searchFields : $searchfields;
$relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
$search = $this->request->get("search", '');
$filter = $this->request->get("filter", '');
$op = $this->request->get("op", '', 'trim');
$sort = $this->request->get("sort", "id");
$order = $this->request->get("order", "DESC");
$offset = $this->request->get("offset", 0);
$limit = $this->request->get("limit", 0);
$filter = json_decode($filter, TRUE);
$op = json_decode($op, TRUE);
$filter = $filter ? $filter : [];
$where = [];
$tableName = '';
if ($relationSearch)
{
if (!empty($this->model))
{
$tableName = $this->model->getQuery()->getTable() . ".";
}
$sort = stripos($sort, ".") === false ? $tableName . $sort : $sort;
}
$adminIds = $this->getDataLimitUserIds();
if (is_array($adminIds))
{
$where[] = [$tableName . $this->dataLimitField, 'in', $adminIds];
}
if ($search)
{
$searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
foreach ($searcharr as $k => &$v)
{
$v = stripos($v, ".") === false ? $tableName . $v : $v;
}
unset($v);
$where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
}
foreach ($filter as $k => $v)
{
$sym = isset($op[$k]) ? $op[$k] : '=';
if (stripos($k, ".") === false)
{
$k = $tableName . $k;
}
$v = !is_array($v) ? trim($v) : $v;
$sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
switch ($sym)
{
case '=':
case '!=':
$where[] = [$k, $sym, (string) $v];
break;
case 'LIKE':
case 'NOT LIKE':
case 'LIKE %...%':
case 'NOT LIKE %...%':
$where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{$v}%"];
break;
case '>':
case '>=':
case '<':
case '<=':
$where[] = [$k, $sym, intval($v)];
break;
case 'FINDIN':
case 'FIND_IN_SET':
$where[] = "FIND_IN_SET('{$v}', `{$k}`)";
break;
case 'IN':
case 'IN(...)':
case 'NOT IN':
case 'NOT IN(...)':
$where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)];
break;
case 'BETWEEN':
case 'NOT BETWEEN':
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr))
continue;
//当出现一边为空时改变操作符
if ($arr[0] === '')
{
$sym = $sym == 'BETWEEN' ? '<=' : '>';
$arr = $arr[1];
}
else if ($arr[1] === '')
{
$sym = $sym == 'BETWEEN' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, $sym, $arr];
break;
case 'RANGE':
case 'NOT RANGE':
$v = str_replace(' - ', ',', $v);
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr))
continue;
//当出现一边为空时改变操作符
if ($arr[0] === '')
{
$sym = $sym == 'RANGE' ? '<=' : '>';
$arr = $arr[1];
}
else if ($arr[1] === '')
{
$sym = $sym == 'RANGE' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' time', $arr];
break;
case 'LIKE':
case 'LIKE %...%':
$where[] = [$k, 'LIKE', "%{$v}%"];
break;
case 'NULL':
case 'IS NULL':
case 'NOT NULL':
case 'IS NOT NULL':
$where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
break;
default:
break;
}
}
$where = function($query) use ($where) {
foreach ($where as $k => $v)
{
if (is_array($v))
{
call_user_func_array([$query, 'where'], $v);
}
else
{
$query->where($v);
}
}
};
return [$where, $sort, $order, $offset, $limit];
}
}

View File

@ -11,7 +11,7 @@ use think\Lang;
/** /**
* 前台控制器基类 * 前台控制器基类
*/ */
class Frontend extends Controller class Frontend extends Base
{ {
/** /**
@ -115,23 +115,4 @@ class Frontend extends Controller
$this->assign('config', $config); $this->assign('config', $config);
} }
/**
* 加载语言文件
* @param string $name
*/
protected function loadlang($name)
{
Lang::load(APP_PATH . $this->request->module() . '/lang/' . $this->request->langset() . '/' . str_replace('.', '/', $name) . '.php');
}
/**
* 渲染配置信息
* @param mixed $name 键名或数组
* @param mixed $value
*/
protected function assignconfig($name, $value = '')
{
$this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]);
}
} }

View File

@ -0,0 +1,335 @@
<?php
namespace app\common\controller;
use app\common\library\UserAuth as Auth;
use think\Config;
use app\common\Controller\Common;
use think\Hook;
use think\Lang;
use think\Session;
/**
* 用户端控制器基类
*/
class Userend extends Base
{
/**
* 无需登录的方法,同时也就不需要鉴权了
* @var array
*/
protected $noNeedLogin = ['login', 'register', 'third'];
/**
* 无需鉴权的方法,但需要登录
* @var array
*/
protected $noNeedRight = ['index','logout'];
/**
* 布局模板
* @var string
*/
protected $layout = 'default';
/**
* 权限Auth
* @var Auth
*/
protected $auth = null;
/**
* 模型对象
* @var \think\Model
*/
protected $model = null;
/**
* 快速搜索时执行查找的字段
*/
protected $searchFields = 'id';
/**
* 是否是关联查询
*/
protected $relationSearch = false;
/**
* 是否开启数据限制
* 支持auth/personal
* 表示按权限判断/仅限个人
* 默认为禁用,若启用请务必保证表中存在user_id字段
*/
protected $dataLimit = false;
/**
* 数据限制字段
*/
protected $dataLimitField = 'user_id';
/**
* 数据限制开启时自动填充限制字段值
*/
protected $dataLimitFieldAutoFill = true;
/**
* 是否开启Validate验证
*/
protected $modelValidate = false;
/**
* 是否开启模型场景验证
*/
protected $modelSceneValidate = false;
/**
* Multi方法可批量修改的字段
*/
protected $multiFields = 'status';
/**
* Selectpage可显示的字段
*/
protected $selectpageFields = '*';
/**
* 导入文件首行类型
* 支持comment/name
* 表示注释或字段名
*/
protected $importHeadType = 'comment';
protected $modulename = '';
protected $controllername = '';
protected $actionname = '';
protected $config = [];
/**
* 引入会员控制器的traits
*/
use \app\common\library\traits\Userend;
public function _initialize()
{
parent::_initialize();
//移除HTML标签
$this->request->filter('strip_tags');
$this->modulename = $this->request->module();
$this->controllername = strtolower($this->request->controller());
$this->actionname = strtolower($this->request->action());
//$path = str_replace('.', '/', $this->controllername) . '/' . $this->actionname;
$path = $this->controllername . '/' . $this->actionname;
// 定义是否Addtabs请求
!defined('IS_ADDTABS') && define('IS_ADDTABS', input("addtabs") ? TRUE : FALSE);
// 定义是否Dialog请求
!defined('IS_DIALOG') && define('IS_DIALOG', input("dialog") ? TRUE : FALSE);
// 定义是否AJAX请求
!defined('IS_AJAX') && define('IS_AJAX', $this->request->isAjax());
$this->auth = Auth::instance();
// token
$token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token')));
// 设置当前请求的URI
$this->auth->setRequestUri($path);
// 检测是否需要验证登录
if (!$this->auth->match($this->noNeedLogin)) {
//初始化
$this->auth->init($token);
//检测是否登录
if (!$this->auth->isLogin()) {
$this->error(__('Please login first'), 'index/user/login');
}
// 判断是否需要验证权限
if (!$this->auth->match($this->noNeedRight)) {
// 判断控制器和方法判断是否有对应权限
if (!$this->auth->check($path)) {
$this->error(__('You have no permission'));
}
}
} else {
// 如果有传递token才验证是否登录状态
if ($token) {
$this->auth->init($token);
}
}
// 非选项卡时重定向
if (!$this->request->isPost() && !IS_AJAX && !IS_ADDTABS && !IS_DIALOG && input("ref") == 'addtabs')
{
$url = preg_replace_callback("/([\?|&]+)ref=addtabs(&?)/i", function($matches) {
return $matches[2] == '&' ? $matches[1] : '';
}, $this->request->url());
if (Config::get('url_domain_deploy'))
{
if (stripos($url, $this->request->server('SCRIPT_NAME')) === 0)
{
$url = substr($url, strlen($this->request->server('SCRIPT_NAME')));
}
$url = url($url, '', false);
}
$this->redirect('user/index/index', [], 302, ['referer' => $url]);
exit;
}
// 设置面包屑导航数据
$breadcrumb = $this->auth->getBreadCrumb($path);
array_pop($breadcrumb);
$this->assign('breadcrumb', $breadcrumb);
// 如果有使用模板布局
if ($this->layout) {
$this->view->engine->layout('layout/' . $this->layout);
}
// 语言检测
$lang = strip_tags($this->request->langset());
$site = Config::get("site");
$upload = \app\common\model\Config::upload();
// 上传信息配置后
Hook::listen("upload_config_init", $upload);
// 配置信息
$this->config = [
'site' => array_intersect_key($site, array_flip(['name', 'indexurl', 'cdnurl', 'version', 'timezone', 'languages'])),
'upload' => $upload,
'modulename' => $this->modulename,
'controllername' => $this->controllername,
'actionname' => $this->actionname,
'jsname' => 'userend/'.str_replace('.', '/', $this->controllername),
'moduleurl' => rtrim(url("/{$this->modulename}", '', false), '/'),
'language' => $lang,
'fastadmin' => Config::get('fastadmin'),
'referer' => Session::get("referer"),
];
$this->config = array_merge($this->config, Config::get("view_replace_str"));
Config::set('upload', array_merge(Config::get('upload'), $upload));
// 配置信息后
Hook::listen("config_init", $this->config);
// 加载当前控制器语言包
$this->loadlang($this->controllername);
$this->assign('site', $site);
$this->assign('config', $this->config);
//渲染权限对象
$this->assign('auth', $this->auth);
$this->assign('user', $this->auth->getUser());
}
/**
* 获取数据限制的会员ID
* 禁用数据限制时返回的是null
* @return mixed
*/
protected function getDataLimitUserIds()
{
if (!$this->dataLimit)
{
return null;
}
$userIds = [];
if (in_array($this->dataLimit, ['auth', 'personal']))
{
//$userIds = $this->dataLimit == 'auth' ? $this->auth->getChildrenUserIds(true) : [$this->auth->id];
$userIds = [$this->auth->id];
}
return $userIds;
}
/**
* Selectpage的实现方法
*
* 当前方法只是一个比较通用的搜索匹配,请按需重载此方法来编写自己的搜索逻辑,$where按自己的需求写即可
* 这里示例了所有的参数,所以比较复杂,实现上自己实现只需简单的几行即可
*
*/
protected function selectpage()
{
//设置过滤方法
$this->request->filter(['strip_tags', 'htmlspecialchars']);
//搜索关键词,客户端输入以空格分开,这里接收为数组
$word = (array) $this->request->request("q_word/a");
//当前页
$page = $this->request->request("pageNumber");
//分页大小
$pagesize = $this->request->request("pageSize");
//搜索条件
$andor = $this->request->request("andOr");
//排序方式
$orderby = (array) $this->request->request("orderBy/a");
//显示的字段
$field = $this->request->request("showField");
//主键
$primarykey = $this->request->request("keyField");
//主键值
$primaryvalue = $this->request->request("keyValue");
//搜索字段
$searchfield = (array) $this->request->request("searchField/a");
//自定义搜索条件
$custom = (array) $this->request->request("custom/a");
$order = [];
foreach ($orderby as $k => $v)
{
$order[$v[0]] = $v[1];
}
$field = $field ? $field : 'name';
//如果有primaryvalue,说明当前是初始化传值
if ($primaryvalue !== null)
{
$where = [$primarykey => ['in', $primaryvalue]];
}
else
{
$where = function($query) use($word, $andor, $field, $searchfield, $custom) {
foreach ($word as $k => $v)
{
foreach ($searchfield as $m => $n)
{
$query->where($n, "like", "%{$v}%", $andor);
}
}
if ($custom && is_array($custom))
{
foreach ($custom as $k => $v)
{
$query->where($k, '=', $v);
}
}
};
}
/*
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds))
{
$this->model->where($this->dataLimitField, 'in', $adminIds);
}
*/
$list = [];
$total = $this->model->where($where)->count();
if ($total > 0)
{
/*if (is_array($adminIds))
{
$this->model->where($this->dataLimitField, 'in', $adminIds);
}*/
$list = $this->model->where($where)
->order($order)
->page($page, $pagesize)
->field("{$primarykey},{$field}")
//->field("password,salt", true)
->select();
}
//这里一定要返回有list这个字段,total是可选的,如果total<=list的数量,则会隐藏分页按钮
return json(['list' => $list, 'total' => $total]);
}
}

View File

@ -0,0 +1,787 @@
<?php
namespace app\common\library;
use app\common\model\User;
use app\common\model\UserRule;
use fast\Random;
use think\Config;
use think\Db;
use think\Hook;
use think\Request;
use think\Validate;
use fast\Tree;
use think\Session;
class UserAuth
{
protected static $instance = null;
protected $_error = '';
protected $_logined = FALSE;
protected $_user = NULL;
protected $_token = '';
//Token默认有效时长
protected $keeptime = 2592000;
protected $requestUri = '';
protected $rules = [];
//默认配置
protected $config = [];
protected $options = [];
protected $allowFields = ['id', 'username', 'nickname', 'mobile', 'avatar', 'score'];
//权限菜单
protected $menus = [];
//面包屑导航
protected $breadcrumb = [];
public function __construct($options = [])
{
if ($config = Config::get('user'))
{
$this->options = array_merge($this->config, $config);
}
$this->options = array_merge($this->config, $options);
}
/**
*
* @param array $options 参数
* @return Auth
*/
public static function instance($options = [])
{
if (is_null(self::$instance))
{
self::$instance = new static($options);
}
return self::$instance;
}
/**
* 获取User模型
* @return User
*/
public function getUser()
{
return $this->_user;
}
/**
* 兼容调用user模型的属性
*
* @param string $name
* @return mixed
*/
public function __get($name)
{
return $this->_user ? $this->_user->$name : NULL;
}
/**
* 根据Token初始化
*
* @param string $token Token
* @return boolean
*/
public function init($token)
{
if ($this->_logined)
{
return TRUE;
}
if ($this->_error)
return FALSE;
$data = Token::get($token);
if (!$data)
{
return FALSE;
}
$user_id = intval($data['user_id']);
if ($user_id > 0)
{
$user = User::get($user_id);
if (!$user)
{
$this->setError('Account not exist');
return FALSE;
}
if ($user['status'] != 'normal')
{
$this->setError('Account is locked');
return FALSE;
}
$this->_user = $user;
$this->_logined = TRUE;
$this->_token = $token;
//初始化成功的事件
Hook::listen("user_init_successed", $this->_user);
return TRUE;
}
else
{
$this->setError('You are not logged in');
return FALSE;
}
}
/**
* 注册用户
*
* @param string $username 用户名
* @param string $password 密码
* @param string $email 邮箱
* @param string $mobile 手机号
* @param array $extend 扩展参数
* @return boolean
*/
public function register($username, $password, $email = '', $mobile = '', $extend = [])
{
// 检测用户名或邮箱、手机号是否存在
if (User::getByUsername($username))
{
$this->setError('Username already exist');
return FALSE;
}
if ($email && User::getByEmail($email))
{
$this->setError('Email already exist');
return FALSE;
}
if ($mobile && User::getByMobile($mobile))
{
$this->setError('Mobile already exist');
return FALSE;
}
$ip = request()->ip();
$time = time();
$data = [
'username' => $username,
'password' => $password,
'email' => $email,
'mobile' => $mobile,
'level' => 1,
'score' => 0,
'avatar' => '',
];
$params = array_merge($data, [
'nickname' => $username,
'salt' => Random::alnum(),
'jointime' => $time,
'joinip' => $ip,
'logintime' => $time,
'loginip' => $ip,
'prevtime' => $time,
'status' => 'normal'
]);
$params['password'] = $this->getEncryptPassword($password, $params['salt']);
$params = array_merge($params, $extend);
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS)
{
$uc = new \addons\ucenter\library\client\Client();
$user_id = $uc->uc_user_register($username, $password, $email);
// 如果小于0则说明发生错误
if ($user_id <= 0)
{
$this->setError($user_id > -4 ? 'Username is incorrect' : 'Email is incorrect');
return FALSE;
}
else
{
$params['id'] = $user_id;
}
}
//账号注册时需要开启事务,避免出现垃圾数据
Db::startTrans();
try
{
$user = User::create($params);
Db::commit();
// 此时的Model中只包含部分数据
$this->_user = User::get($user->id);
//设置Token
$this->_token = Random::uuid();
Token::set($this->_token, $user->id, $this->keeptime);
//注册成功的事件
Hook::listen("user_register_successed", $this->_user);
return TRUE;
}
catch (Exception $e)
{
$this->setError($e->getMessage());
Db::rollback();
return FALSE;
}
}
/**
* 用户登录
*
* @param string $account 账号,用户名、邮箱、手机号
* @param string $password 密码
* @return boolean
*/
public function login($account, $password)
{
$field = Validate::is($account, 'email') ? 'email' : (Validate::regex($account, '/^1\d{10}$/') ? 'mobile' : 'username');
$user = User::get([$field => $account]);
if (!$user)
{
$this->setError('Account is incorrect');
return FALSE;
}
if ($user->status != 'normal')
{
$this->setError('Account is locked');
return FALSE;
}
if ($user->password != $this->getEncryptPassword($password, $user->salt))
{
$this->setError('Password is incorrect');
return FALSE;
}
//直接登录会员
$this->direct($user->id);
return TRUE;
}
/**
* 注销
*
* @return boolean
*/
public function logout()
{
if (!$this->_logined)
{
$this->setError('You are not logged in');
return false;
}
//设置登录标识
$this->_logined = FALSE;
//删除Token
Token::delete($this->_token);
//注销成功的事件
Hook::listen("user_logout_successed", $this->_user);
return TRUE;
}
/**
* 修改密码
* @param string $newpassword 新密码
* @param string $oldpassword 旧密码
* @param bool $ignoreoldpassword 忽略旧密码
* @return boolean
*/
public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
{
if (!$this->_logined)
{
$this->setError('You are not logged in');
return false;
}
//判断旧密码是否正确
if ($this->_user->password == $this->getEncryptPassword($oldpassword, $this->_user->salt) || $ignoreoldpassword)
{
$salt = Random::alnum();
$newpassword = $this->getEncryptPassword($newpassword, $salt);
$this->_user->save(['password' => $newpassword, 'salt' => $salt]);
Token::delete($this->_token);
//修改密码成功的事件
Hook::listen("user_changepwd_successed", $this->_user);
return true;
}
else
{
$this->setError('Password is incorrect');
return false;
}
}
/**
* 直接登录账号
* @param int $user_id
* @return boolean
*/
public function direct($user_id)
{
$user = User::get($user_id);
if ($user)
{
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS)
{
$uc = new \addons\ucenter\library\client\Client();
$re = $uc->uc_user_login($this->user->id, $this->user->password . '#split#' . $this->user->salt, 3);
// 如果小于0则说明发生错误
if ($re <= 0)
{
$this->setError('Username or password is incorrect');
return FALSE;
}
}
$ip = request()->ip();
$time = time();
//判断连续登录和最大连续登录
if ($user->logintime < \fast\Date::unixtime('day'))
{
$user->successions = $user->logintime < \fast\Date::unixtime('day', -1) ? 1 : $user->successions + 1;
$user->maxsuccessions = max($user->successions, $user->maxsuccessions);
}
$user->prevtime = $user->logintime;
//记录本次登录的IP和时间
$user->loginip = $ip;
$user->logintime = $time;
$user->save();
//查询用户信息之后, 查询用户的登记昵称
$levelId = $user['level'];
$levelName = Db::name("user_level")->where("level_id", $levelId)->column("level_name");
$user['level_name'] = $levelName;
$this->_user = $user;
$this->_token = Random::uuid();
Token::set($this->_token, $user->id, $this->keeptime);
$this->_logined = TRUE;
//登录成功的事件
Hook::listen("user_login_successed", $this->_user);
return TRUE;
}
else
{
return FALSE;
}
}
/**
* 检测是否是否有对应权限
* @param string $path 控制器/方法
* @param string $module 模块 默认为当前模块
* @return boolean
*/
public function check($path = NULL, $module = NULL)
{
if (!$this->_logined)
return false;
$ruleList = $this->getRuleList();
$rules = [];
foreach ($ruleList as $k => $v)
{
$rules[] = $v['name'];
}
//$url = ($module ? $module : request()->module()) . '/' . (is_null($path) ? $this->getRequestUri() : $path);
$url = is_null($path) ? $this->getRequestUri() : $path;
//$url = strtolower(str_replace('.', '/', $url));
return in_array($url, $rules) ? TRUE : FALSE;
}
/**
* 判断是否登录
* @return boolean
*/
public function isLogin()
{
if ($this->_logined)
{
return true;
}
return false;
}
/**
* 获取当前Token
* @return string
*/
public function getToken()
{
return $this->_token;
}
/**
* 获取会员基本信息
*/
public function getUserinfo()
{
$data = $this->_user->toArray();
$allowFields = $this->getAllowFields();
$userinfo = array_intersect_key($data, array_flip($allowFields));
$userinfo = array_merge($userinfo, Token::get($this->_token));
return $userinfo;
}
/**
* 获取会员组别规则列表
* @return array
*/
public function getRuleList()
{
if ($this->rules) return $this->rules;
$group = $this->_user->group;
if (!$group) return [];
$rules = explode(',', $group->rules);
$this->rules = collection(UserRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,name,title,ismenu')->select())->toArray();
return $this->rules;
}
/**
* 获取当前请求的URI
* @return string
*/
public function getRequestUri()
{
return $this->requestUri;
}
/**
* 设置当前请求的URI
* @param string $uri
*/
public function setRequestUri($uri)
{
$this->requestUri = $uri;
}
/**
* 获取允许输出的字段
* @return array
*/
public function getAllowFields()
{
return $this->allowFields;
}
/**
* 设置允许输出的字段
* @param array $fields
*/
public function setAllowFields($fields)
{
$this->allowFields = $fields;
}
/**
* 删除一个指定会员
* @param int $user_id 会员ID
* @return boolean
*/
public function delete($user_id)
{
$user = User::get($user_id);
if (!$user)
{
return FALSE;
}
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS)
{
$uc = new \addons\ucenter\library\client\Client();
$re = $uc->uc_user_delete($user['id']);
// 如果小于0则说明发生错误
if ($re <= 0)
{
$this->setError('Account is locked');
return FALSE;
}
}
// 调用事务删除账号
$result = Db::transaction(function($db) use($user_id) {
// 删除会员
User::destroy($user_id);
// 删除会员指定的所有Token
Token::clear($user_id);
return TRUE;
});
if ($result)
{
Hook::listen("user_delete_successed", $user);
}
return $result ? TRUE : FALSE;
}
/**
* 获取密码加密后的字符串
* @param string $password 密码
* @param string $salt 密码盐
* @return string
*/
public function getEncryptPassword($password, $salt = '')
{
return md5(md5($password) . $salt);
}
/**
* 检测当前控制器和方法是否匹配传递的数组
*
* @param array $arr 需要验证权限的数组
* @return boolean
*/
public function match($arr = [])
{
$request = Request::instance();
$arr = is_array($arr) ? $arr : explode(',', $arr);
if (!$arr)
{
return FALSE;
}
$arr = array_map('strtolower', $arr);
// 是否存在
if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr))
{
return TRUE;
}
// 没找到匹配
return FALSE;
}
/**
* 设置会话有效时间
* @param int $keeptime 默认为永久
*/
public function keeptime($keeptime = 0)
{
$this->keeptime = $keeptime;
}
/**
* 渲染用户数据
* @param array $datalist 二维数组
* @param mixed $fields 加载的字段列表
* @param string $fieldkey 渲染的字段
* @param string $renderkey 结果字段
* @return array
*/
public function render(&$datalist, $fields = [], $fieldkey = 'user_id', $renderkey = 'userinfo')
{
$fields = !$fields ? ['id', 'nickname', 'level', 'avatar'] : (is_array($fields) ? $fields : explode(',', $fields));
$ids = [];
foreach ($datalist as $k => $v)
{
if (!isset($v[$fieldkey]))
continue;
$ids[] = $v[$fieldkey];
}
$list = [];
if ($ids)
{
if (!in_array('id', $fields))
{
$fields[] = 'id';
}
$ids = array_unique($ids);
$selectlist = User::where('id', 'in', $ids)->column($fields);
foreach ($selectlist as $k => $v)
{
$list[$v['id']] = $v;
}
}
foreach ($datalist as $k => &$v)
{
$v[$renderkey] = isset($list[$v[$fieldkey]]) ? $list[$v[$fieldkey]] : NULL;
}
unset($v);
return $datalist;
}
/**
* 设置错误信息
*
* @param $error 错误信息
* @return Auth
*/
public function setError($error)
{
$this->_error = $error;
return $this;
}
/**
* 获取错误信息
* @return string
*/
public function getError()
{
return $this->_error ? __($this->_error) : '';
}
/**
* 获得面包屑导航
* @param string $path
* @return array
*/
public function getBreadCrumb($path = '')
{
if ($this->breadcrumb || !$path) return $this->breadcrumb;
$path_rule_id = 0;
foreach ($this->rules as $rule) {
$path_rule_id = $rule['name'] == $path ? $rule['id'] : $path_rule_id;
}
if ($path_rule_id) {
$this->breadcrumb = Tree::instance()->init($this->rules)->getParents($path_rule_id, true);
foreach ($this->breadcrumb as $k => &$v) {
$v['url'] = url($v['name']);
$v['title'] = __($v['title']);
}
}
return $this->breadcrumb;
}
/**
* 获取左侧菜单栏
*
* @param array $params URL对应的badge数据
* @return string
*/
public function getSidebar($params = [], $fixedPage = 'dashboard')
{
$colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple'];
$colorNums = count($colorArr);
$badgeList = [];
$module = request()->module();
// 生成菜单的badge
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';
}
else {
$nums = $v;
$color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums];
$class = 'label';
}
//必须nums大于0才显示
if ($nums) {
$badgeList[$url] = '<small class="' . $class . ' pull-right bg-' . $color . '">' . $nums . '</small>';
}
}
// 读取用户当前拥有的权限节点
$userRule = $this->getRuleList();
$selected = $referer = [];
$refererUrl = Session::get('referer');
$pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader');
// 必须将结果集转换为数组
//$ruleList = collection(UserRule::where('status', 'normal')->where('ismenu', 1)->order('weigh', 'desc')->select())->toArray();//->cache("__usermenu__")
$ruleList = $this->getMenuList();
foreach ($ruleList as $k => &$v)
{
/*if (!in_array($v['name'], $userRule)) {
unset($ruleList[$k]);
continue;
}*/
$v['icon'] = $v['icon'] . ' fa-fw';
$v['url'] = '/' . $module . '/' . $v['name'];
$v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : '';
$v['py'] = $pinyin->abbr($v['title'], '');
$v['pinyin'] = $pinyin->permalink($v['title'], '');
$v['title'] = __($v['title']);
//$select_id = $v['name'] == $fixedPage ? $v['id'] : $select_id;
$selected = $v['name'] == $fixedPage ? $v : $selected;
$referer = url($v['url']) == $refererUrl ? $v : $referer;
}
if ($selected == $referer) {
$referer = [];
}
$selected && $selected['url'] = url($selected['url']);
$referer && $referer['url'] = url($referer['url']);
$select_id = $selected ? $selected['id'] : 0;
$menu = $nav = '';
//是否启用多级菜单导航
if (Config::get('fastadmin.multiplenav')) {
$topList = [];
foreach ($ruleList as $index => $item) {
if (!$item['pid']) {
$topList[] = $item;
}
}
$selectParentIds = [];
$tree = Tree::instance();
$tree->init($ruleList);
if ($select_id) {
$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"');
$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);
$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"');
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>';
}
if ($referer) {
$nav .= '<li role="presentation" id="tab_' . $referer['id'] . '" class="active"><a href="#con_' . $referer['id'] . '" node-id="' . $referer['id'] . '" aria-controls="' . $referer['id'] . '" role="tab" data-toggle="tab"><i class="' . $referer['icon'] . ' fa-fw"></i> <span>' . $referer['title'] . '</span> </a> <i class="close-tab fa fa-remove"></i></li>';
}
}
return [$menu, $nav, $selected, $referer];
// 构造菜单数据
//$usertree = new Tree();
//$usertree->init($ruleList);
//$usermenu = $usertree->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>'."\n", $select_id, '', 'ul', 'class="treeview-menu"');
//return $usermenu;
}
/**
* 获取会员组别规则菜单
* @return array
*/
public function getMenuList()
{
if ($this->menus) return $this->menus;
$group = $this->_user->group;
if (!$group) return [];
$rules = explode(',', $group->rules);
$this->menus = collection(UserRule::where('status', 'normal')->where('id', 'in', $rules)->where('ismenu', 1)
->order('weigh', 'desc')->select())->toArray();//->field('id,pid,name,title,ismenu,status,icon')
return $this->menus;
}
/**
* 获取等级
* @return array
public function getRank()
{
if ($this->_logined){//登录才能获取等级
$rank = $this->_user->level;
if (!$rank) {
return false;
}
return UserRanks::get( $rank);
}
}
*/
}

View File

@ -0,0 +1,422 @@
<?php
namespace app\common\library\traits;
trait Userend
{
/**
* 查看
*/
public function index()
{
//设置过滤方法
$this->request->filter(['strip_tags']);
if ($this->request->isAjax())
{
//如果发送的来源是Selectpage则转发到Selectpage
if ($this->request->request('pkey_name'))
{
return $this->selectpage();
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$fields = $this->model->getQuery()->getTableInfo('', 'fields');
if(in_array("user_id", $fields)){
$total = $this->model
->where($where)
->where( 'user_id',$this->auth->id)
->order($sort, $order)
->count();
$list = $this->model
->where($where)
->where( 'user_id',$this->auth->id)
->order($sort, $order)
->limit($offset, $limit)
->select();
}elseif(in_array("shop_id", $fields)){
$total = $this->model
->where($where)
->where( 'shop_id',$this->auth->shopId)
->order($sort, $order)
->count();
$list = $this->model
->where($where)
->where( 'shop_id',$this->auth->shopId)
->order($sort, $order)
->limit($offset, $limit)
->select();
}else{
$total = $this->model
->where($where)
->order($sort, $order)
->count();
$list = $this->model
->where($where)
->order($sort, $order)
->limit($offset, $limit)
->select();
}
$list = collection($list)->toArray();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 回收站
*/
public function recyclebin()
{
//设置过滤方法
$this->request->filter(['strip_tags']);
if ($this->request->isAjax())
{
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$list = $this->model
->onlyTrashed()
->where($where)
->order($sort, $order)
->limit($offset, $limit)
->select();
$result = array("total" => count($list), "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost())
{
$params = $this->request->post("row/a");
if ($params)
{
if ($this->dataLimit && $this->dataLimitFieldAutoFill)
{
$params[$this->dataLimitField] = $this->auth->id;
}
try
{
//将按钮中的状态参数加入记录中
$params['status'] = input('status',0);
//是否采用模型验证
if ($this->modelValidate)
{
$name = basename(str_replace('\\', '/', get_class($this->model)));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate;
$this->model->validate($validate);
}
$result = $this->model->allowField(true)->save($params);
if ($result !== false)
{
$this->success();
}
else
{
$this->error($this->model->getError());
}
}
catch (\think\exception\PDOException $e)
{
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = NULL)
{
$row = $this->model->get($ids);
if (!$row)
$this->error(__('No Results were found'));
$userIds = $this->getDataLimitUserIds();
if (is_array($userIds))
{
if (!in_array($row[$this->dataLimitField], $userIds))
{
$this->error(__('You have no permission'));
}
}
if ($this->request->isPost())
{
$params = $this->request->post("row/a");
if ($params)
{
try
{
//将按钮中的状态参数加入记录中
$params['status'] = input('status',0);
//是否采用模型验证
if ($this->modelValidate)
{
$name = basename(str_replace('\\', '/', get_class($this->model)));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : true) : $this->modelValidate;
$row->validate($validate);
}
$result = $row->allowField(true)->save($params);
if ($result !== false)
{
$this->success();
}
else
{
$this->error($row->getError());
}
}
catch (\think\exception\PDOException $e)
{
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 删除
*/
public function del($ids = "")
{
if($this->request->param("table")) $this->model = model($this->request->param("table"));
if ($ids)
{
$pk = $this->model->getPk();
$userIds = $this->getDataLimitUserIds();
if (is_array($userIds))
{
$count = $this->model->where($this->dataLimitField, 'in', $userIds);
}
$list = $this->model->where($pk, 'in', $ids)->select();
$count = 0;
foreach ($list as $k => $v)
{
$count += $v->delete();
}
if ($count)
{
$this->success();
}
else
{
$this->error(__('No rows were deleted'));
}
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 真实删除
*/
public function destroy($ids = "")
{
$pk = $this->model->getPk();
$userIds = $this->getDataLimitUserIds();
if (is_array($userIds))
{
$count = $this->model->where($this->dataLimitField, 'in', $userIds);
}
if ($ids)
{
$this->model->where($pk, 'in', $ids);
}
$count = 0;
$list = $this->model->onlyTrashed()->select();
foreach ($list as $k => $v)
{
$count += $v->delete(true);
}
if ($count)
{
$this->success();
}
else
{
$this->error(__('No rows were deleted'));
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 还原
*/
public function restore($ids = "")
{
$pk = $this->model->getPk();
$userIds = $this->getDataLimitUserIds();
if (is_array($userIds))
{
$this->model->where($this->dataLimitField, 'in', $userIds);
}
if ($ids)
{
$this->model->where($pk, 'in', $ids);
}
$count = $this->model->restore('1=1');
if ($count)
{
$this->success();
}
$this->error(__('No rows were updated'));
}
/**
* 批量更新
*/
public function multi($ids = "")
{
if($this->request->param("table")) $this->model = model($this->request->param("table"));
$ids = $ids ? $ids : $this->request->param("ids");
if ($ids)
{
if ($this->request->has('params'))
{
parse_str($this->request->post("params"), $values);
$values = array_intersect_key($values, array_flip(is_array($this->multiFields) ? $this->multiFields : explode(',', $this->multiFields)));
if ($values)
{
$userIds = $this->getDataLimitUserIds();
if (is_array($userIds))
{
$where[]=[$this->dataLimitField, 'in', $userIds];
}
$where[]=[$this->model->getPk(), 'in', $ids];
$count = $this->model->where($where)->update($values);
if ($count)
{
$this->success();
}
else
{
$this->error(__('No rows were updated'));
}
}
else
{
$this->error(__('You have no permission'));
}
}
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
/**
* 导入
*/
protected function import()
{
$file = $this->request->request('file');
if (!$file)
{
$this->error(__('Parameter %s can not be empty', 'file'));
}
$filePath = ROOT_PATH . DS . 'public' . DS . $file;
if (!is_file($filePath))
{
$this->error(__('No results were found'));
}
$PHPReader = new \PHPExcel_Reader_Excel2007();
if (!$PHPReader->canRead($filePath))
{
$PHPReader = new \PHPExcel_Reader_Excel5();
if (!$PHPReader->canRead($filePath))
{
$PHPReader = new \PHPExcel_Reader_CSV();
if (!$PHPReader->canRead($filePath))
{
$this->error(__('Unknown data format'));
}
}
}
//导入文件首行类型,默认是注释,如果需要使用字段名称请使用name
$importHeadType = isset($this->importHeadType) ? $this->importHeadType : 'comment';
$table = $this->model->getQuery()->getTable();
$database = \think\Config::get('database.database');
$fieldArr = [];
$list = db()->query("SELECT COLUMN_NAME,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?", [$table, $database]);
foreach ($list as $k => $v)
{
if ($importHeadType == 'comment')
{
$fieldArr[$v['COLUMN_COMMENT']] = $v['COLUMN_NAME'];
}
else
{
$fieldArr[$v['COLUMN_NAME']] = $v['COLUMN_NAME'];
}
}
$PHPExcel = $PHPReader->load($filePath); //加载文件
$currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表
$allColumn = $currentSheet->getHighestDataColumn(); //取得最大的列号
$allRow = $currentSheet->getHighestRow(); //取得一共有多少行
$maxColumnNumber = \PHPExcel_Cell::columnIndexFromString($allColumn);
for ($currentRow = 1; $currentRow <= 1; $currentRow++)
{
for ($currentColumn = 0; $currentColumn < $maxColumnNumber; $currentColumn++)
{
$val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue();
$fields[] = $val;
}
}
$insert = [];
for ($currentRow = 2; $currentRow <= $allRow; $currentRow++)
{
$values = [];
for ($currentColumn = 0; $currentColumn < $maxColumnNumber; $currentColumn++)
{
$val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue();
$values[] = is_null($val) ? '' : $val;
}
$row = [];
$temp = array_combine($fields, $values);
foreach ($temp as $k => $v)
{
if (isset($fieldArr[$k]) && $k !== '')
{
$row[$fieldArr[$k]] = $v;
}
}
if ($row)
{
$insert[] = $row;
}
}
if (!$insert)
{
$this->error(__('No rows were updated'));
}
try
{
$this->model->saveAll($insert);
}
catch (\think\exception\PDOException $exception)
{
$this->error($exception->getMessage());
}
$this->success();
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace app\common\model;
use app\common\library\UserAuth as Auth;
use think\Model;
class UserLog extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = '';
//自定义日志标题
protected static $title = '';
//自定义日志内容
protected static $content = '';
public static function setTitle($title)
{
self::$title = $title;
}
public static function setContent($content)
{
self::$content = $content;
}
public static function record($title = '')
{
$auth = Auth::instance();
$user_id = $auth->isLogin() ? $auth->id : 0;
$username = $auth->isLogin() ? $auth->username : __('Unknown');
$content = self::$content;
if (!$content)
{
$content = request()->param();
foreach ($content as $k => $v)
{
if (is_string($v) && strlen($v) > 200 || stripos($k, 'password') !== false)
{
unset($content[$k]);
}
}
}
$title = self::$title;
if (!$title)
{
$title = [];
$breadcrumb = Auth::instance()->getBreadcrumb();
foreach ($breadcrumb as $k => $v)
{
$title[] = $v['title'];
}
$title = implode(' ', $title);
}
self::create([
'title' => $title,
'content' => !is_scalar($content) ? json_encode($content) : $content,
'url' => request()->url(),
'user_id' => $user_id,
'username' => $username,
'useragent' => request()->server('HTTP_USER_AGENT'),
'ip' => request()->ip()
]);
}
public function user()
{
return $this->belongsTo('User', 'user_id')->setEagerlyType(0);
}
}

View File

@ -2,20 +2,28 @@
namespace app\common\model; namespace app\common\model;
use think\Cache;
use think\Model; use think\Model;
class UserRule extends Model class UserRule extends Model
{ {
// 表名
protected $name = 'user_rule';
// 自动写入时间戳字段 // 自动写入时间戳字段
protected $autoWriteTimestamp = 'int'; protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名 // 定义时间戳字段名
protected $createTime = 'createtime'; protected $createTime = 'createtime';
protected $updateTime = 'updatetime'; protected $updateTime = 'updatetime';
// 追加属性
protected $append = [ protected static function init()
]; {
self::afterWrite(function ($row) {
Cache::rm('__menu__');
});
}
public function getTitleAttr($value, $data)
{
return __($value);
}
} }

View File

@ -18,9 +18,9 @@ return [
// 应用命名空间 // 应用命名空间
'app_namespace' => 'app', 'app_namespace' => 'app',
// 应用调试模式 // 应用调试模式
'app_debug' => Env::get('app.debug', false), 'app_debug' => Env::get('app.debug', true),
// 应用Trace // 应用Trace
'app_trace' => Env::get('app.trace', false), 'app_trace' => Env::get('app.trace', true),
// 应用模式状态 // 应用模式状态
'app_status' => '', 'app_status' => '',
// 是否支持多模块 // 是否支持多模块

View File

@ -18,11 +18,11 @@ return [
// 服务器地址 // 服务器地址
'hostname' => Env::get('database.hostname', '127.0.0.1'), 'hostname' => Env::get('database.hostname', '127.0.0.1'),
// 数据库名 // 数据库名
'database' => Env::get('database.database', 'fastadmin'), 'database' => Env::get('database.database', 'fastadmin_edit'),
// 用户名 // 用户名
'username' => Env::get('database.username', 'root'), 'username' => Env::get('database.username', 'root'),
// 密码 // 密码
'password' => Env::get('database.password', ''), 'password' => Env::get('database.password', 'root'),
// 端口 // 端口
'hostport' => Env::get('database.hostport', ''), 'hostport' => Env::get('database.hostport', ''),
// 连接dsn // 连接dsn

View File

@ -26,7 +26,7 @@ class Ajax extends Frontend
$controllername = input("controllername"); $controllername = input("controllername");
$this->loadlang($controllername); $this->loadlang($controllername);
//强制输出JSON Object //强制输出JSON Object
$result = 'define(' . json_encode(Lang::get(), JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE) . ');'; $result = jsonp(Lang::get(), 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]);
return $result; return $result;
} }

View File

@ -58,7 +58,7 @@ class User extends Frontend
* @param $name * @param $name
* @return mixed * @return mixed
*/ */
public function _empty($name) public function _empty($name='')
{ {
$data = Hook::listen("user_request_empty", $name); $data = Hook::listen("user_request_empty", $name);
foreach ($data as $index => $datum) { foreach ($data as $index => $datum) {
@ -130,7 +130,7 @@ class User extends Frontend
$uc = new \addons\ucenter\library\client\Client(); $uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synregister($this->auth->id, $password); $synchtml = $uc->uc_user_synregister($this->auth->id, $password);
} }
$this->success(__('Sign up successful') . $synchtml, $url ? $url : url('user/index')); $this->success(__('Sign up successful') . $synchtml, $url ? $url : url('user/index/index'));
} else { } else {
$this->error($this->auth->getError(), null, ['token' => $this->request->token()]); $this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
} }
@ -138,7 +138,7 @@ class User extends Frontend
//判断来源 //判断来源
$referer = $this->request->server('HTTP_REFERER'); $referer = $this->request->server('HTTP_REFERER');
if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host())) if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
&& !preg_match("/(user\/login|user\/register)/i", $referer)) { && !preg_match("/(index\/user\/login|index\/user\/register)/i", $referer)) {
$url = $referer; $url = $referer;
} }
$this->view->assign('url', $url); $this->view->assign('url', $url);
@ -189,7 +189,7 @@ class User extends Frontend
$uc = new \addons\ucenter\library\client\Client(); $uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synlogin($this->auth->id); $synchtml = $uc->uc_user_synlogin($this->auth->id);
} }
$this->success(__('Logged in successful') . $synchtml, $url ? $url : url('user/index')); $this->success(__('Logged in successful') . $synchtml, $url ? $url : url('user/index/index'));
} else { } else {
$this->error($this->auth->getError(), null, ['token' => $this->request->token()]); $this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
} }
@ -197,7 +197,7 @@ class User extends Frontend
//判断来源 //判断来源
$referer = $this->request->server('HTTP_REFERER'); $referer = $this->request->server('HTTP_REFERER');
if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host())) if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
&& !preg_match("/(user\/login|user\/register)/i", $referer)) { && !preg_match("/(index\/user\/login|index\/user\/register)/i", $referer)) {
$url = $referer; $url = $referer;
} }
$this->view->assign('url', $url); $this->view->assign('url', $url);
@ -218,7 +218,7 @@ class User extends Frontend
$uc = new \addons\ucenter\library\client\Client(); $uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synlogout(); $synchtml = $uc->uc_user_synlogout();
} }
$this->success(__('Logout successful') . $synchtml, url('user/index')); $this->success(__('Logout successful') . $synchtml, url('index/index'));
} }
/** /**

View File

@ -12,17 +12,17 @@
<title>FastAdmin - {:__('The fastest framework based on ThinkPHP5 and Bootstrap')}</title> <title>FastAdmin - {:__('The fastest framework based on ThinkPHP5 and Bootstrap')}</title>
<!-- Bootstrap Core CSS --> <!-- Bootstrap Core CSS -->
<link href="//cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="//cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="__CDN__/assets/css/index.css" rel="stylesheet"> <link href="__CDN__/assets/css/index.css" rel="stylesheet">
<!-- Plugin CSS --> <!-- Plugin CSS -->
<link href="//cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"> <link href="//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="//cdn.jsdelivr.net/npm/simple-line-icons@2.4.1/css/simple-line-icons.min.css" rel="stylesheet"> <link href="//cdn.bootcss.com/simple-line-icons/2.4.1/css/simple-line-icons.min.css" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="//cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script> <script src="//cdn.bootcss.com/html5shiv/3.7.0/html5shiv.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script> <script src="//cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
@ -64,7 +64,7 @@
<h1>FastAdmin</h1> <h1>FastAdmin</h1>
<h3>{:__('The fastest framework based on ThinkPHP5 and Bootstrap')}</h3> <h3>{:__('The fastest framework based on ThinkPHP5 and Bootstrap')}</h3>
<a href="{:url('admin/index/login')}" class="btn btn-warning btn-xl page-scroll">{:__('Go to Dashboard')}</a> <a href="{:url('admin/index/login')}" class="btn btn-warning btn-xl page-scroll">{:__('Go to Dashboard')}</a>
<a href="{:url('index/user/index')}" class="btn btn-outline btn-xl page-scroll">{:__('Go to Member center')}</a> <a href="{:url('user/index/index')}" class="btn btn-outline btn-xl page-scroll">{:__('Go to Member center')}</a>
</div> </div>
</div> </div>
</div> </div>
@ -163,13 +163,13 @@
</footer> </footer>
<!-- jQuery --> <!-- jQuery -->
<script src="//cdn.jsdelivr.net/npm/jquery@2.1.4/dist/jquery.min.js"></script> <script src="//cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript --> <!-- Bootstrap Core JavaScript -->
<script src="//cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script> <script src="//cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- Plugin JavaScript --> <!-- Plugin JavaScript -->
<script src="//cdn.jsdelivr.net/npm/jquery.easing@1.4.1/jquery.easing.min.js"></script> <script src="//cdn.bootcss.com/jquery-easing/1.4.1/jquery.easing.min.js"></script>
<script> <script>
$(function () { $(function () {

View File

@ -0,0 +1,13 @@
<?php
return [
'Title' => 'Title',
'Auth tips' => 'Unlimited parent-child permission grouping, administrator can belong to multiple groups at the same time',
'Responsive tips' => 'Based on Bootstrap and AdminLTE, mobile phones, tablets, PCs are automatically adapted',
'Languages tips' => 'Backend and Frontend support, View and JS share the same language package',
'Module tips' => 'RequireJS and Bower for frontend package component management,Composer for backend package component management',
'CRUD tips' => 'One key to generate a controller, model, view and JS file, one key to generate an API document and auth rule',
'Extension tips' => 'Installed and uninstalled directly online plug-in, supporting the command line one key operation',
'Do not hesitate' => 'Do not hesitate',
'Start to act' => 'Start Action',
];

View File

@ -0,0 +1,173 @@
<?php
return [
'User id' => '会员ID',
'Username' => '用户名',
'Nickname' => '昵称',
'Password' => '密码',
'Sign up' => '注 册',
'Sign in' => '登 录',
'Sign out' => '注 销',
'Keep login' => '保持会话',
'Guest' => '游客',
'Welcome' => '%s你好',
'View' => '查看',
'Add' => '添加',
'Edit' => '编辑',
'Del' => '删除',
'Delete' => '删除',
'Import' => '导入',
'Export' => '导出',
'All' => '全部',
'Detail' => '详情',
'Multi' => '批量更新',
'Setting' => '配置',
'Move' => '移动',
'Name' => '名称',
'Status' => '状态',
'Weigh' => '权重',
'Operate' => '操作',
'Warning' => '温馨提示',
'Default' => '默认',
'Article' => '文章',
'Page' => '单页',
'OK' => '确定',
'Apply' => '应用',
'Cancel' => '取消',
'Clear' => '清空',
'Custom Range' => '自定义',
'Today' => '今天',
'Yesterday' => '昨天',
'Last 7 days' => '最近7天',
'Last 30 days' => '最近30天',
'Last month' => '上月',
'This month' => '本月',
'Loading' => '加载中',
'More' => '更多',
'Yes' => '是',
'No' => '否',
'Normal' => '正常',
'Hidden' => '隐藏',
'Locked' => '锁定',
'Submit' => '提交',
'Reset' => '重置',
'Execute' => '执行',
'Close' => '关闭',
'Choose' => '选择',
'Search' => '搜索',
'Refresh' => '刷新',
'Install' => '安装',
'Uninstall' => '卸载',
'First' => '首页',
'Previous' => '上一页',
'Next' => '下一页',
'Last' => '末页',
'None' => '无',
'Home' => '主页',
'Online' => '在线',
'Login' => '登录',
'Logout' => '注销',
'Profile' => '个人资料',
'Index' => '首页',
'Hot' => '热门',
'Recommend' => '推荐',
'Dashboard' => '控制台',
'Upload' => '上传',
'Uploading' => '上传中',
'Code' => '编号',
'Message' => '内容',
'Line' => '行号',
'File' => '文件',
'Menu' => '菜单',
'Type' => '类型',
'Title' => '标题',
'Content' => '内容',
'Append' => '追加',
'Select' => '选择',
'Memo' => '备注',
'Parent' => '父级',
'Params' => '参数',
'Permission' => '权限',
'Check all' => '选中全部',
'Expand all' => '展开全部',
'Begin time' => '开始时间',
'End time' => '结束时间',
'Create time' => '创建时间',
'Update time' => '更新时间',
'Flag' => '标志',
'Drag to sort' => '拖动进行排序',
'Redirect now' => '立即跳转',
'Key' => '键',
'Value' => '值',
'Common search' => '普通搜索',
'Search %s' => '搜索 %s',
'View %s' => '查看 %s',
'%d second%s ago' => '%d秒前',
'%d minute%s ago' => '%d分钟前',
'%d hour%s ago' => '%d小时前',
'%d day%s ago' => '%d天前',
'%d week%s ago' => '%d周前',
'%d month%s ago' => '%d月前',
'%d year%s ago' => '%d年前',
'Set to normal' => '设为正常',
'Set to hidden' => '设为隐藏',
//提示
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Click to search %s' => '点击搜索 %s',
'Click to toggle' => '点击切换',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
'Unknown data format' => '未知的数据格式!',
'Network error' => '网络错误!',
'Invalid parameters' => '未知参数',
'No results were found' => '记录未找到',
'No rows were deleted' => '未删除任何行',
'No rows were updated' => '未更新任何行',
'Parameter %s can not be empty' => '参数%s不能为空',
'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?',
'Are you sure you want to delete this item?' => '确定删除此项?',
'Are you sure you want to delete or turncate?' => '确定删除或清空?',
'You have no permission' => '你没有权限访问',
'Please enter your username' => '请输入你的用户名',
'Please enter your password' => '请输入你的密码',
'Please login first' => '请登录后操作',
'You can upload up to %d file%s' => '你最多还可以上传%d个文件',
'You can choose up to %d file%s' => '你最多还可以选择%d个文件',
'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中',
'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转',
//菜单
'Dashboard' => '控制台',
'General' => '常规管理',
'Category' => '分类管理',
'Addon' => '插件管理',
'Auth' => '权限管理',
'Config' => '系统配置',
'Attachment' => '附件管理',
'Profile' => '个人配置',
'Admin' => '管理员管理',
'Admin log' => '管理员日志',
'Group' => '角色组',
'Rule' => '菜单规则',
'User' => '会员管理',
'User group' => '会员分组',
'User rule' => '会员规则',
'Select attachment' => '选择附件',
'Update profile' => '更新个人信息',
'Local install' => '本地安装',
'Update state' => '禁用启用',
'Admin group' => '超级管理组',
'Second group' => '二级管理组',
'Third group' => '三级管理组',
'Second group 2' => '二级管理组2',
'Third group 2' => '三级管理组2',
'Dashboard tips' => '用于展示当前系统中的统计数据、统计报表及重要实时数据',
'Config tips' => '可以在此增改系统的变量和分组,也可以自定义分组和变量,如果需要删除请从数据库中删除',
'Category tips' => '用于统一管理网站的所有分类,分类可进行无限级分类',
'Attachment tips' => '主要用于管理上传到服务器或第三方存储的数据',
'Addon tips' => '可在线安装、卸载、禁用、启用插件同时支持添加本地插件。FastAdmin已上线插件商店 ,你可以发布你的免费或付费插件:<a href="https://www.fastadmin.net/store.html" target="_blank">https://www.fastadmin.net/store.html</a>',
'Admin tips' => '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成',
'Admin log tips' => '管理员可以查看自己所拥有的权限的管理员日志',
'Group tips' => '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员',
'Rule tips' => '规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过命令行进行生成规则节点',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制',
'Uploaded file format is limited' => '上传文件格式受限制',
'Upload successful' => '上传成功',
];

View File

@ -0,0 +1,56 @@
<?php
return [
'Title' => '标题',
'Search menu' => '搜索菜单',
'Layout Options' => '布局设定',
'Fixed Layout' => '固定布局',
'You can\'t use fixed and boxed layouts together' => '盒子模型和固定布局不能同时启作用',
'Boxed Layout' => '盒子布局',
'Activate the boxed layout' => '盒子布局最大宽度将被限定为1250px',
'Toggle Sidebar' => '切换菜单栏',
'Toggle the left sidebar\'s state (open or collapse)' => '切换菜单栏的展示或收起',
'Sidebar Expand on Hover' => '菜单栏自动展开',
'Let the sidebar mini expand on hover' => '鼠标移到菜单栏自动展开',
'Toggle Right Sidebar Slide' => '切换右侧操作栏',
'Toggle between slide over content and push content effects' => '切换右侧操作栏覆盖或独占',
'Toggle Right Sidebar Skin' => '切换右侧操作栏背景',
'Toggle between dark and light skins for the right sidebar' => '将右侧操作栏背景亮色或深色切换',
'Show sub menu' => '显示菜单栏子菜单',
'Always show sub menu' => '菜单栏子菜单将始终显示',
'Disable top menu badge' => '禁用顶部彩色小角标',
'Disable top menu badge without left menu' => '左边菜单栏的彩色小角标不受影响',
'Skins' => '皮肤',
'You\'ve logged in, do not login again' => '你已经登录,无需重复登录',
'Username or password can not be empty' => '用户名密码不能为空',
'Username or password is incorrect' => '用户名或密码不正确',
'Username is incorrect' => '用户名不正确',
'Password is incorrect' => '密码不正确',
'Please try again after 1 day' => '请于1天后再尝试登录',
'Login successful' => '登录成功!',
'Logout successful' => '退出成功!',
'Verification code is incorrect' => '验证码不正确',
'Wipe cache completed' => '清除缓存成功',
'Wipe cache failed' => '清除缓存失败',
'Wipe cache' => '清空缓存',
'Wipe all cache' => '一键清除缓存',
'Wipe content cache' => '清空内容缓存',
'Wipe template cache' => '清除模板缓存',
'Wipe addons cache' => '清除插件缓存',
'Check for updates' => '检测更新',
'Discover new version' => '发现新版本',
'Go to download' => '去下载更新',
'Currently is the latest version' => '当前已经是最新版本',
'Ignore this version' => '忽略此次更新',
'Do not remind again' => '不再提示',
'Your current version' => '你的版本是',
'New version' => '新版本',
'Release notes' => '更新说明',
'Latest news' => '最新消息',
'View more' => '查看更多',
'Links' => '相关链接',
'Docs' => '官方文档',
'Forum' => '交流社区',
'QQ qun' => 'QQ交流群',
'Captcha' => '验证码',
];

View File

@ -0,0 +1,68 @@
<?php
return [
'User center' => '会员中心',
'User log' => '用户日志',
'Register' => '注册',
'Login' => '登录',
'Account' => '账号',
'Mobile' => '手机号',
'Email' => '邮箱',
'Captcha' => '验证码',
'Lv' => 'Lv',
'Score' => '积分',
'Day' => '天',
'Intro' => '个人介绍',
'Successions' => '连续登录',
'Maxsuccessions' => '最长连续登录',
'Logintime' => '登录时间',
'Prevtime' => '最后登录',
'Change' => '修改',
'Click to edit' => '点击编辑',
'Email/Mobile/Username' => '邮箱/手机/用户名',
'Sign up successful' => '注册成功',
'Email active successful' => '邮箱激活成功',
'Username can not be empty' => '用户名不能为空',
'Username must be 3 to 30 characters' => '用户名必须3-30个字符',
'Account must be 3 to 50 characters' => '账户必须3-50个字符',
'Password can not be empty' => '密码不能为空',
'Password must be 6 to 30 characters' => '密码必须6-30个字符',
'Email is incorrect' => '邮箱格式不正确',
'Mobile is incorrect' => '手机格式不正确',
'Username already exist' => '用户名已经存在',
'Email already exist' => '邮箱已经存在',
'Mobile already exist' => '手机号已经存在',
'Username is incorrect' => '用户名不正确',
'Email is incorrect' => '邮箱不正确',
'Reset password' => '修改密码',
'Reset password by email' => '通过邮箱',
'Reset password by mobile' => '通过手机重置',
'Reset password successful' => '修改密码成功',
'Account is locked' => '账户已经被锁定',
'Password is incorrect' => '密码不正确',
'Account is incorrect' => '账户不正确',
'Account not exist' => '账户不存在',
'Account can not be empty' => '账户不能为空',
'Username or password is incorrect' => '用户名或密码不正确',
'You are not logged in' => '你当前还未登录',
'You\'ve logged in, do not login again' => '你已经登录,请不要重复登录',
'This guy hasn\'t written anything yet' => '这个人很懒,啥也没写',
'Profile' => '个人资料',
'Old password' => '旧密码',
'New password' => '新密码',
'Renew password' => '确认新密码',
'Change password' => '修改密码',
'New email' => '新邮箱',
'New mobile' => '新手机号',
'Change password successful' => '修改密码成功',
'Captcha is incorrect' => '验证码不正确',
'Upload successful' => '上传成功',
'Sign up successful' => '注册成功',
'Logged in successful' => '登录成功',
'Logout successful' => '注销成功',
'User center already closed' => '会员中心已经关闭',
'Operation failed' => '操作失败',
'Invalid parameters' => '参数不正确',
'Change password failure' => '修改密码失败',
'Change password successful' => '修改密码成功',
];

View File

@ -0,0 +1,16 @@
<?php
namespace app\user\behavior;
class UserLog
{
public function run($params)
{
if (request()->isPost())
{
\app\common\model\UserLog::record();
}
}
}

View File

@ -0,0 +1,163 @@
<?php
use app\common\model\Category;
use fast\Form;
use fast\Tree;
use think\Db;
if (!function_exists('build_select')) {
/**
* 生成下拉列表
* @param string $name
* @param mixed $options
* @param mixed $selected
* @param mixed $attr
* @return string
*/
function build_select($name, $options, $selected = [], $attr = [])
{
$options = is_array($options) ? $options : explode(',', $options);
$selected = is_array($selected) ? $selected : explode(',', $selected);
return Form::select($name, $options, $selected, $attr);
}
}
if (!function_exists('build_radios')) {
/**
* 生成单选按钮组
* @param string $name
* @param array $list
* @param mixed $selected
* @return string
*/
function build_radios($name, $list = [], $selected = null)
{
$html = [];
$selected = is_null($selected) ? key($list) : $selected;
$selected = is_array($selected) ? $selected : explode(',', $selected);
foreach ($list as $k => $v) {
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
}
return '<div class="radio">' . implode(' ', $html) . '</div>';
}
}
if (!function_exists('build_checkboxs')) {
/**
* 生成复选按钮组
* @param string $name
* @param array $list
* @param mixed $selected
* @return string
*/
function build_checkboxs($name, $list = [], $selected = null)
{
$html = [];
$selected = is_null($selected) ? [] : $selected;
$selected = is_array($selected) ? $selected : explode(',', $selected);
foreach ($list as $k => $v) {
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
}
return '<div class="checkbox">' . implode(' ', $html) . '</div>';
}
}
if (!function_exists('build_category_select')) {
/**
* 生成分类下拉列表框
* @param string $name
* @param string $type
* @param mixed $selected
* @param array $attr
* @return string
*/
function build_category_select($name, $type, $selected = null, $attr = [], $header = [])
{
$tree = Tree::instance();
$tree->init(Category::getCategoryArray($type), 'pid');
$categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name');
$categorydata = $header ? $header : [];
foreach ($categorylist as $k => $v) {
$categorydata[$v['id']] = $v['name'];
}
$attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr);
return build_select($name, $categorydata, $selected, $attr);
}
}
if (!function_exists('build_toolbar')) {
/**
* 生成表格操作按钮栏
* @param array $btns 按钮组
* @param array $attr 按钮属性值
* @return string
*/
function build_toolbar($btns = NULL, $attr = [])
{
$auth = \app\admin\library\Auth::instance();
$controller = str_replace('.', '/', strtolower(think\Request::instance()->controller()));
$btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import'];
$btns = is_array($btns) ? $btns : explode(',', $btns);
$index = array_search('delete', $btns);
if ($index !== FALSE) {
$btns[$index] = 'del';
}
$btnAttr = [
'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', '', __('Refresh')],
'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add'), __('Add')],
'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit'), __('Edit')],
'del' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete'), __('Delete')],
'import' => ['javascript:;', 'btn btn-danger btn-import', 'fa fa-upload', __('Import'), __('Import')],
];
$btnAttr = array_merge($btnAttr, $attr);
$html = [];
foreach ($btns as $k => $v) {
//如果未定义或没有权限
if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}"))) {
continue;
}
list($href, $class, $icon, $text, $title) = $btnAttr[$v];
$extend = $v == 'import' ? 'id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"' : '';
$html[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
}
return implode(' ', $html);
}
}
if (!function_exists('build_heading')) {
/**
* 生成页面Heading
*
* @param string $path 指定的path
* @return string
*/
function build_heading($path = NULL, $container = TRUE)
{
$title = $content = '';
if (is_null($path)) {
$action = request()->action();
$controller = str_replace('.', '/', request()->controller());
$path = strtolower($controller . ($action && $action != 'index' ? '/' . $action : ''));
}
// 根据当前的URI自动匹配父节点的标题和备注
$data = Db::name('auth_rule')->where('name', $path)->field('title,remark')->find();
if ($data) {
$title = __($data['title']);
$content = __($data['remark']);
}
if (!$content)
return '';
$result = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container) {
$result = '<div class="panel-heading">' . $result . '</div>';
}
return $result;
}
}

View File

@ -0,0 +1,16 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
return [
'url_common_param' => true,
'url_html_suffix' => '',
'controller_auto_search' => true,
];

View File

@ -0,0 +1,40 @@
<?php
namespace app\user\controller;
use app\common\controller\Userend;
use think\Lang;
/**
* Ajax异步请求接口
* @internal
*/
class Ajax extends Userend
{
protected $noNeedLogin = ['lang'];
protected $noNeedRight = ['*'];
protected $layout = '';
/**
* 加载语言包
*/
public function lang()
{
header('Content-Type: application/javascript');
$callback = $this->request->get('callback');
$controllername = input("controllername");
$this->loadlang($controllername);
return jsonp(Lang::get(), 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]);
}
/**
* 上传文件
*/
public function upload()
{
return action('api/common/upload');
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace app\user\controller;
use app\common\controller\Userend;
/**
* 控制台
*
* @icon fa fa-dashboard
* @remark 用于展示当前系统中的统计数据、统计报表及重要实时数据
*/
class Dashboard extends Userend
{
/**
* 查看
*/
public function index()
{
$seventtime = \fast\Date::unixtime('day', -7);
$paylist = $createlist = [];
for ($i = 0; $i < 7; $i++)
{
$day = date("Y-m-d", $seventtime + ($i * 86400));
$createlist[$day] = mt_rand(20, 200);
$paylist[$day] = mt_rand(1, mt_rand(1, $createlist[$day]));
}
$hooks = config('addons.hooks');
$uploadmode = isset($hooks['upload_config_init']) && $hooks['upload_config_init'] ? implode(',', $hooks['upload_config_init']) : 'local';
$this->view->assign([
'totaluser' => 35200,
'totalviews' => 219390,
'totalorder' => 32143,
'totalorderamount' => 174800,
'todayuserlogin' => 321,
'todayusersignup' => 430,
'todayorder' => 2324,
'unsettleorder' => 132,
'sevendnu' => '80%',
'sevendau' => '32%',
'paylist' => $paylist,
'createlist' => $createlist,
'uploadmode' => $uploadmode
]);
return $this->view->fetch();
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace app\user\controller;
use app\common\Controller\Userend;
use think\Hook;
use think\Config;
use think\Cookie;
/**
* 会员中心入口文件
*/
class Index extends Userend
{
protected $noNeedLogin = ['login'];
protected $noNeedRight = ['index', 'logout'];
protected $layout = '';
public function _initialize()
{
parent::_initialize();
if (!Config::get('fastadmin.usercenter')) {
$this->error(__('User center already closed'));
}
$auth = $this->auth;
$ucenter = get_addon_info('ucenter');
if ($ucenter && $ucenter['state']) {
include ADDON_PATH . 'ucenter' . DS . 'uc.php';
}
//监听注册登录注销的事件
Hook::add('user_login_successed', function ($user) use ($auth) {
$expire = input('post.keeplogin') ? 30 * 86400 : 0;
Cookie::set('uid', $user->id, $expire);
Cookie::set('token', $auth->getToken(), $expire);
});
Hook::add('user_register_successed', function ($user) use ($auth) {
Cookie::set('uid', $user->id);
Cookie::set('token', $auth->getToken());
});
Hook::add('user_delete_successed', function ($user) use ($auth) {
Cookie::delete('uid');
Cookie::delete('token');
});
Hook::add('user_logout_successed', function ($user) use ($auth) {
Cookie::delete('uid');
Cookie::delete('token');
});
}
/**
* 会员中心
*/
public function index()
{
//左侧菜单
list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([
'dashboard' => 'hot',
], $this->view->site['fixedpage']);
$action = $this->request->request('action');
if ($this->request->isPost()){
if ($action == 'refreshmenu'){
$this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]);
}
}
$this->view->assign('menulist', $menulist);
$this->view->assign('navlist', $navlist);
$this->view->assign('fixedmenu', $fixedmenu);
$this->view->assign('referermenu', $referermenu);
$this->view->assign('title', __('User center'));
return $this->view->fetch();
}
public function login()
{
$this->redirect(url('index/user/login'));
}
public function logout()
{
$this->redirect(url('index/user/logout'));
}
}

View File

@ -0,0 +1,309 @@
<?php
namespace app\user\controller;
use app\common\controller\Userend;
use think\Config;
use think\Cookie;
use think\Hook;
use think\Validate;
/**
* 会员中心入口文件
*/
class User extends Userend
{
protected $noNeedLogin = ['login', 'regist', 'third'];
protected $noNeedRight = ['index','logout'];
public function _initialize()
{
parent::_initialize();
if (!Config::get('fastadmin.usercenter')) {
$this->error(__('User center already closed'));
}
$auth = $this->auth;
$ucenter = get_addon_info('ucenter');
if ($ucenter && $ucenter['state']) {
include ADDON_PATH . 'ucenter' . DS . 'uc.php';
}
//监听注册登录注销的事件
Hook::add('user_login_successed', function ($user) use ($auth) {
$expire = input('post.keeplogin') ? 30 * 86400 : 0;
Cookie::set('uid', $user->id, $expire);
Cookie::set('token', $auth->getToken(), $expire);
});
Hook::add('user_register_successed', function ($user) use ($auth) {
Cookie::set('uid', $user->id);
Cookie::set('token', $auth->getToken());
});
Hook::add('user_delete_successed', function ($user) use ($auth) {
Cookie::delete('uid');
Cookie::delete('token');
});
Hook::add('user_logout_successed', function ($user) use ($auth) {
Cookie::delete('uid');
Cookie::delete('token');
});
}
/**
* 会员中心
*/
public function index()
{
//左侧菜单
list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([
'dashboard' => 'hot',
], $this->view->site['fixedpage']);
$action = $this->request->request('action');
if ($this->request->isPost()){
if ($action == 'refreshmenu'){
$this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]);
}
}
$this->view->assign('menulist', $menulist);
$this->view->assign('navlist', $navlist);
$this->view->assign('fixedmenu', $fixedmenu);
$this->view->assign('referermenu', $referermenu);
$this->view->assign('title', __('User center'));
return $this->view->fetch();
}
/**
* 注册会员
*/
public function regist()
{
$url = $this->request->request('url');
if ($this->auth->id)
$this->success(__('You\'ve logged in, do not login again'), $url);
if ($this->request->isPost()) {
$username = $this->request->post('username');
$password = $this->request->post('password');
$email = $this->request->post('email');
$mobile = $this->request->post('mobile', '');
$captcha = $this->request->post('captcha');
$token = $this->request->post('__token__');
$rule = [
'username' => 'require|length:3,30',
'password' => 'require|length:6,30',
'email' => 'require|email',
'mobile' => 'regex:/^1\d{10}$/',
'captcha' => 'require|captcha',
'__token__' => 'token',
];
$msg = [
'username.require' => 'Username can not be empty',
'username.length' => 'Username must be 3 to 30 characters',
'password.require' => 'Password can not be empty',
'password.length' => 'Password must be 6 to 30 characters',
'captcha.require' => 'Captcha can not be empty',
'captcha.captcha' => 'Captcha is incorrect',
'email' => 'Email is incorrect',
'mobile' => 'Mobile is incorrect',
];
$data = [
'username' => $username,
'password' => $password,
'email' => $email,
'mobile' => $mobile,
'captcha' => $captcha,
'__token__' => $token,
];
$validate = new Validate($rule, $msg);
$result = $validate->check($data);
if (!$result) {
$this->error(__($validate->getError()), null, ['token' => $this->request->token()]);
}
if ($this->auth->register($username, $password, $email, $mobile)) {
$synchtml = '';
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS) {
$uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synregister($this->auth->id, $password);
}
$this->success(__('Sign up successful') . $synchtml, $url ? $url : url('user/index'));
} else {
$this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
}
}
//判断来源
$referer = $this->request->server('HTTP_REFERER');
if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
&& !preg_match("/(user\/login|user\/regist)/i", $referer)) {
$url = $referer;
}
$this->view->assign('url', $url);
$this->view->assign('title', __('Register'));
return $this->view->fetch();
}
/**
* 会员登录
*/
public function login()
{
$url = $this->request->request('url');
if ($this->auth->id)
$this->success(__('You\'ve logged in, do not login again'), $url);
if ($this->request->isPost()) {
$account = $this->request->post('account');
$password = $this->request->post('password');
$keeplogin = (int)$this->request->post('keeplogin');
$token = $this->request->post('__token__');
$rule = [
'account' => 'require|length:3,50',
'password' => 'require|length:6,30',
'__token__' => 'token',
];
$msg = [
'account.require' => 'Account can not be empty',
'account.length' => 'Account must be 3 to 50 characters',
'password.require' => 'Password can not be empty',
'password.length' => 'Password must be 6 to 30 characters',
];
$data = [
'account' => $account,
'password' => $password,
'__token__' => $token,
];
$validate = new Validate($rule, $msg);
$result = $validate->check($data);
if (!$result) {
$this->error(__($validate->getError()), null, ['token' => $this->request->token()]);
return FALSE;
}
if ($this->auth->login($account, $password)) {
$synchtml = '';
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS) {
$uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synlogin($this->auth->id);
}
$this->success(__('Logged in successful') . $synchtml, $url ? $url : url('user/index'));
} else {
$this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
}
}
//判断来源
$referer = $this->request->server('HTTP_REFERER');
if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
&& !preg_match("/(user\/login|user\/register)/i", $referer)) {
$url = $referer;
}
$this->view->assign('url', $url);
$this->view->assign('title', __('Login'));
return $this->view->fetch();
}
/**
* 注销登录
*/
function logout()
{
//注销本站
$this->auth->logout();
$synchtml = '';
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS) {
$uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synlogout();
}
$this->success(__('Logout successful') . $synchtml, url('home/index/index'));
}
/**
* 修改密码
*/
public function changepwd()
{
if ($this->request->isPost()) {
$oldpassword = $this->request->post("oldpassword");
$newpassword = $this->request->post("newpassword");
$renewpassword = $this->request->post("renewpassword");
$token = $this->request->post('__token__');
$rule = [
'oldpassword' => 'require|length:6,30',
'newpassword' => 'require|length:6,30',
'renewpassword' => 'require|length:6,30|confirm:newpassword',
'__token__' => 'token',
];
$msg = [
];
$data = [
'oldpassword' => $oldpassword,
'newpassword' => $newpassword,
'renewpassword' => $renewpassword,
'__token__' => $token,
];
$field = [
'oldpassword' => __('Old password'),
'newpassword' => __('New password'),
'renewpassword' => __('Renew password')
];
$validate = new Validate($rule, $msg, $field);
$result = $validate->check($data);
if (!$result) {
$this->error(__($validate->getError()), null, ['token' => $this->request->token()]);
return FALSE;
}
$ret = $this->auth->changepwd($newpassword, $oldpassword);
if ($ret) {
$synchtml = '';
////////////////同步到Ucenter////////////////
if (defined('UC_STATUS') && UC_STATUS) {
$uc = new \addons\ucenter\library\client\Client();
$synchtml = $uc->uc_user_synlogout();
}
$this->success(__('Reset password successful') . $synchtml, url('user/login'));
} else {
$this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
}
}
$this->view->assign('title', __('Change password'));
return $this->view->fetch();
}
/**
* 个人信息
*/
public function profile()
{
//设置过滤方法
$this->request->filter(['strip_tags']);
if ($this->request->isAjax())
{
$model = model('UserLog');
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$total = $model
->where($where)
->where('user_id', $this->auth->id)
->order($sort, $order)
->count();
$list = $model
->where($where)
->where('user_id', $this->auth->id)
->order($sort, $order)
->limit($offset, $limit)
->select();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace app\index\model;
use app\common\library\Auth;
use think\Model;
use think\Cookie;
class UserLog extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = '';
//自定义日志标题
protected static $title = '';
//自定义日志内容
protected static $content = '';
public static function setTitle($title)
{
self::$title = $title;
}
public static function setContent($content)
{
self::$content = $content;
}
/*
* 日志记录
*/
public static function record($title = '')
{
$auth = Auth::instance();
$token = request()->server('HTTP_TOKEN', request()->request('token', Cookie::get('token')));
$auth->init($token);
$user_id = $auth->isLogin() ? $auth->id : 0;
$username = $auth->isLogin() ? $auth->username : __('Unknown');
$content = self::$content;
if (!$content) {
$content = request()->param();
foreach ($content as $k => $v) {
if (is_string($v) && strlen($v) > 200 || stripos($k, 'password') !== false) {
unset($content[$k]);
}
}
}
$title = self::$title;
if (!$title) {
$title = [];
$breadcrumb = $auth->getBreadCrumb();
if($breadcrumb){
foreach ($breadcrumb as $k => $v) {
$title[] = $v['title'];
}
}
$title = implode(' ', $title);
}
self::create([
'title' => $title,
'content' => !is_scalar($content) ? json_encode($content) : $content,
'url' => request()->url(),
'user_id' => $user_id,
'username' => $username,
'useragent' => request()->server('HTTP_USER_AGENT'),
'ip' => request()->ip()
]);
}
public function user()
{
return $this->belongsTo('User', 'user_id')->setEagerlyType(0);
}
}

View File

@ -0,0 +1,16 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 应用行为扩展定义文件
return [
// 应用结束
'app_end' => ['app\\user\\behavior\\UserLog', ],
];

View File

@ -0,0 +1,458 @@
{layout name="layout/default" /}
<style type="text/css">
.sm-st {
background:#fff;
padding:20px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px;
margin-bottom:20px;
-webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.05);
box-shadow: 0 1px 0px rgba(0,0,0,0.05);
}
.sm-st-icon {
width:60px;
height:60px;
display:inline-block;
line-height:60px;
text-align:center;
font-size:30px;
background:#eee;
-webkit-border-radius:5px;
-moz-border-radius:5px;
border-radius:5px;
float:left;
margin-right:10px;
color:#fff;
}
.sm-st-info {
font-size:12px;
padding-top:2px;
}
.sm-st-info span {
display:block;
font-size:24px;
font-weight:600;
}
.orange {
background:#fa8564 !important;
}
.tar {
background:#45cf95 !important;
}
.sm-st .green {
background:#86ba41 !important;
}
.pink {
background:#AC75F0 !important;
}
.yellow-b {
background: #fdd752 !important;
}
.stat-elem {
background-color: #fff;
padding: 18px;
border-radius: 40px;
}
.stat-info {
text-align: center;
background-color:#fff;
border-radius: 5px;
margin-top: -5px;
padding: 8px;
-webkit-box-shadow: 0 1px 0px rgba(0,0,0,0.05);
box-shadow: 0 1px 0px rgba(0,0,0,0.05);
font-style: italic;
}
.stat-icon {
text-align: center;
margin-bottom: 5px;
}
.st-red {
background-color: #F05050;
}
.st-green {
background-color: #27C24C;
}
.st-violet {
background-color: #7266ba;
}
.st-blue {
background-color: #23b7e5;
}
.stats .stat-icon {
color: #28bb9c;
display: inline-block;
font-size: 26px;
text-align: center;
vertical-align: middle;
width: 50px;
float:left;
}
.stat {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
margin-right: 10px; }
.stat .value {
font-size: 20px;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500; }
.stat .name {
overflow: hidden;
text-overflow: ellipsis; }
.stat.lg .value {
font-size: 26px;
line-height: 28px; }
.stat.lg .name {
font-size: 16px; }
.stat-col .progress {height:2px;}
.stat-col .progress-bar {line-height:2px;height:2px;}
.item {
padding:30px 0;
}
</style>
<div class="panel panel-default panel-intro">
<div class="panel-heading">
{:build_heading(null, false)}
<ul class="nav nav-tabs">
<li class="active"><a href="#one" data-toggle="tab">{:__('Dashboard')}</a></li>
<li><a href="#two" data-toggle="tab">{:__('Custom')}</a></li>
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="row">
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-red"><i class="fa fa-users"></i></span>
<div class="sm-st-info">
<span>{$totaluser}</span>
{:__('Total user')}
</div>
</div>
</div>
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-violet"><i class="fa fa-book"></i></span>
<div class="sm-st-info">
<span>{$totalviews}</span>
{:__('Total view')}
</div>
</div>
</div>
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-blue"><i class="fa fa-shopping-bag"></i></span>
<div class="sm-st-info">
<span>{$totalorder}</span>
{:__('Total order')}
</div>
</div>
</div>
<div class="col-sm-3 col-xs-6">
<div class="sm-st clearfix">
<span class="sm-st-icon st-green"><i class="fa fa-cny"></i></span>
<div class="sm-st-info">
<span>{$totalorderamount}</span>
{:__('Total order amount')}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-8">
<div id="echart" style="height:200px;width:100%;"></div>
</div>
<div class="col-lg-4">
<div class="card sameheight-item stats">
<div class="card-block">
<div class="row row-sm stats-container">
<div class="col-xs-6 stat-col">
<div class="stat-icon"> <i class="fa fa-rocket"></i> </div>
<div class="stat">
<div class="value"> {$todayusersignup} </div>
<div class="name"> {:__('Today user signup')} </div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 30%"></div>
</div>
</div>
<div class="col-xs-6 stat-col">
<div class="stat-icon"> <i class="fa fa-shopping-cart"></i> </div>
<div class="stat">
<div class="value"> {$todayuserlogin} </div>
<div class="name"> {:__('Today user login')} </div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 25%"></div>
</div>
</div>
<div class="col-xs-6 stat-col">
<div class="stat-icon"> <i class="fa fa-line-chart"></i> </div>
<div class="stat">
<div class="value"> {$todayorder} </div>
<div class="name"> {:__('Today order')} </div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 25%"></div>
</div>
</div>
<div class="col-xs-6 stat-col">
<div class="stat-icon"> <i class="fa fa-users"></i> </div>
<div class="stat">
<div class="value"> {$unsettleorder} </div>
<div class="name"> {:__('Unsettle order')} </div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 25%"></div>
</div>
</div>
<div class="col-xs-6 stat-col">
<div class="stat-icon"> <i class="fa fa-list-alt"></i> </div>
<div class="stat">
<div class="value"> {$sevendnu} </div>
<div class="name"> {:__('Seven dnu')} </div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 25%"></div>
</div>
</div>
<div class="col-xs-6 stat-col">
<div class="stat-icon"> <i class="fa fa-dollar"></i> </div>
<div class="stat">
<div class="value"> {$sevendau} </div>
<div class="name"> {:__('Seven dau')} </div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: 25%"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top:15px;">
<div class="col-lg-12">
</div>
<div class="col-xs-6 col-md-3">
<div class="panel bg-blue">
<div class="panel-body">
<div class="panel-title">
<span class="label label-success pull-right">{:__('Real time')}</span>
<h5>{:__('Category count')}</h5>
</div>
<div class="panel-content">
<h1 class="no-margins">1234</h1>
<div class="stat-percent font-bold text-gray"><i class="fa fa-commenting"></i> 1234</div>
<small>{:__('Category count tips')}</small>
</div>
</div>
</div>
</div>
<div class="col-xs-6 col-md-3">
<div class="panel bg-aqua-gradient">
<div class="panel-body">
<div class="ibox-title">
<span class="label label-info pull-right">{:__('Real time')}</span>
<h5>{:__('Attachment count')}</h5>
</div>
<div class="ibox-content">
<h1 class="no-margins">1043</h1>
<div class="stat-percent font-bold text-gray"><i class="fa fa-modx"></i> 2592</div>
<small>{:__('Attachment count tips')}</small>
</div>
</div>
</div>
</div>
<div class="col-xs-6 col-md-3">
<div class="panel bg-purple-gradient">
<div class="panel-body">
<div class="ibox-title">
<span class="label label-primary pull-right">{:__('Real time')}</span>
<h5>{:__('Article count')}</h5>
</div>
<div class="ibox-content">
<div class="row">
<div class="col-md-6">
<h1 class="no-margins">1234</h1>
<div class="font-bold text-navy"><i class="fa fa-commenting"></i> <small>{:__('Comment count')}</small></div>
</div>
<div class="col-md-6">
<h1 class="no-margins">6754</h1>
<div class="font-bold text-navy"><i class="fa fa-heart"></i> <small>{:__('Like count')}</small></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-6 col-md-3">
<div class="panel bg-green-gradient">
<div class="panel-body">
<div class="ibox-title">
<span class="label label-primary pull-right">{:__('Real time')}</span>
<h5>{:__('News count')}</h5>
</div>
<div class="ibox-content">
<div class="row">
<div class="col-md-6">
<h1 class="no-margins">5302</h1>
<div class="font-bold text-navy"><i class="fa fa-commenting"></i> <small>{:__('Comment count')}</small></div>
</div>
<div class="col-md-6">
<h1 class="no-margins">8205</h1>
<div class="font-bold text-navy"><i class="fa fa-user"></i> <small>{:__('Like count')}</small></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-4">
<div class="box box-danger">
<div class="box-header">
<h3 class="box-title">{:__('Recent news')}</h3>
<div class="box-tools pull-right">
<a href="https://www.fastadmin.net" target="_blank" class="btn btn-box-tool">{:__('More')}</a>
</div>
</div>
<div class="box-body" id="news-list">
</div>
</div>
</div>
<div class="col-lg-4">
<div class="box box-success">
<div class="box-header">
<h3 class="box-title">{:__('Recent discussion')}</h3>
<div class="box-tools pull-right">
<a href="https://forum.fastadmin.net" class="btn btn-box-tool">{:__('More')}</a>
</div>
</div>
<div class="box-body" id="discussion-list">
</div>
</div>
</div>
<div class="col-lg-4">
<div class="box box-info">
<div class="box-header"><h3 class="box-title">{:__('Server info')}</h3></div>
<div class="box-body">
<table class="table table-striped">
<tbody>
<tr>
<td width="140">{:__('FastAdmin version')}</td>
<td>{$Think.config.fastadmin.version} <a href="javascript:;" class="btn btn-xs btn-checkversion">检查最新版</a></td>
</tr>
<tr>
<td>{:__('Sapi name')}</td>
<td>{:php_sapi_name()}</td>
</tr>
<tr>
<td>{:__('Debug mode')}</td>
<td>{$Think.config.app_debug?__('Yes'):__('No')}</td>
</tr>
<tr>
<td>{:__('Software')}</td>
<td>{$Think.server.SERVER_SOFTWARE}</td>
</tr>
<tr>
<td>{:__('Upload mode')}</td>
<td>{$uploadmode}</td>
</tr>
<tr>
<td>{:__('Upload url')}</td>
<td>{$config.upload.uploadurl}</td>
</tr>
<tr>
<td>{:__('Upload Cdn url')}</td>
<td>{$config.upload.cdnurl}</td>
</tr>
<tr>
<td>{:__('Timezone')}</td>
<td>{:date_default_timezone_get()}</td>
</tr>
<tr>
<td>{:__('Cdn url')}</td>
<td>__CDN__</td>
</tr>
<tr>
<td>{:__('Language')}</td>
<td>{$config.language}</td>
</tr>
</tbody></table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="two">
<div class="row">
<div class="col-xs-12">
{:__('Custom zone')}
</div>
</div>
</div>
</div>
</div>
</div>
<script id="newstpl" type="text/html">
<ul class="nav nav-stacked">
<%for(var i=0;i < news.length;i++){%>
<%var item=news[i];%>
<li>
<a href="<%=item.url%>" target="_blank">
<span class="text"><%=item.title%></span>
</a>
</li>
<%}%>
</ul>
</script>
<script id="discussiontpl" type="text/html">
<ul class="products-list product-list-in-box">
<%for(var i=0;i < news.length;i++){%>
<%var item=news[i];%>
<li class="item">
<div class="">
<a href="<%=item.url%>" target="_blank" class="product-title"><%=item.title%>
<span class="label label-warning pull-right"><%=item.comments_count%></span></a>
<span class="product-description">
<%=item.last_time%>
</span>
</div>
</li>
<%}%>
</ul>
</script>
<script>
var Orderdata = {
column: {:json_encode(array_keys($paylist))},
paydata: {:json_encode(array_values($paylist))},
createdata: {:json_encode(array_values($createlist))},
};
</script>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="{$site.languages.frontend}">
<head>
{include file="layout/meta" /}
</head>
<body class="hold-transition skin-green sidebar-mini fixed {if $config.fastadmin.multiplenav}multiplenav{/if}" id="tabs">
<div class="wrapper">
<header id="header" class="main-header">
{include file='layout/header' /}
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
{include file='layout/menu' /}
</aside>
<!-- 主体内容区域 -->
<div class="content-wrapper tab-content tab-addtabs">
{if $fixedmenu}
<div role="tabpanel" class="tab-pane {:$referermenu?'':'active'}" id="con_{$fixedmenu.id}">
<iframe src="{$fixedmenu.url}?addtabs=1" width="100%" height="100%" frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling-x="no" scrolling-y="auto" allowtransparency="yes"></iframe>
</div>
{/if}
{if $referermenu}
<div role="tabpanel" class="tab-pane active" id="con_{$referermenu.id}">
<iframe src="{$referermenu.url}?addtabs=1" width="100%" height="100%" frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling-x="no" scrolling-y="auto" allowtransparency="yes"></iframe>
</div>
{/if}
</div>
<!-- /.content-wrapper -->
<!-- 底部链接,默认隐藏 -->
<footer class="main-footer hide">
<div class="pull-right hidden-xs">
</div>
<strong>Copyright &copy; 2017-2018 <a href="https://www.fastadmin.net">Fastadmin</a>.</strong> All rights reserved.
</footer>
<!-- Add the sidebar's background. This div must be placed
immediately after the control sidebar -->
<div class="control-sidebar-bg"></div>
{include file="layout/control" /}
</div>
<!-- ./wrapper -->
<!-- end main content -->
{include file="layout/script" /}
</body>
</html>

View File

@ -0,0 +1,59 @@
<style>
.skin-list li{
float:left; width: 33.33333%; padding: 5px;
}
.skin-list li a{
display: block; box-shadow: 0 0 3px rgba(0,0,0,0.4);
}
</style>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Create the tabs -->
<ul class="nav nav-tabs nav-justified control-sidebar-tabs">
<li class="active"><a href="#control-sidebar-setting-tab" data-toggle="tab" aria-expanded="true"><i class="fa fa-wrench"></i></a></li>
<li><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li>
<li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<!-- Home tab content -->
<div class="tab-pane active" id="control-sidebar-setting-tab">
<h4 class="control-sidebar-heading">{:__('Layout Options')}</h4>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-layout="fixed" class="pull-right"> {:__('Fixed Layout')}</label><p>{:__("You can't use fixed and boxed layouts together")}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-layout="layout-boxed" class="pull-right"> {:__('Boxed Layout')}</label><p>{:__('Activate the boxed layout')}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-layout="sidebar-collapse" class="pull-right"> {:__('Toggle Sidebar')}</label><p>{:__("Toggle the left sidebar's state (open or collapse)")}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-enable="expandOnHover" class="pull-right"> {:__('Sidebar Expand on Hover')}</label><p>{:__('Let the sidebar mini expand on hover')}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-menu="show-submenu" class="pull-right"> {:__('Show sub menu')}</label><p>{:__('Always show sub menu')}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-menu="disable-top-badge" class="pull-right"> {:__('Disable top menu badge')}</label><p>{:__('Disable top menu badge without left menu')}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-controlsidebar="control-sidebar-open" class="pull-right"> {:__('Toggle Right Sidebar Slide')}</label><p>{:__('Toggle between slide over content and push content effects')}</p></div>
<div class="form-group"><label class="control-sidebar-subheading"><input type="checkbox" data-sidebarskin="toggle" class="pull-right"> {:__('Toggle Right Sidebar Skin')}</label><p>{:__('Toggle between dark and light skins for the right sidebar')}</p></div>
<h4 class="control-sidebar-heading">{:__('Skins')}</h4>
<ul class="list-unstyled clearfix skin-list">
<li><a href="javascript:;" data-skin="skin-blue" style="" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px; background: #367fa9;"></span><span class="bg-light-blue" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #222d32;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Blue</p></li>
<li><a href="javascript:;" data-skin="skin-white" class="clearfix full-opacity-hover"><div style="box-shadow: 0 0 2px rgba(0,0,0,0.1)" class="clearfix"><span style="display:block; width: 20%; float: left; height: 7px; background: #fefefe;"></span><span style="display:block; width: 80%; float: left; height: 7px; background: #fefefe;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #222;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">White</p></li>
<li><a href="javascript:;" data-skin="skin-purple" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-purple-active"></span><span class="bg-purple" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #222d32;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Purple</p></li>
<li><a href="javascript:;" data-skin="skin-green" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-green-active"></span><span class="bg-green" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #222d32;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Green</p></li>
<li><a href="javascript:;" data-skin="skin-red" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-red-active"></span><span class="bg-red" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #222d32;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Red</p></li>
<li><a href="javascript:;" data-skin="skin-yellow" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-yellow-active"></span><span class="bg-yellow" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #222d32;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-blue-light" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px; background: #367fa9;"></span><span class="bg-light-blue" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #f9fafc;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Blue Light</p></li>
<li><a href="javascript:;" data-skin="skin-white-light" class="clearfix full-opacity-hover"><div style="box-shadow: 0 0 2px rgba(0,0,0,0.1)" class="clearfix"><span style="display:block; width: 20%; float: left; height: 7px; background: #fefefe;"></span><span style="display:block; width: 80%; float: left; height: 7px; background: #fefefe;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #f9fafc;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">White Light</p></li>
<li><a href="javascript:;" data-skin="skin-purple-light" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-purple-active"></span><span class="bg-purple" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #f9fafc;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Purple Light</p></li>
<li><a href="javascript:;" data-skin="skin-green-light" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-green-active"></span><span class="bg-green" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #f9fafc;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Green Light</p></li>
<li><a href="javascript:;" data-skin="skin-red-light" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-red-active"></span><span class="bg-red" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #f9fafc;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Red Light</p></li>
<li><a href="javascript:;" data-skin="skin-yellow-light" class="clearfix full-opacity-hover"><div><span style="display:block; width: 20%; float: left; height: 7px;" class="bg-yellow-active"></span><span class="bg-yellow" style="display:block; width: 80%; float: left; height: 7px;"></span></div><div><span style="display:block; width: 20%; float: left; height: 20px; background: #f9fafc;"></span><span style="display:block; width: 80%; float: left; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin" style="font-size: 12px;">Yellow Light</p></li>
</ul>
</div>
<!-- /.tab-pane -->
<!-- Home tab content -->
<div class="tab-pane" id="control-sidebar-home-tab">
<h4 class="control-sidebar-heading">{:__('Home')}</h4>
</div>
<!-- /.tab-pane -->
<!-- Settings tab content -->
<div class="tab-pane" id="control-sidebar-settings-tab">
<h4 class="control-sidebar-heading">{:__('Setting')}</h4>
</div>
<!-- /.tab-pane -->
</div>
</aside>
<!-- /.control-sidebar -->

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="{$site.languages.frontend}">
<head>
{include file="layout/meta" /}
</head>
<body class="inside-header inside-aside {:defined('IS_DIALOG') && IS_DIALOG ? 'is-dialog' : ''}">
<div id="main" role="main">
<div class="tab-content tab-addtabs">
<div id="content">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<section class="content-header hide">
<h1>
{:__('Dashboard')}
<small>{:__('Control panel')}</small>
</h1>
</section>
{if !IS_DIALOG}
<!-- RIBBON -->
<div id="ribbon">
<ol class="breadcrumb pull-left">
<li><a href="dashboard" class="addtabsit"><i class="fa fa-dashboard"></i> {:__('Dashboard')}</a></li>
</ol>
<ol class="breadcrumb pull-right">
{foreach $breadcrumb as $vo}
<li><a href="javascript:;" data-url="{$vo.url}">{$vo.title}</a></li>
{/foreach}
</ol>
</div>
<!-- END RIBBON -->
{/if}
<div class="content">
{__CONTENT__}
</div>
</div>
</div>
</div>
</div>
</div>
{include file="layout/script" /}
</body>
</html>

View File

@ -0,0 +1,132 @@
<!-- 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>
<!-- 普通模式下Logo -->
<span class="logo-lg"><b>{$site.name|mb_substr=0,4,'utf-8'}</b>{$site.name|mb_substr=4,null,'utf-8'}</span>
</a>
<!-- 顶部通栏样式 -->
<nav class="navbar navbar-static-top">
<!--第一级菜单-->
<div id="firstnav">
<!-- 边栏切换按钮-->
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">{:__('Toggle navigation')}</span>
</a>
<!--如果不想在顶部显示角标,则给ul加上disable-top-badge类即可-->
<ul class="nav nav-tabs nav-addtabs disable-top-badge hidden-xs" role="tablist">
{$navlist}
</ul>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li>
<a href="__PUBLIC__" target="_blank"><i class="fa fa-home" style="font-size:14px;"></i></a>
</li>
<li class="dropdown notifications-menu hidden-xs">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-bell-o"></i>
<span class="label label-warning"></span>
</a>
<ul class="dropdown-menu">
<li class="header">{:__('Latest news')}</li>
<li>
<!-- FastAdmin最新更新信息,你可以替换成你自己站点的信息,请注意修改public/assets/js/backend/index.js文件 -->
<ul class="menu">
</ul>
</li>
<li class="footer"><a href="#" target="_blank">{:__('View more')}</a></li>
</ul>
</li>
<!-- 多语言列表 -->
{if $Think.config.lang_switch_on}
<li class="hidden-xs">
<a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-language"></i></a>
<ul class="dropdown-menu">
<li class="{$config['language']=='zh-cn'?'active':''}">
<a href="?ref=addtabs&lang=zh-cn">简体中文</a>
</li>
<li class="{$config['language']=='en'?'active':''}">
<a href="?ref=addtabs&lang=en">English</a>
</li>
</ul>
</li>
{/if}
<!-- 全屏按钮 -->
<li class="hidden-xs">
<a href="#" data-toggle="fullscreen"><i class="fa fa-arrows-alt"></i></a>
</li>
<!-- 账号信息下拉框 -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="__CDN__{$user.avatar}" class="user-image" alt="{$user.nickname}">
<span class="hidden-xs">{$user.nickname}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="__CDN__{$user.avatar}" class="img-circle" alt="">
<p>
{$user.nickname}
<small>{$user.logintime|date="Y-m-d H:i:s",###}</small>
</p>
</li>
<!-- Menu Body -->
<li class="user-body">
<div class="row">
<div class="col-xs-4 text-center">
<a href="https://www.fastadmin.net" target="_blank">{:__('FastAdmin')}</a>
</div>
<div class="col-xs-4 text-center">
<a href="https://forum.fastadmin.net" target="_blank">{:__('Forum')}</a>
</div>
<div class="col-xs-4 text-center">
<a href="https://doc.fastadmin.net" target="_blank">{:__('Docs')}</a>
</div>
</div>
</li>
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-left">
<a href="index/user/profile" class="btn btn-primary addtabsit"><i class="fa fa-user"></i>
{:__('Profile')}</a>
</div>
<div class="pull-right">
<a href="{:url('index/user/logout')}" class="btn btn-danger"><i class="fa fa-sign-out"></i>
{:__('Logout')}</a>
</div>
</li>
</ul>
</li>
<!-- 控制栏切换按钮 -->
<li class="hidden-xs">
<a href="javascript:;" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a>
</li>
</ul>
</div>
</div>
{if $config.fastadmin.multiplenav}
<!--第二级菜单,只有在multiplenav开启时才显示-->
<div id="secondnav">
<ul class="nav nav-tabs nav-addtabs disable-top-badge" role="tablist">
{if $fixedmenu}
<li role="presentation" id="tab_{$fixedmenu.id}" class="{:$referermenu?'':'active'}"><a href="#con_{$fixedmenu.id}" node-id="{$fixedmenu.id}" aria-controls="{$fixedmenu.id}" role="tab" data-toggle="tab"><i class="fa fa-dashboard fa-fw"></i> <span>{$fixedmenu.title}</span> <span class="pull-right-container"> </span></a></li>
{/if}
{if $referermenu}
<li role="presentation" id="tab_{$referermenu.id}" class="active"><a href="#con_{$referermenu.id}" node-id="{$referermenu.id}" aria-controls="{$referermenu.id}" role="tab" data-toggle="tab"><i class="fa fa-list fa-fw"></i> <span>{$referermenu.title}</span> <span class="pull-right-container"> </span></a> <i class="close-tab fa fa-remove"></i></li>
{/if}
</ul>
</div>
{/if}
</nav>

View File

@ -0,0 +1,34 @@
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel hidden-xs">
<div class="pull-left image">
<a href="user/profile" class="addtabsit"><img src="__CDN__{$user.avatar}" class="img-circle" /></a>
</div>
<div class="pull-left info">
<p>{$user.nickname}</p>
<i class="fa fa-circle text-success"></i> {:__('Online')}
</div>
</div>
<!-- search form -->
<form action="" method="get" class="sidebar-form" onsubmit="return false;">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="{:__('Search menu')}">
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
</button>
</span>
<div class="menuresult list-group sidebar-form hide">
</div>
</div>
</form>
<!-- /.search form -->
<!-- sidebar menu: : style can be found in sidebar.less -->
<!--如果想始终显示子菜单,则给ul加上show-submenu类即可-->
<ul class="sidebar-menu">
{$menulist}
</ul>
</section>
<!-- /.sidebar -->

View File

@ -0,0 +1,19 @@
<meta charset="utf-8">
<title>{$title|default=''}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="renderer" content="webkit">
<link rel="shortcut icon" href="__CDN__/assets/img/favicon.ico" />
<!-- Loading Bootstrap -->
<link href="__CDN__/assets/css/backend{$Think.config.app_debug?'':'.min'}.css?v={$Think.config.site.version}" rel="stylesheet">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
<!--[if lt IE 9]>
<script src="__CDN__/assets/js/html5shiv.js"></script>
<script src="__CDN__/assets/js/respond.min.js"></script>
<![endif]-->
<script type="text/javascript">
var require = {
config: {$config|json_encode}
};
</script>

View File

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

View File

@ -0,0 +1,35 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<h2 class="page-header">{:__('Change password')}</h2>
<form id="changepwd-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="form-group">
<label for="oldpassword" class="control-label col-xs-12 col-sm-2">{:__('Old password')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="password" class="form-control" id="oldpassword" name="oldpassword" value="" data-rule="required" placeholder="{:__('Old password')}">
</div>
</div>
<div class="form-group">
<label for="newpassword" class="control-label col-xs-12 col-sm-2">{:__('New password')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="password" class="form-control" id="newpassword" name="newpassword" value="" data-rule="required" placeholder="{:__('New password')}" />
</div>
</div>
<div class="form-group">
<label for="renewpassword" class="control-label col-xs-12 col-sm-2">{:__('Renew password')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="password" class="form-control" id="renewpassword" name="renewpassword" value="" data-rule="required" placeholder="{:__('Renew password')}" />
</div>
</div>
<div class="form-group normal-footer">
<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-success btn-embossed disabled">{:__('Submit')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,74 @@
<style>
.basicinfo {
margin: 15px 0;
}
.basicinfo .row > .col-xs-4 {
padding-right: 0;
}
.basicinfo .row > div {
margin: 5px 0;
}
</style>
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<h2 class="page-header">
{:__('User center')}
<div id="toolbar" class="toolbar pull-right">
<a href="{:url('user/profile')}" class="btn btn-success">
<i class="fa fa-pencil"></i>
{:__('Profile')}
</a>
<a href="{:url('user/changepwd')}" class="btn btn-edit btn-success">
<i class="fa fa-key fa-fw"></i>
{:__('Change password')}
</a>
</div>
</h2>
<div class="row user-baseinfo">
<div class="col-md-3 col-sm-3 col-xs-2 text-center user-center">
<a href="{:url('user/profile')}" title="{:__('Click to edit')}">
<span class="avatar-img"><img src="{$user.avatar}" alt=""></span>
</a>
</div>
<div class="col-md-9 col-sm-9 col-xs-10">
<!-- Content -->
<div class="ui-content">
<!-- Heading -->
<h4><a href="{:url('user/profile')}">{$user.nickname}</a></h4>
<!-- Paragraph -->
<p>
<a href="{:url('user/profile')}">
{$user.bio|default=__("This guy hasn't written anything yet")}
</a>
</p>
<!-- Success -->
<div class="basicinfo">
<div class="row">
<div class="col-xs-4 col-md-2">{:__('Lv')}</div>
<div class="col-xs-8 col-md-4"><a href="javascript:;" class="viewlv">{$user.level}</a>
</div>
<div class="col-xs-4 col-md-2">{:__('Score')}</div>
<div class="col-xs-8 col-md-4"><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>
<div class="col-xs-4 col-md-2">{:__('Prevtime')}</div>
<div class="col-xs-8 col-md-4">{$user.prevtime|date="Y-m-d H:i:s",###}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,228 @@
<style>
.profile-avatar-container {
position:relative;
width:100px;
}
.profile-avatar-container .profile-user-img{
width:100px;
height:100px;
}
.profile-avatar-container .profile-avatar-text {
display:none;
}
.profile-avatar-container:hover .profile-avatar-text {
display:block;
position:absolute;
height:100px;
width:100px;
background:#444;
opacity: .6;
color: #fff;
top:0;
left:0;
line-height: 100px;
text-align: center;
}
.profile-avatar-container button{
position:absolute;
top:0;left:0;width:100px;height:100px;opacity: 0;
}
</style>
<div class="row animated fadeInRight">
<div class="col-md-4">
<div class="box box-success">
<div class="panel-heading">
{:__('Profile')}
</div>
<div class="panel-body">
<form id="profile-form" role="form" data-toggle="validator" method="POST" action="{:url('api/user/profile')}">
{:token()}
<input type="hidden" name="avatar" id="c-avatar" value="{$user.avatar}" />
<div class="box-body box-profile">
<div class="profile-avatar-container">
<img class="profile-user-img img-responsive img-circle plupload" src="{$user.avatar}" alt="">
<div class="profile-avatar-text img-circle">{:__('Click to edit')}</div>
<button id="plupload-avatar" class="plupload" data-mimetype="png,jpg,jpeg,gif" data-input-id="c-avatar"><i class="fa fa-upload"></i> {:__('Upload')}</button>
</div>
<h3 class="profile-username text-center">{$user.username}</h3>
<div class="form-group">
<label class="control-label">{:__('Nickname')}:</label>
<input type="text" class="form-control" id="nickname" name="nickname" value="{$user.nickname}" data-rule="required" placeholder="">
</div>
<div class="form-group">
<label for="c-bio" class="control-label">{:__('Intro')}:</label>
<input id="c-bio" data-rule="" data-tip="一句话介绍一下你自己" class="form-control" name="bio" type="text" value="{$user.bio}">
</div>
<div class="form-group">
<label for="c-email" class="control-label">{:__('Email')}:</label>
<div class="input-group">
<input type="text" class="form-control" id="c-email" name="email" value="{$user.email}" disabled placeholder="">
<span class="input-group-btn" style="padding:0;border:none;">
<a href="javascript:;" class="btn btn-info btn-change" data-type="email">{:__('Change')}</a>
</span>
</div>
</div>
<div class="form-group">
<label for="c-mobile" class="control-label">{:__('Mobile')}:</label>
<div class="input-group">
<input type="text" class="form-control" id="c-mobile" name="mobile" value="{$user.mobile}" disabled placeholder="">
<span class="input-group-btn" style="padding:0;border:none;">
<a href="javascript:;" class="btn btn-info btn-change" data-type="mobile">{:__('Change')}</a>
</span>
</div>
</div>
<div class="form-group normal-footer text-center">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('Ok')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-8">
<div class="panel panel-default panel-intro panel-nav">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active"><a href="#one" data-toggle="tab"><i class="fa fa-list"></i> 基本信息</a></li>
<li ><a href="#two" data-toggle="tab"><i class="fa fa-circle-o"></i> {:__('User log')}</a></li>
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<ul class="list-group list-group-unbordered">
<li class="list-group-item">
<b>{:__('Lv')}</b> <a class="pull-right">{$user.level}</a>
</li>
<li class="list-group-item">
<b>{:__('Score')}</b> <a class="pull-right">{$user.score}</a>
</li>
<li class="list-group-item">
<b>{:__('Successions')}</b> <a class="pull-right">{$user.successions} {:__('Day')}</a>
</li>
<li class="list-group-item">
<b>{:__('Maxsuccessions')}</b> <a class="pull-right">{$user.maxsuccessions} {:__('Day')}</a>
</li>
<li class="list-group-item">
<b>{:__('Logintime')}</b> <a class="pull-right">{$user.logintime|date="Y-m-d H:i:s",###}</a>
</li>
<li class="list-group-item">
<b>{:__('Prevtime')}</b> <a class="pull-right">{$user.prevtime|date="Y-m-d H:i:s",###}</a>
</li>
</ul>
</div>
<div class="tab-pane fade" id="two">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar('refresh')}
</div>
<table id="table" class="table table-striped table-bordered table-hover" width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/html" id="emailtpl">
<form id="email-form" class="form-horizontal form-layer" method="POST" action="{:url('api/user/changeemail')}">
<div class="form-body">
<input type="hidden" name="action" value="changeemail" />
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">{:__('New Email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="email" name="email" value="" data-rule="required;email;remote({:url('api/validate/check_email_available')}, event=changeemail, id={$user.id})" placeholder="{:__('New email')}">
<span class="msg-box"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" id="email-captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_ems_correct')}, event=changeemail, email:#email)" />
<span class="input-group-btn" style="padding:0;border:none;">
<a href="javascript:;" class="btn btn-info btn-captcha" data-url="{:url('api/ems/send')}" data-type="email" data-event="changeemail">获取验证码</a>
</span>
</div>
<span class="msg-box"></span>
</div>
</div>
</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">
<button type="submit" class="btn btn-md btn-info">{:__('Submit')}</button>
</div>
</div>
</div>
</form>
</script>
<script type="text/html" id="mobiletpl">
<form id="mobile-form" class="form-horizontal form-layer" method="POST" action="{:url('api/user/changemobile')}">
<div class="form-body">
<input type="hidden" name="action" value="changemobile" />
<div class="form-group">
<label for="c-mobile" class="control-label col-xs-12 col-sm-3">{:__('New mobile')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="mobile" name="mobile" value="" data-rule="required;mobile;remote({:url('api/validate/check_mobile_available')}, event=changemobile, id={$user.id})" placeholder="{:__('New mobile')}">
<span class="msg-box"></span>
</div>
</div>
<div class="form-group">
<label for="mobile-captcha" class="control-label col-xs-12 col-sm-3">{:__('Captcha')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input type="text" name="captcha" id="mobile-captcha" class="form-control" data-rule="required;length(4);integer[+];remote({:url('api/validate/check_sms_correct')}, event=changemobile, mobile:#mobile)" />
<span class="input-group-btn" style="padding:0;border:none;">
<a href="javascript:;" class="btn btn-info btn-captcha" data-url="{:url('api/sms/send')}" data-type="mobile" data-event="changemobile">获取验证码</a>
</span>
</div>
<span class="msg-box"></span>
</div>
</div>
</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">
<button type="submit" class="btn btn-md btn-info">{:__('Submit')}</button>
</div>
</div>
</div>
</form>
</script>
<style>
.form-layer {height:100%;min-height:150px;min-width:300px;}
.form-body {
width:100%;
overflow:auto;
top:0;
position:absolute;
z-index:10;
bottom:50px;
padding:15px;
}
.form-layer .form-footer {
height:50px;
line-height:50px;
background-color: #ecf0f1;
width:100%;
position:absolute;
z-index:200;
bottom:0;
margin:0;
}
.form-footer .form-group{
margin-left:0;
margin-right:0;
}
</style>

View File

@ -0,0 +1,157 @@
require.config({
urlArgs: "v=" + requirejs.s.contexts._.config.config.site.version,
packages: [{
name: 'moment',
location: '../libs/moment',
main: 'moment'
}
],
//在打包压缩时将会把include中的模块合并到主文件中
include: ['css', 'layer', 'toastr', 'fast', 'userend', 'userend-init', 'table', 'form', 'dragsort', 'drag', 'drop', 'addtabs', 'selectpage'],
paths: {
'lang': "empty:",
'form': 'require-form',
'table': 'require-table',
'upload': 'require-upload',
'validator': 'require-validator',
'drag': 'jquery.drag.min',
'drop': 'jquery.drop.min',
'echarts': 'echarts.min',
'echarts-theme': 'echarts-theme',
'adminlte': 'adminlte',
'bootstrap-table-commonsearch': 'bootstrap-table-commonsearch',
'bootstrap-table-template': 'bootstrap-table-template',
//
// 以下的包从bower的libs目录加载
'jquery': '../libs/jquery/dist/jquery.min',
'bootstrap': '../libs/bootstrap/dist/js/bootstrap.min',
'bootstrap-datetimepicker': '../libs/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min',
'bootstrap-daterangepicker': '../libs/bootstrap-daterangepicker/daterangepicker',
'bootstrap-select': '../libs/bootstrap-select/dist/js/bootstrap-select.min',
'bootstrap-select-lang': '../libs/bootstrap-select/dist/js/i18n/defaults-zh_CN',
'bootstrap-table': '../libs/bootstrap-table/dist/bootstrap-table.min',
'bootstrap-table-export': '../libs/bootstrap-table/dist/extensions/export/bootstrap-table-export.min',
'bootstrap-table-mobile': '../libs/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile',
'bootstrap-table-lang': '../libs/bootstrap-table/dist/locale/bootstrap-table-zh-CN',
'tableexport': '../libs/tableExport.jquery.plugin/tableExport.min',
'dragsort': '../libs/fastadmin-dragsort/jquery.dragsort',
'sortable': '../libs/Sortable/Sortable.min',
'addtabs': '../libs/fastadmin-addtabs/jquery.addtabs',
'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll',
'validator-core': '../libs/nice-validator/dist/jquery.validator',
'validator-lang': '../libs/nice-validator/dist/local/zh-CN',
'plupload': '../libs/plupload/js/plupload.min',
'toastr': '../libs/toastr/toastr',
'jstree': '../libs/jstree/dist/jstree.min',
'layer': '../libs/fastadmin-layer/dist/layer',
'cookie': '../libs/jquery.cookie/jquery.cookie',
'cxselect': '../libs/fastadmin-cxselect/js/jquery.cxselect',
'template': '../libs/art-template/dist/template-native',
'selectpage': '../libs/fastadmin-selectpage/selectpage',
'citypicker': '../libs/fastadmin-citypicker/dist/js/city-picker.min',
'citypicker-data': '../libs/fastadmin-citypicker/dist/js/city-picker.data',
},
// shim依赖配置
shim: {
'addons': ['userend'],
'bootstrap': ['jquery'],
'bootstrap-table': {
deps: [
'bootstrap',
// 'css!../libs/bootstrap-table/dist/bootstrap-table.min.css'
],
exports: '$.fn.bootstrapTable'
},
'bootstrap-table-lang': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-export': {
deps: ['bootstrap-table', 'tableexport'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-mobile': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-advancedsearch': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-commonsearch': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-template': {
deps: ['bootstrap-table', 'template'],
exports: '$.fn.bootstrapTable.defaults'
},
'tableexport': {
deps: ['jquery'],
exports: '$.fn.extend'
},
'slimscroll': {
deps: ['jquery'],
exports: '$.fn.extend'
},
'adminlte': {
deps: ['bootstrap', 'slimscroll'],
exports: '$.AdminLTE'
},
'bootstrap-datetimepicker': [
'moment/locale/zh-cn',
// 'css!../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css',
],
// 'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
'bootstrap-select-lang': ['bootstrap-select'],
// 'toastr': ['css!../libs/toastr/toastr.min.css'],
'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],
'plupload': {
deps: ['../libs/plupload/js/moxie.min'],
exports: "plupload"
},
// 'layer': ['css!../libs/layer/dist/theme/default/layer.css'],
// 'validator-core': ['css!../libs/nice-validator/dist/jquery.validator.css'],
'validator-lang': ['validator-core'],
// 'selectpage': ['css!../libs/fastadmin-selectpage/selectpage.css'],
'citypicker': ['citypicker-data', 'css!../libs/fastadmin-citypicker/dist/css/city-picker.css']
},
baseUrl: requirejs.s.contexts._.config.config.site.cdnurl + '/assets/js/', //资源基础路径
map: {
'*': {
'css': '../libs/require-css/css.min'
}
},
waitSeconds: 30,
charset: 'utf-8' // 文件编码
});
require(['jquery', 'bootstrap'], function ($, undefined) {
//初始配置
var Config = requirejs.s.contexts._.config.config;
//将Config渲染到全局
window.Config = Config;
// 配置语言包的路径
var paths = {};
paths['lang'] = Config.moduleurl + '/ajax/lang?callback=define&controllername=' + Config.controllername;
// 避免目录冲突
paths['userend/'] = 'userend/';
require.config({paths: paths});
// 初始化
$(function () {
require(['fast'], function (Fast) {
require(['userend', 'userend-init', 'addons'], function (Userend, undefined, Addons) {
//加载相应模块
if (Config.jsname) {
require([Config.jsname], function (Controller) {
Controller[Config.actionname] != undefined && Controller[Config.actionname]();
}, function (e) {
console.error(e);
// 这里可捕获模块加载的错误
});
}
});
});
});
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
define(['userend'], function (Userend) {
});

View File

@ -0,0 +1,243 @@
define(['fast', 'template', 'moment'], function (Fast, Template, Moment) {
var Userend = {
api: {
sidebar: function (params) {
colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple'];
$colorNums = colorArr.length;
badgeList = {};
$.each(params, function (k, v) {
$url = Fast.api.fixurl(k);
if ($.isArray(v)) {
$nums = typeof v[0] !== 'undefined' ? v[0] : 0;
$color = typeof v[1] !== 'undefined' ? v[1] : colorArr[(!isNaN($nums) ? $nums : $nums.length) % $colorNums];
$class = typeof v[2] !== 'undefined' ? v[2] : 'label';
} else {
$nums = v;
$color = colorArr[(!isNaN($nums) ? $nums : $nums.length) % $colorNums];
$class = 'label';
}
//必须nums大于0才显示
badgeList[$url] = $nums > 0 ? '<small class="' + $class + ' pull-right bg-' + $color + '">' + $nums + '</small>' : '';
});
$.each(badgeList, function (k, v) {
var anchor = top.window.$("li a[addtabs][url='" + k + "']");
if (anchor) {
top.window.$(".pull-right-container", anchor).html(v);
top.window.$(".nav-addtabs li a[node-id='" + anchor.attr("addtabs") + "'] .pull-right-container").html(v);
}
});
},
addtabs: function (url, title, icon) {
var dom = "a[url='{url}']"
var leftlink = top.window.$(dom.replace(/\{url\}/, url));
if (leftlink.size() > 0) {
leftlink.trigger("click");
} else {
url = Fast.api.fixurl(url);
leftlink = top.window.$(dom.replace(/\{url\}/, url));
if (leftlink.size() > 0) {
var event = leftlink.parent().hasClass("active") ? "dblclick" : "click";
leftlink.trigger(event);
} else {
var baseurl = url.substr(0, url.indexOf("?") > -1 ? url.indexOf("?") : url.length);
leftlink = top.window.$(dom.replace(/\{url\}/, baseurl));
//能找到相对地址
if (leftlink.size() > 0) {
icon = typeof icon !== 'undefined' ? icon : leftlink.find("i").attr("class");
title = typeof title !== 'undefined' ? title : leftlink.find("span:first").text();
leftlink.trigger("fa.event.toggleitem");
}
var navnode = top.window.$(".nav-tabs ul li a[node-url='" + url + "']");
if (navnode.size() > 0) {
navnode.trigger("click");
} else {
//追加新的tab
var id = Math.floor(new Date().valueOf() * Math.random());
icon = typeof icon !== 'undefined' ? icon : 'fa fa-circle-o';
title = typeof title !== 'undefined' ? title : '';
top.window.$("<a />").append('<i class="' + icon + '"></i> <span>' + title + '</span>').prop("href", url).attr({
url: url,
addtabs: id
}).addClass("hide").appendTo(top.window.document.body).trigger("click");
}
}
}
},
closetabs: function (url) {
if (typeof url === 'undefined') {
top.window.$("ul.nav-addtabs li.active .close-tab").trigger("click");
} else {
var dom = "a[url='{url}']"
var navlink = top.window.$(dom.replace(/\{url\}/, url));
if (navlink.size() === 0) {
url = Fast.api.fixurl(url);
navlink = top.window.$(dom.replace(/\{url\}/, url));
if (navlink.size() === 0) {
} else {
var baseurl = url.substr(0, url.indexOf("?") > -1 ? url.indexOf("?") : url.length);
navlink = top.window.$(dom.replace(/\{url\}/, baseurl));
//能找到相对地址
if (navlink.size() === 0) {
navlink = top.window.$(".nav-tabs ul li a[node-url='" + url + "']");
}
}
}
if (navlink.size() > 0 && navlink.attr('addtabs')) {
top.window.$("ul.nav-addtabs li#tab_" + navlink.attr('addtabs') + " .close-tab").trigger("click");
}
}
},
replaceids: function (elem, url) {
//如果有需要替换ids的
if (url.indexOf("{ids}") > -1) {
var ids = 0;
var tableId = $(elem).data("table-id");
if (tableId && $(tableId).size() > 0 && $(tableId).data("bootstrap.table")) {
var Table = require("table");
ids = Table.api.selectedids($(tableId)).join(",");
}
url = url.replace(/\{ids\}/g, ids);
}
return url;
},
refreshmenu: function () {
top.window.$(".sidebar-menu").trigger("refresh");
},
gettablecolumnbutton: function(options){
if (typeof options.tableId !== 'undefined' && typeof options.fieldIndex !== 'undefined' && typeof options.buttonIndex !== 'undefined') {
var tableOptions = $("#" + options.tableId).bootstrapTable('getOptions');
if (tableOptions) {
var columnObj = null;
$.each(tableOptions.columns, function (i, columns) {
$.each(columns, function (j, column) {
if (typeof column.fieldIndex !== 'undefined' && column.fieldIndex === options.fieldIndex) {
columnObj = column;
return false;
}
});
if (columnObj) {
return false;
}
});
if (columnObj) {
return columnObj['buttons'][options.buttonIndex];
}
}
}
return null;
},
},
init: function () {
//公共代码
//添加ios-fix兼容iOS下的iframe
if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
$("html").addClass("ios-fix");
}
//配置Toastr的参数
Toastr.options.positionClass = Config.controllername === 'index' ? "toast-top-right-index" : "toast-top-right";
//点击包含.btn-dialog的元素时弹出dialog
$(document).on('click', '.btn-dialog,.dialogit', function (e) {
var that = this;
var options = $.extend({}, $(that).data() || {});
var url = Userend.api.replaceids(that, $(that).attr('href'));
var title = $(that).attr("title") || $(that).data("title") || $(that).data('original-title');
var button = Userend.api.gettablecolumnbutton(options);
if (button && typeof button.callback === 'function') {
options.callback = button.callback;
}
if (typeof options.confirm !== 'undefined') {
Layer.confirm(options.confirm, function (index) {
Userend.api.open(url, title, options);
Layer.close(index);
});
} else {
Userend.api.open(url, title, options);
}
return false;
});
//点击包含.btn-addtabs的元素时新增选项卡
$(document).on('click', '.btn-addtabs,.addtabsit', function (e) {
var that = this;
var options = $.extend({}, $(that).data() || {});
var url = Userend.api.replaceids(that, $(that).attr('href'));
var title = $(that).attr("title") || $(that).data("title") || $(that).data('original-title');
if (typeof options.confirm !== 'undefined') {
Layer.confirm(options.confirm, function (index) {
Userend.api.addtabs(url, title);
Layer.close(index);
});
} else {
Userend.api.addtabs(url, title);
}
return false;
});
//点击包含.btn-ajax的元素时发送Ajax请求
$(document).on('click', '.btn-ajax,.ajaxit', function (e) {
var that = this;
var options = $.extend({}, $(that).data() || {});
if (typeof options.url === 'undefined' && $(that).attr("href")) {
options.url = $(that).attr("href");
}
options.url = Userend.api.replaceids(this, options.url);
var success = typeof options.success === 'function' ? options.success : null;
var error = typeof options.error === 'function' ? options.error : null;
delete options.success;
delete options.error;
var button = Userend.api.gettablecolumnbutton(options);
if (button) {
if (typeof button.success === 'function') {
success = button.success;
}
if (typeof button.error === 'function') {
error = button.error;
}
}
//如果未设备成功的回调,设定了自动刷新的情况下自动进行刷新
if (!success && typeof options.tableId !== 'undefined' && typeof options.refresh !== 'undefined' && options.refresh) {
$("#" + options.tableId).bootstrapTable('refresh');
}
if (typeof options.confirm !== 'undefined') {
Layer.confirm(options.confirm, function (index) {
Userend.api.ajax(options, success, error);
Layer.close(index);
});
} else {
Userend.api.ajax(options, success, error);
}
return false;
});
//修复含有fixed-footer类的body边距
if ($(".fixed-footer").size() > 0) {
$(document.body).css("padding-bottom", $(".fixed-footer").outerHeight());
}
//修复不在iframe时layer-footer隐藏的问题
if ($(".layer-footer").size() > 0 && self === top) {
$(".layer-footer").show();
}
//优化在多个弹窗下点击不能切换的操作体验
if (Fast.api.query("dialog") == "1" && self != top && self.frameElement && self.frameElement.tagName == "IFRAME") {
$(window).on('click', function () {
var layero = self.frameElement.parentNode.parentElement;
if (parent.Layer.zIndex != parseInt(parent.window.$(layero).css("z-index"))) {
parent.window.$(layero).trigger("mousedown");
parent.Layer.zIndex = parseInt(parent.window.$(layero).css("z-index"));
}
});
}
//tooltip和popover
$('body').tooltip({selector: '[data-toggle="tooltip"]'});
$('body').tooltip({selector: '[data-toggle="popover"]'});
}
};
Userend.api = $.extend(Fast.api, Userend.api);
//将Template渲染至全局,以便于在子框架中调用
window.Template = Template;
//将Moment渲染至全局,以便于在子框架中调用
window.Moment = Moment;
//将Userend渲染至全局,以便于在子框架中调用
window.Userend = Userend;
Userend.init();
return Userend;
});

View File

@ -0,0 +1,112 @@
define(['jquery', 'bootstrap', 'backend', 'addtabs', 'table', 'echarts', 'echarts-theme', 'template'], function ($, undefined, Backend, Datatable, Table, Echarts, undefined, Template) {
var Controller = {
index: function () {
// 基于准备好的dom初始化echarts实例
var myChart = Echarts.init(document.getElementById('echart'), 'walden');
// 指定图表的配置项和数据
var option = {
title: {
text: '',
subtext: ''
},
tooltip: {
trigger: 'axis'
},
legend: {
data: [__('Sales'), __('Orders')]
},
toolbox: {
show: false,
feature: {
magicType: {show: true, type: ['stack', 'tiled']},
saveAsImage: {show: true}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: Orderdata.column
},
yAxis: {
},
grid: [{
left: 'left',
top: 'top',
right: '10',
bottom: 30
}],
series: [{
name: __('Sales'),
type: 'line',
smooth: true,
areaStyle: {
normal: {
}
},
lineStyle: {
normal: {
width: 1.5
}
},
data: Orderdata.paydata
},
{
name: __('Orders'),
type: 'line',
smooth: true,
areaStyle: {
normal: {
}
},
lineStyle: {
normal: {
width: 1.5
}
},
data: Orderdata.createdata
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
//动态添加数据可以通过Ajax获取数据然后填充
setInterval(function () {
Orderdata.column.push((new Date()).toLocaleTimeString().replace(/^\D*/, ''));
var amount = Math.floor(Math.random() * 200) + 20;
Orderdata.createdata.push(amount);
Orderdata.paydata.push(Math.floor(Math.random() * amount) + 1);
//按自己需求可以取消这个限制
if (Orderdata.column.length >= 20) {
//移除最开始的一条数据
Orderdata.column.shift();
Orderdata.paydata.shift();
Orderdata.createdata.shift();
}
myChart.setOption({
xAxis: {
data: Orderdata.column
},
series: [{
name: __('Sales'),
data: Orderdata.paydata
},
{
name: __('Orders'),
data: Orderdata.createdata
}]
});
}, 2000);
$(window).resize(function () {
myChart.resize();
});
}
};
return Controller;
});

View File

@ -0,0 +1,421 @@
define(['jquery', 'bootstrap', 'userend', 'addtabs', 'adminlte', 'form'], function ($, undefined, Userend, undefined, AdminLTE, Form) {
var Controller = {
index: function () {
//双击重新加载页面
$(document).on("dblclick", ".sidebar-menu li > a", function (e) {
$("#con_" + $(this).attr("addtabs") + " iframe").attr('src', function (i, val) {
return val;
});
e.stopPropagation();
});
//修复在移除窗口时下拉框不隐藏的BUG
$(window).on("blur", function () {
$("[data-toggle='dropdown']").parent().removeClass("open");
if ($("body").hasClass("sidebar-open")) {
$(".sidebar-toggle").trigger("click");
}
});
//快捷搜索
$(".menuresult").width($("form.sidebar-form > .input-group").width());
var isAndroid = /(android)/i.test(navigator.userAgent);
var searchResult = $(".menuresult");
$("form.sidebar-form").on("blur", "input[name=q]", function () {
searchResult.addClass("hide");
if (isAndroid) {
$.AdminLTE.options.sidebarSlimScroll = true;
}
}).on("focus", "input[name=q]", function () {
if (isAndroid) {
$.AdminLTE.options.sidebarSlimScroll = false;
}
if ($("a", searchResult).size() > 0) {
searchResult.removeClass("hide");
}
}).on("keyup", "input[name=q]", function () {
searchResult.html('');
var val = $(this).val();
var html = new Array();
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>');
if (html.length >= 100) {
return false;
}
}
});
}
$(searchResult).append(html.join(""));
if (html.length > 0) {
searchResult.removeClass("hide");
} else {
searchResult.addClass("hide");
}
});
//快捷搜索点击事件
$("form.sidebar-form").on('mousedown click', '.menuresult a[data-url]', function () {
Userend.api.addtabs($(this).data("url"));
});
//读取FastAdmin的更新信息
$.ajax({
url: Config.fastadmin.api_url + '/news/index',
type: 'post',
dataType: 'jsonp',
success: function (ret) {
$(".notifications-menu > a span").text(ret.new > 0 ? ret.new : '');
$(".notifications-menu .footer a").attr("href", ret.url);
$.each(ret.newslist, function (i, j) {
var item = '<li><a href="' + j.url + '" target="_blank"><i class="' + j.icon + '"></i> ' + j.title + '</a></li>';
$(item).appendTo($(".notifications-menu ul.menu"));
});
}
});
//读取首次登录事件:推荐插件列表
if (localStorage.getItem("fastep") == "installed") {
$.ajax({
url: Config.fastadmin.api_url + '/addon/recommend',
type: 'post',
dataType: 'jsonp',
success: function (ret) {
require(['template'], function (Template) {
var install = function (name, title) {
Fast.api.ajax({
url: 'addon/install',
data: {name: name, faversion: Config.fastadmin.version}
}, function (data, ret) {
Fast.api.refreshmenu();
});
};
$(document).on('click', '.btn-install', function () {
$(this).prop("disabled", true).addClass("disabled");
$("input[name=addon]:checked").each(function () {
install($(this).data("name"));
});
return false;
});
$(document).on('click', '.btn-notnow', function () {
Layer.closeAll();
});
Layer.open({
type: 1, skin: 'layui-layer-page', area: ["860px", "620px"], title: '',
content: Template.render(ret.tpl, {addonlist: ret.rows})
});
localStorage.setItem("fastep", "dashboard");
});
}
});
}
//切换左侧sidebar显示隐藏
$(document).on("click fa.event.toggleitem", ".sidebar-menu li > a", function (e) {
$(".sidebar-menu li").removeClass("active");
//当外部触发隐藏的a时,触发父辈a的事件
if (!$(this).closest("ul").is(":visible")) {
//如果不需要左侧的菜单栏联动可以注释下面一行即可
$(this).closest("ul").prev().trigger("click");
}
var visible = $(this).next("ul").is(":visible");
if (!visible) {
$(this).parents("li").addClass("active");
} else {
}
e.stopPropagation();
});
//全屏事件
$(document).on('click', "[data-toggle='fullscreen']", function () {
var doc = document.documentElement;
if ($(document.body).hasClass("full-screen")) {
$(document.body).removeClass("full-screen");
document.exitFullscreen ? document.exitFullscreen() : document.mozCancelFullScreen ? document.mozCancelFullScreen() : document.webkitExitFullscreen && document.webkitExitFullscreen();
} else {
$(document.body).addClass("full-screen");
doc.requestFullscreen ? doc.requestFullscreen() : doc.mozRequestFullScreen ? doc.mozRequestFullScreen() : doc.webkitRequestFullscreen ? doc.webkitRequestFullscreen() : doc.msRequestFullscreen && doc.msRequestFullscreen();
}
});
var multiplenav = Config.fastadmin.multiplenav;
var firstnav = $("#firstnav .nav-addtabs");
var nav = multiplenav ? $("#secondnav .nav-addtabs") : firstnav;
//刷新菜单事件
$(document).on('refresh', '.sidebar-menu', function () {
Fast.api.ajax({
url: 'index/index',
data: {action: 'refreshmenu'}
}, function (data) {
$(".sidebar-menu li:not([data-rel='external'])").remove();
$(".sidebar-menu").prepend(data.menulist);
if (multiplenav) {
firstnav.html(data.navlist);
}
$("li[role='presentation'].active a", nav).trigger('click');
return false;
}, function () {
return false;
});
});
if (multiplenav) {
//一级菜单自适应
$(window).resize(function () {
var siblingsWidth = 0;
firstnav.siblings().each(function () {
siblingsWidth += $(this).outerWidth();
});
firstnav.width(firstnav.parent().width() - siblingsWidth);
firstnav.refreshAddtabs();
});
//点击顶部第一级菜单栏
firstnav.on("click", "li a", function () {
$("li", firstnav).removeClass("active");
$(this).closest("li").addClass("active");
$(".sidebar-menu > li.treeview").addClass("hidden");
if ($(this).attr("url") == "javascript:;") {
var sonlist = $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "']");
sonlist.removeClass("hidden");
var sidenav;
var last_id = $(this).attr("last-id");
if (last_id) {
sidenav = $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "'] a[addtabs='" + last_id + "']");
} else {
sidenav = $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "']:first > a");
}
if (sidenav) {
sidenav.attr("href") != "javascript:;" && sidenav.trigger('click');
}
} else {
}
});
//点击左侧菜单栏
$(document).on('click', '.sidebar-menu li a[addtabs]', function (e) {
var parents = $(this).parentsUntil("ul.sidebar-menu", "li");
var top = parents[parents.length - 1];
var pid = $(top).attr("pid");
if (pid) {
var obj = $("li a[addtabs=" + pid + "]", firstnav);
var last_id = obj.attr("last-id");
if (!last_id || last_id != pid) {
obj.attr("last-id", $(this).attr("addtabs"));
if (!obj.closest("li").hasClass("active")) {
obj.trigger("click");
}
}
}
});
var mobilenav = $(".mobilenav");
$("#firstnav .nav-addtabs li a").each(function () {
mobilenav.append($(this).clone().addClass("btn btn-app"));
});
//点击移动端一级菜单
mobilenav.on("click", "a", function () {
$("a", mobilenav).removeClass("active");
$(this).addClass("active");
$(".sidebar-menu > li.treeview").addClass("hidden");
if ($(this).attr("url") == "javascript:;") {
var sonlist = $(".sidebar-menu > li[pid='" + $(this).attr("addtabs") + "']");
sonlist.removeClass("hidden");
}
});
}
//这一行需要放在点击左侧链接事件之前
var addtabs = Config.referer ? localStorage.getItem("addtabs") : null;
//绑定tabs事件,如果需要点击强制刷新iframe,则请将iframeForceRefresh置为true
nav.addtabs({iframeHeight: "100%", iframeForceRefresh: false, nav: nav});
if ($("ul.sidebar-menu li.active a").size() > 0) {
$("ul.sidebar-menu li.active a").trigger("click");
} else {
if (Config.fastadmin.multiplenav) {
$("li:first > a", firstnav).trigger("click");
} else {
$("ul.sidebar-menu li a[url!='javascript:;']:first").trigger("click");
}
}
//如果是刷新操作则直接返回刷新前的页面
if (Config.referer) {
if (Config.referer === $(addtabs).attr("url")) {
var active = $("ul.sidebar-menu li a[addtabs=" + $(addtabs).attr("addtabs") + "]");
if (multiplenav && active.size() == 0) {
active = $("ul li a[addtabs='" + $(addtabs).attr("addtabs") + "']");
}
if (active.size() > 0) {
active.trigger("click");
} else {
$(addtabs).appendTo(document.body).addClass("hide").trigger("click");
}
} else {
//刷新页面后跳到到刷新前的页面
Userend.api.addtabs(Config.referer);
}
}
var my_skins = [
"skin-blue",
"skin-white",
"skin-red",
"skin-yellow",
"skin-purple",
"skin-green",
"skin-blue-light",
"skin-white-light",
"skin-red-light",
"skin-yellow-light",
"skin-purple-light",
"skin-green-light"
];
setup();
function change_layout(cls) {
$("body").toggleClass(cls);
AdminLTE.layout.fixSidebar();
//Fix the problem with right sidebar and layout boxed
if (cls == "layout-boxed")
AdminLTE.controlSidebar._fix($(".control-sidebar-bg"));
if ($('body').hasClass('fixed') && cls == 'fixed') {
AdminLTE.pushMenu.expandOnHover();
AdminLTE.layout.activate();
}
AdminLTE.controlSidebar._fix($(".control-sidebar-bg"));
AdminLTE.controlSidebar._fix($(".control-sidebar"));
}
function change_skin(cls) {
if (!$("body").hasClass(cls)) {
$("body").removeClass(my_skins.join(' ')).addClass(cls);
localStorage.setItem('userskin', cls);
var cssfile = Config.site.cdnurl + "/assets/css/skins/" + cls + ".css";
$('head').append('<link rel="stylesheet" href="' + cssfile + '" type="text/css" />');
}
return false;
}
function setup() {
var tmp = localStorage.getItem('userskin');
if (tmp && $.inArray(tmp, my_skins))
change_skin(tmp);
// 皮肤切换
$("[data-skin]").on('click', function (e) {
if ($(this).hasClass('knob'))
return;
e.preventDefault();
change_skin($(this).data('skin'));
});
// 布局切换
$("[data-layout]").on('click', function () {
change_layout($(this).data('layout'));
});
// 切换子菜单显示和菜单小图标的显示
$("[data-menu]").on('click', function () {
if ($(this).data("menu") == 'show-submenu') {
$("ul.sidebar-menu").toggleClass("show-submenu");
} else {
nav.toggleClass("disable-top-badge");
}
});
// 右侧控制栏切换
$("[data-controlsidebar]").on('click', function () {
change_layout($(this).data('controlsidebar'));
var slide = !AdminLTE.options.controlSidebarOptions.slide;
AdminLTE.options.controlSidebarOptions.slide = slide;
if (!slide)
$('.control-sidebar').removeClass('control-sidebar-open');
});
// 右侧控制栏背景切换
$("[data-sidebarskin='toggle']").on('click', function () {
var sidebar = $(".control-sidebar");
if (sidebar.hasClass("control-sidebar-dark")) {
sidebar.removeClass("control-sidebar-dark")
sidebar.addClass("control-sidebar-light")
} else {
sidebar.removeClass("control-sidebar-light")
sidebar.addClass("control-sidebar-dark")
}
});
// 菜单栏展开或收起
$("[data-enable='expandOnHover']").on('click', function () {
$(this).attr('disabled', true);
AdminLTE.pushMenu.expandOnHover();
if (!$('body').hasClass('sidebar-collapse'))
$("[data-layout='sidebar-collapse']").click();
});
// 重设选项
if ($('body').hasClass('fixed')) {
$("[data-layout='fixed']").attr('checked', 'checked');
}
if ($('body').hasClass('layout-boxed')) {
$("[data-layout='layout-boxed']").attr('checked', 'checked');
}
if ($('body').hasClass('sidebar-collapse')) {
$("[data-layout='sidebar-collapse']").attr('checked', 'checked');
}
if ($('ul.sidebar-menu').hasClass('show-submenu')) {
$("[data-menu='show-submenu']").attr('checked', 'checked');
}
if (nav.hasClass('disable-top-badge')) {
$("[data-menu='disable-top-badge']").attr('checked', 'checked');
}
}
$(window).resize();
},
login: function () {
var lastlogin = localStorage.getItem("lastlogin");
if (lastlogin) {
lastlogin = JSON.parse(lastlogin);
$("#profile-img").attr("src", Userend.api.cdnurl(lastlogin.avatar));
$("#profile-name").val(lastlogin.username);
}
//让错误提示框居中
Fast.config.toastr.positionClass = "toast-top-center";
//本地验证未通过时提示
$("#login-form").data("validator-options", {
invalid: function (form, errors) {
$.each(errors, function (i, j) {
Toastr.error(j);
});
},
target: '#errtips'
});
//为表单绑定事件
Form.api.bindevent($("#login-form"), function (data) {
localStorage.setItem("lastlogin", JSON.stringify({
id: data.id,
username: data.username,
avatar: data.avatar
}));
location.href = Userend.api.fixurl(data.url);
});
},
};
return Controller;
});

View File

@ -0,0 +1,57 @@
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'upload'], function ($, undefined, Backend, Table, Form, Upload) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
search: true,
advancedSearch: true,
pagination: true,
extend: {
"index_url": "general/profile/index",
"add_url": "",
"edit_url": "",
"del_url": "",
"multi_url": "",
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
columns: [
[
{field: 'id', title: 'ID'},
{field: 'title', title: __('Title')},
{field: 'url', title: __('Url'), align: 'left', formatter: Table.api.formatter.url},
{field: 'ip', title: __('ip'), formatter:Table.api.formatter.search},
{field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true},
]
],
commonSearch: false
});
// 为表格绑定事件
Table.api.bindevent(table);//当内容渲染完成后
// 给上传按钮添加上传成功事件
$("#plupload-avatar").data("upload-success", function (data) {
var url = Backend.api.cdnurl(data.url);
$(".profile-user-img").prop("src", url);
Toastr.success("上传成功!");
});
// 给表单绑定事件
Form.api.bindevent($("#update-form"), function () {
$("input[name='row[password]']").val('');
var url = Backend.api.cdnurl($("#c-avatar").val());
top.window.$(".user-panel .image img,.user-menu > a > img,.user-header > img").prop("src", url);
return true;
});
},
};
return Controller;
});