作者 Karson

新增命令行一键生成API文档功能

新增插件绑定二级域名功能
新增加载JS公用模块
新增命令行创建插件自动生成菜单功能
新增后台菜单Fast.api.refreshmenu
新增后台菜单在数据变更后自动刷新的功能
新增require.min.js压缩版
新增从Headers中读取授权token的功能
新增Form.events.daterangepicker时间区别事件
新增Form表单提示成功和失败的回调事件
新增Fast.api.getrowbyid和Fast.api.getrowbyindex方法
新增commonsearch的find_in_set类型搜索
新增Menu::export的方法
新增php think api一键生成API文档功能
新增php think min的压缩参数和调试功能

优化API模块生产环境下错误信息的显示
优化移动端显示移除顶部Logo一行
优化bower.json和composer.json的版本依赖
优化插件管理列表显示
优化后台控制区多作的选项卡数据
优化CRUD生成的复选框样式及文字
优化规则管理的列表显示
优化第三方前端资源,移除冗余资源

修复在启用域名部署下的BUG
修复API初始化接口的BUG
修复会员积分日志模型BUG
修复多语言切换不存在的BUG
修复Backend.php中multi操作不触发模型事件的BUG
正在显示 73 个修改的文件 包含 2313 行增加510 行删除

要显示太多修改。

为保证性能只显示 73 of 73+ 个文件。

{
"directory" : "public/assets/libs"
"directory" : "public/assets/libs",
"ignoredDependencies": [
"file-saver",
"html2canvas",
"jspdf",
"jspdf-autotable"
]
}
\ No newline at end of file
... ...
... ... @@ -16,10 +16,12 @@ FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。
* 基于`Bower`进行前端组件包管理
* 数据库表一键生成`CRUD`,包括控制器、模型、视图、JS、语言包、菜单等
* 一键压缩打包JS和CSS文件,一键CDN静态资源部署
* 一键生成API接口文档
* 强大的插件扩展功能,在线安装卸载升级插件
* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证
* 二级域名部署支持,同时域名支持绑定到插件
* 多语言支持,服务端及客户端支持
* 强大的第三方插件支持(CMS、博客、文档生成)
* 强大的第三方模块支持(CMS、博客、文档生成)
* 整合第三方短信接口(阿里云、创蓝短信)
* 无缝整合第三方云存储(七牛、阿里云OSS、又拍云)功能
* 第三方登录(QQ、微信、微博)整合
... ...
... ... @@ -2,14 +2,17 @@
namespace app\admin\command;
use app\common\library\Menu;
use think\addons\AddonException;
use think\addons\Service;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Db;
use think\Exception;
use think\exception\PDOException;
class Addon extends Command
{
... ... @@ -63,14 +66,43 @@ class Addon extends Command
rmdirs($addonDir);
}
mkdir($addonDir);
mkdir($addonDir . DS . 'controller');
$menuList = Menu::export($name);
$createMenu = $this->getCreateMenu($menuList);
$prefix = Config::get('database.prefix');
$createTableSql = '';
try
{
$result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;");
if (isset($result[0]) && isset($result[0]['Create Table']))
{
$createTableSql = $result[0]['Create Table'];
}
}
catch (PDOException $e)
{
}
$data = [
'name' => $name,
'addon' => $name,
'addonClassName' => ucfirst($name)
'name' => $name,
'addon' => $name,
'addonClassName' => ucfirst($name),
'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu, "\t") . ";\n\tMenu::create(\$menu);" : '',
'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '',
'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '',
'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '',
];
$this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php');
$this->writeToFile("config", $data, $addonDir . 'config.php');
$this->writeToFile("info", $data, $addonDir . 'info.ini');
$this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php');
if ($createTableSql)
{
$createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql);
file_put_contents($addonDir . 'install.sql', $createTableSql);
}
$output->info("Create Successed!");
break;
case 'disable':
... ... @@ -257,6 +289,37 @@ class Addon extends Command
}
/**
* 获取创建菜单的数组
* @param array $menu
* @return array
*/
protected function getCreateMenu($menu)
{
$result = [];
foreach ($menu as $k => & $v)
{
$arr = [
'name' => $v['name'],
'title' => $v['title'],
];
if ($v['icon'] != 'fa fa-circle-o')
{
$arr['icon'] = $v['icon'];
}
if ($v['ismenu'])
{
$arr['ismenu'] = $v['ismenu'];
}
if (isset($v['childlist']) && $v['childlist'])
{
$arr['sublist'] = $this->getCreateMenu($v['childlist']);
}
$result[] = $arr;
}
return $result;
}
/**
* 写入到文件
* @param string $name
* @param array $data
... ...
... ... @@ -2,6 +2,7 @@
namespace addons\{%name%};
use app\common\library\Menu;
use think\Addons;
/**
... ... @@ -16,6 +17,7 @@ class {%addonClassName%} extends Addons
*/
public function install()
{
{%addonInstallMenu%}
return true;
}
... ... @@ -25,6 +27,7 @@ class {%addonClassName%} extends Addons
*/
public function uninstall()
{
{%addonUninstallMenu%}
return true;
}
... ... @@ -34,6 +37,7 @@ class {%addonClassName%} extends Addons
*/
public function enable()
{
{%addonEnableMenu%}
return true;
}
... ... @@ -43,6 +47,7 @@ class {%addonClassName%} extends Addons
*/
public function disable()
{
{%addonDisableMenu%}
return true;
}
... ...
<?php
namespace addons\{%addon%}\controller;
use think\addons\Controller;
class Index extends Controller
{
public function index()
{
$this->error("当前插件暂无前台页面");
}
}
... ...
name = {%name%}
title = 插件名称
title = 插件名称({%name%})
intro = FastAdmin插件
author = yourname
website = http://www.fastadmin.net
... ...
<?php
namespace app\admin\command;
use app\admin\command\Api\library\Builder;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
class Api extends Command
{
protected function configure()
{
$site = Config::get('site');
$this
->setName('api')
->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '')
->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api')
->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html')
->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html')
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false)
->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'])
->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name'])
->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null)
->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn')
->setDescription('Compress js and css file');
}
protected function execute(Input $input, Output $output)
{
$apiDir = __DIR__ . DS . 'Api' . DS;
$force = $input->getOption('force');
$url = $input->getOption('url');
$language = $input->getOption('language');
$langFile = $apiDir . 'lang' . DS . $language . '.php';
if (!is_file($langFile))
{
throw new Exception('language file not found');
}
$lang = include $langFile;
// 目标目录
$output_dir = ROOT_PATH . 'public' . DS;
$output_file = $output_dir . $input->getOption('output');
if (is_file($output_file) && !$force)
{
throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true ");
}
// 模板文件
$template_dir = $apiDir . 'template' . DS;
$template_file = $template_dir . $input->getOption('template');
if (!is_file($template_file))
{
throw new Exception('template file not found');
}
// 额外的类
$classes = $input->getOption('class');
// 标题
$title = $input->getOption('title');
// 作者
$author = $input->getOption('author');
// 模块
$module = $input->getOption('module');
$moduleDir = APP_PATH . $module . DS;
if (!is_dir($moduleDir))
{
throw new Exception('module not found');
}
$controllerDir = $moduleDir . Config::get('url_controller_layer') . DS;
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file)
{
if (!$file->isDir())
{
$filePath = $file->getRealPath();
$classes[] = $this->get_class_from_file($filePath);
}
}
$config = [
'title' => $title,
'author' => $author,
'description' => '',
'apiurl' => $url,
];
$builder = new Builder($classes);
$content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]);
if (!file_put_contents($output_file, $content))
{
throw new Exception('Cannot save the content to ' . $output_file);
}
$output->info("Build Successed!");
}
/**
* get full qualified class name
*
* @param string $path_to_file
* @author JBYRNE http://jarretbyrne.com/2015/06/197/
* @return string
*/
protected function get_class_from_file($path_to_file)
{
//Grab the contents of the file
$contents = file_get_contents($path_to_file);
//Start with a blank namespace and class
$namespace = $class = "";
//Set helper values to know that we have found the namespace/class token and need to collect the string values after them
$getting_namespace = $getting_class = false;
//Go through each token and evaluate it as necessary
foreach (token_get_all($contents) as $token)
{
//If this token is the namespace declaring, then flag that the next tokens will be the namespace name
if (is_array($token) && $token[0] == T_NAMESPACE)
{
$getting_namespace = true;
}
//If this token is the class declaring, then flag that the next tokens will be the class name
if (is_array($token) && $token[0] == T_CLASS)
{
$getting_class = true;
}
//While we're grabbing the namespace name...
if ($getting_namespace === true)
{
//If the token is a string or the namespace separator...
if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR]))
{
//Append the token's value to the name of the namespace
$namespace .= $token[1];
}
else if ($token === ';')
{
//If the token is the semicolon, then we're done with the namespace declaration
$getting_namespace = false;
}
}
//While we're grabbing the class name...
if ($getting_class === true)
{
//If the token is a string, it's the name of the class
if (is_array($token) && $token[0] == T_STRING)
{
//Store the token's value as the class name
$class = $token[1];
//Got what we need, stope here
break;
}
}
}
//Build the fully-qualified class name and return it
return $namespace ? $namespace . '\\' . $class : $class;
}
}
... ...
<?php
return [
'Info' => '基础信息',
'Sandbox' => '在线测试',
'Sampleoutput' => '返回示例',
'Headers' => 'Headers',
'Parameters' => '参数',
'Body' => '正文',
'Name' => '名称',
'Type' => '类型',
'Required' => '必选',
'Description' => '描述',
'Send' => '提交',
'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中',
'Apiurltips' => 'API接口URL',
'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中',
'ReturnHeaders' => '响应头',
'ReturnParameters' => '返回参数',
'Response' => '响应输出',
];
... ...
<?php
namespace app\admin\command\Api\library;
use think\Config;
/**
* @website https://github.com/calinrada/php-apidoc
* @author Calin Rada <rada.calin@gmail.com>
* @author Karson <karsonzhang@163.com>
*/
class Builder
{
/**
*
* @var \think\View
*/
public $view = null;
/**
* parse classes
* @var array
*/
protected $classes = [];
/**
*
* @param array $classes
*/
public function __construct($classes = [])
{
$this->classes = array_merge($this->classes, $classes);
$this->view = \think\View::instance(Config::get('template'), Config::get('view_replace_str'));
}
protected function extractAnnotations()
{
$st_output = [];
foreach ($this->classes as $class)
{
$st_output[] = Extractor::getAllClassAnnotations($class);
}
return end($st_output);
}
protected function generateHeadersTemplate($docs)
{
if (!isset($docs['ApiHeaders']))
{
return [];
}
$headerslist = array();
foreach ($docs['ApiHeaders'] as $params)
{
$tr = array(
'name' => $params['name'],
'type' => $params['type'],
'sample' => isset($params['sample']) ? $params['sample'] : '',
'required' => isset($params['required']) ? $params['required'] : false,
'description' => isset($params['description']) ? $params['description'] : '',
);
$headerslist[] = $tr;
}
return $headerslist;
}
protected function generateParamsTemplate($docs)
{
if (!isset($docs['ApiParams']))
{
return [];
}
$paramslist = array();
foreach ($docs['ApiParams'] as $params)
{
$tr = array(
'name' => $params['name'],
'type' => isset($params['type']) ? $params['type'] : 'string',
'sample' => isset($params['sample']) ? $params['sample'] : '',
'required' => isset($params['required']) ? $params['required'] : true,
'description' => isset($params['description']) ? $params['description'] : '',
);
$paramslist[] = $tr;
}
return $paramslist;
}
protected function generateReturnHeadersTemplate($docs)
{
if (!isset($docs['ApiReturnHeaders']))
{
return [];
}
$headerslist = array();
foreach ($docs['ApiReturnHeaders'] as $params)
{
$tr = array(
'name' => $params['name'],
'type' => 'string',
'sample' => isset($params['sample']) ? $params['sample'] : '',
'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No',
'description' => isset($params['description']) ? $params['description'] : '',
);
$headerslist[] = $tr;
}
return $headerslist;
}
protected function generateReturnParamsTemplate($st_params)
{
if (!isset($st_params['ApiReturnParams']))
{
return [];
}
$paramslist = array();
foreach ($st_params['ApiReturnParams'] as $params)
{
$tr = array(
'name' => $params['name'],
'type' => isset($params['type']) ? $params['type'] : 'string',
'sample' => isset($params['sample']) ? $params['sample'] : '',
'description' => isset($params['description']) ? $params['description'] : '',
);
$paramslist[] = $tr;
}
return $paramslist;
}
protected function generateBadgeForMethod($data)
{
$method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]);
$labes = array(
'POST' => 'label-primary',
'GET' => 'label-success',
'PUT' => 'label-warning',
'DELETE' => 'label-danger',
'PATCH' => 'label-default',
'OPTIONS' => 'label-info'
);
return isset($labes[$method]) ? $labes[$method] : $labes['GET'];
}
public function parse()
{
$annotations = $this->extractAnnotations();
$counter = 0;
$section = null;
$docslist = [];
foreach ($annotations as $class => $methods)
{
foreach ($methods as $name => $docs)
{
if (isset($docs['ApiSector'][0]))
{
$section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0];
}
else
{
$section = $class;
}
if (0 === count($docs))
{
continue;
}
$docslist[$section][] = [
'id' => $counter,
'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0],
'method_label' => $this->generateBadgeForMethod($docs),
'section' => $section,
'route' => is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0],
'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0],
'body' => isset($docs['ApiBody'][0]) ? is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0] : '',
'headerslist' => $this->generateHeadersTemplate($docs),
'paramslist' => $this->generateParamsTemplate($docs),
'returnheaderslist' => $this->generateReturnHeadersTemplate($docs),
'returnparamslist' => $this->generateReturnParamsTemplate($docs),
'return' => isset($docs['ApiReturn']) ? is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0] : '',
];
$counter++;
}
}
return $docslist;
}
public function getView()
{
return $this->view;
}
/**
* 渲染
* @param string $template
* @param array $vars
* @return string
*/
public function render($template, $vars = [])
{
$docslist = $this->parse();
return $this->view->display(file_get_contents($template), array_merge($vars, ['docslist' => $docslist]));
}
}
... ...
<?php
namespace app\admin\command\Api\library;
/**
* Class imported from https://github.com/eriknyk/Annotations
* @author Erik Amaru Ortiz https://github.com/eriknyk‎
*
* @license http://opensource.org/licenses/bsd-license.php The BSD License
* @author Calin Rada <rada.calin@gmail.com>
*/
class Extractor
{
/**
* Static array to store already parsed annotations
* @var array
*/
private static $annotationCache;
/**
* Indicates that annotations should has strict behavior, 'false' by default
* @var boolean
*/
private $strict = false;
/**
* Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects()
* @var string
*/
public $defaultNamespace = '';
/**
* Sets strict variable to true/false
* @param bool $value boolean value to indicate that annotations to has strict behavior
*/
public function setStrict($value)
{
$this->strict = (bool) $value;
}
/**
* Sets default namespace to use in object instantiation
* @param string $namespace default namespace
*/
public function setDefaultNamespace($namespace)
{
$this->defaultNamespace = $namespace;
}
/**
* Gets default namespace used in object instantiation
* @return string $namespace default namespace
*/
public function getDefaultAnnotationNamespace()
{
return $this->defaultNamespace;
}
/**
* Gets all anotations with pattern @SomeAnnotation() from a given class
*
* @param string $className class name to get annotations
* @return array self::$annotationCache all annotated elements
*/
public static function getClassAnnotations($className)
{
if (!isset(self::$annotationCache[$className]))
{
$class = new \ReflectionClass($className);
self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
}
return self::$annotationCache[$className];
}
public static function getAllClassAnnotations($className)
{
$class = new \ReflectionClass($className);
foreach ($class->getMethods() as $object)
{
self::$annotationCache['annotations'][$className][$object->name] = self::getMethodAnnotations($className, $object->name);
}
return self::$annotationCache['annotations'];
}
/**
* Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class
*
* @param string $className class name
* @param string $methodName method name to get annotations
* @return array self::$annotationCache all annotated elements of a method given
*/
public static function getMethodAnnotations($className, $methodName)
{
if (!isset(self::$annotationCache[$className . '::' . $methodName]))
{
try
{
$method = new \ReflectionMethod($className, $methodName);
$class = new \ReflectionClass($className);
if (!$method->isPublic() || $method->isConstructor())
{
$annotations = array();
}
else
{
$annotations = self::consolidateAnnotations($method, $class);
}
}
catch (\ReflectionException $e)
{
$annotations = array();
}
self::$annotationCache[$className . '::' . $methodName] = $annotations;
}
return self::$annotationCache[$className . '::' . $methodName];
}
/**
* Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class
* and instance its abcAnnotation class
*
* @param string $className class name
* @param string $methodName method name to get annotations
* @return array self::$annotationCache all annotated objects of a method given
*/
public function getMethodAnnotationsObjects($className, $methodName)
{
$annotations = $this->getMethodAnnotations($className, $methodName);
$objects = array();
$i = 0;
foreach ($annotations as $annotationClass => $listParams)
{
$annotationClass = ucfirst($annotationClass);
$class = $this->defaultNamespace . $annotationClass . 'Annotation';
// verify is the annotation class exists, depending if Annotations::strict is true
// if not, just skip the annotation instance creation.
if (!class_exists($class))
{
if ($this->strict)
{
throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class));
}
else
{
// silent skip & continue
continue;
}
}
if (empty($objects[$annotationClass]))
{
$objects[$annotationClass] = new $class();
}
foreach ($listParams as $params)
{
if (is_array($params))
{
foreach ($params as $key => $value)
{
$objects[$annotationClass]->set($key, $value);
}
}
else
{
$objects[$annotationClass]->set($i++, $params);
}
}
}
return $objects;
}
private static function consolidateAnnotations($method, $class)
{
$dockblockClass = $class->getDocComment();
$docblockMethod = $method->getDocComment();
$methodName = $method->getName();
$methodAnnotations = self::parseAnnotations($docblockMethod);
$classAnnotations = self::parseAnnotations($dockblockClass);
if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty')
{
return [];
}
$properties = $class->getDefaultProperties();
$noNeedLogin = isset($properties['noNeedLogin']) ? is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']] : [];
$noNeedRight = isset($properties['noNeedRight']) ? is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']] : [];
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr);
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr);
$methodTitle = isset($methodArr[1]) && isset($methodArr[1][0]) ? $methodArr[1][0] : '';
$classTitle = isset($classArr[1]) && isset($classArr[1][0]) ? $classArr[1][0] : '';
if (!isset($methodAnnotations['ApiMethod']))
{
$methodAnnotations['ApiMethod'] = ['get'];
}
if (!isset($methodAnnotations['ApiSummary']))
{
$methodAnnotations['ApiSummary'] = [$methodTitle];
}
if ($methodAnnotations)
{
foreach ($classAnnotations as $name => $valueClass)
{
if (count($valueClass) !== 1)
{
continue;
}
if ($name === 'ApiRoute')
{
if (isset($methodAnnotations[$name]))
{
$methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]];
}
else
{
$methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()];
}
}
if ($name === 'ApiSector')
{
$methodAnnotations[$name] = $valueClass;
}
}
}
if (!isset($methodAnnotations['ApiTitle']))
{
$methodAnnotations['ApiTitle'] = [$methodTitle];
}
if (!isset($methodAnnotations['ApiRoute']))
{
$urlArr = [];
$className = $class->getName();
list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className);
$prefixArr = explode('\\', $prefix);
$suffixArr = explode('\\', $suffix);
if ($prefixArr[0] == \think\Config::get('app_namespace'))
{
$prefixArr[0] = '';
}
$urlArr = array_merge($urlArr, $prefixArr);
$urlArr[] = implode('.', array_map(function($item) {
return \think\Loader::parseName($item);
}, $suffixArr));
$urlArr[] = $method->getName();
$methodAnnotations['ApiRoute'] = [implode('/', $urlArr)];
}
if (!isset($methodAnnotations['ApiSector']))
{
$methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : [$classTitle];
}
if (!isset($methodAnnotations['ApiParams']))
{
$params = self::parseCustomAnnotations($docblockMethod, 'param');
foreach ($params as $k => $v)
{
$arr = explode(' ', preg_replace("/[\s]+/", " ", $v));
$methodAnnotations['ApiParams'][] = [
'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '',
'nullable' => false,
'type' => isset($arr[0]) ? $arr[0] : 'string',
'description' => isset($arr[2]) ? $arr[2] : ''
];
}
}
$methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)];
$methodAnnotations['ApiPermissionRight'] = [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)];
return $methodAnnotations;
}
/**
* Parse annotations
*
* @param string $docblock
* @param string $name
* @return array parsed annotations params
*/
private static function parseCustomAnnotations($docblock, $name = 'param')
{
$annotations = array();
$docblock = substr($docblock, 3, -2);
if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches))
{
foreach ($matches[1] as $k => $v)
{
$annotations[] = $v;
}
}
return $annotations;
}
/**
* Parse annotations
*
* @param string $docblock
* @return array parsed annotations params
*/
private static function parseAnnotations($docblock)
{
$annotations = array();
// Strip away the docblock header and footer to ease parsing of one line annotations
$docblock = substr($docblock, 3, -2);
if (preg_match_all('/@(?<name>[A-Za-z_-]+)[\s\t]*\((?<args>(?:(?!\)).)*)\)\r?/s', $docblock, $matches))
{
$numMatches = count($matches[0]);
for ($i = 0; $i < $numMatches; ++$i)
{
// annotations has arguments
if (isset($matches['args'][$i]))
{
$argsParts = trim($matches['args'][$i]);
$name = $matches['name'][$i];
$value = self::parseArgs($argsParts);
}
else
{
$value = array();
}
$annotations[$name][] = $value;
}
}
return $annotations;
}
/**
* Parse individual annotation arguments
*
* @param string $content arguments string
* @return array annotated arguments
*/
private static function parseArgs($content)
{
// Replace initial stars
$content = preg_replace('/^\s*\*/m', '', $content);
$data = array();
$len = strlen($content);
$i = 0;
$var = '';
$val = '';
$level = 1;
$prevDelimiter = '';
$nextDelimiter = '';
$nextToken = '';
$composing = false;
$type = 'plain';
$delimiter = null;
$quoted = false;
$tokens = array('"', '"', '{', '}', ',', '=');
while ($i <= $len)
{
$prev_c = substr($content, $i - 1, 1);
$c = substr($content, $i++, 1);
if ($c === '"' && $prev_c !== "\\")
{
$delimiter = $c;
//open delimiter
if (!$composing && empty($prevDelimiter) && empty($nextDelimiter))
{
$prevDelimiter = $nextDelimiter = $delimiter;
$val = '';
$composing = true;
$quoted = true;
}
else
{
// close delimiter
if ($c !== $nextDelimiter)
{
throw new Exception(sprintf(
"Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c
));
}
// validating syntax
if ($i < $len)
{
if (',' !== substr($content, $i, 1) && '\\' !== $prev_c)
{
throw new Exception(sprintf(
"Parse Error: missing comma separator near: ...%s<--", substr($content, ($i - 10), $i)
));
}
}
$prevDelimiter = $nextDelimiter = '';
$composing = false;
$delimiter = null;
}
}
elseif (!$composing && in_array($c, $tokens))
{
switch ($c)
{
case '=':
$prevDelimiter = $nextDelimiter = '';
$level = 2;
$composing = false;
$type = 'assoc';
$quoted = false;
break;
case ',':
$level = 3;
// If composing flag is true yet,
// it means that the string was not enclosed, so it is parsing error.
if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter))
{
throw new Exception(sprintf(
"Parse Error: enclosing error -> expected: [%s], given: [%s]", $nextDelimiter, $c
));
}
$prevDelimiter = $nextDelimiter = '';
break;
case '{':
$subc = '';
$subComposing = true;
while ($i <= $len)
{
$c = substr($content, $i++, 1);
if (isset($delimiter) && $c === $delimiter)
{
throw new Exception(sprintf(
"Parse Error: Composite variable is not enclosed correctly."
));
}
if ($c === '}')
{
$subComposing = false;
break;
}
$subc .= $c;
}
// if the string is composing yet means that the structure of var. never was enclosed with '}'
if ($subComposing)
{
throw new Exception(sprintf(
"Parse Error: Composite variable is not enclosed correctly. near: ...%s'", $subc
));
}
$val = self::parseArgs($subc);
break;
}
}
else
{
if ($level == 1)
{
$var .= $c;
}
elseif ($level == 2)
{
$val .= $c;
}
}
if ($level === 3 || $i === $len)
{
if ($type == 'plain' && $i === $len)
{
$data = self::castValue($var);
}
else
{
$data[trim($var)] = self::castValue($val, !$quoted);
}
$level = 1;
$var = $val = '';
$composing = false;
$quoted = false;
}
}
return $data;
}
/**
* Try determinate the original type variable of a string
*
* @param string $val string containing possibles variables that can be cast to bool or int
* @param boolean $trim indicate if the value passed should be trimmed after to try cast
* @return mixed returns the value converted to original type if was possible
*/
private static function castValue($val, $trim = false)
{
if (is_array($val))
{
foreach ($val as $key => $value)
{
$val[$key] = self::castValue($value);
}
}
elseif (is_string($val))
{
if ($trim)
{
$val = trim($val);
}
$val = stripslashes($val);
$tmp = strtolower($val);
if ($tmp === 'false' || $tmp === 'true')
{
$val = $tmp === 'true';
}
elseif (is_numeric($val))
{
return $val + 0;
}
unset($tmp);
}
return $val;
}
}
... ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="{$config.author}">
<title>{$config.title}</title>
<link href="https://cdn.bootcss.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
body { padding-top: 70px; margin-bottom: 15px; }
.tab-pane { padding-top: 10px; }
.mt0 { margin-top: 0px; }
.footer { font-size: 12px; color: #666; }
.label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; }
.string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
.popover { max-width: 400px; max-height: 400px; overflow-y: auto;}
</style>
</head>
<body>
<!-- Fixed navbar -->
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="http://www.fastadmin.net" target="_blank">{$config.title}</a>
</div>
<div class="navbar-collapse collapse">
<form class="navbar-form navbar-right">
<div class="form-group">
Token:
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Tokentips}" placeholder="token" id="token" />
</div>
<div class="form-group">
Apiurl:
</div>
<div class="form-group">
<input id="apiUrl" type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Apiurltips}" placeholder="https://api.mydomain.com" value="{$config.apiurl}" />
</div>
<div class="form-group">
<button type="button" class="btn btn-success btn-sm" data-toggle="tooltip" title="{$lang.Savetips}" id="save_data">
<span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
</button>
</div>
</form>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container">
<div class="panel-group" id="accordion">
{foreach name="docslist" id="docs"}
<h2>{$key}</h2>
<hr>
{foreach name="docs" id="api" }
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<span class="label {$api.method_label}">{$api.method|strtoupper}</span> <a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.route}</a>
</h4>
</div>
<div id="collapseOne{$api.id}" class="panel-collapse collapse">
<div class="panel-body">
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="doctab{$api.id}">
<li class="active"><a href="#info{$api.id}" data-toggle="tab">{$lang.Info}</a></li>
<li><a href="#sandbox{$api.id}" data-toggle="tab">{$lang.Sandbox}</a></li>
<li><a href="#sample{$api.id}" data-toggle="tab">{$lang.Sampleoutput}</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="info{$api.id}">
<div class="well">
{$api.summary}
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Headers}</strong></div>
<div class="panel-body">
{if $api.headerslist}
<table class="table table-hover">
<thead>
<tr>
<th>{$lang.Name}</th>
<th>{$lang.Type}</th>
<th>{$lang.Required}</th>
<th>{$lang.Description}</th>
</tr>
</thead>
<tbody>
{foreach name="api['headerslist']" id="header"}
<tr>
<td>{$header.name}</td>
<td>{$header.type}</td>
<td>{$header.required?'是':'否'}</td>
<td>{$header.description}</td>
</tr>
{/foreach}
</tbody>
</table>
{else /}
{/if}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
<div class="panel-body">
{if $api.paramslist}
<table class="table table-hover">
<thead>
<tr>
<th>{$lang.Name}</th>
<th>{$lang.Type}</th>
<th>{$lang.Required}</th>
<th>{$lang.Description}</th>
</tr>
</thead>
<tbody>
{foreach name="api['paramslist']" id="param"}
<tr>
<td>{$param.name}</td>
<td>{$param.type}</td>
<td>{:$param.required?'是':'否'}</td>
<td>{$param.description}</td>
</tr>
{/foreach}
</tbody>
</table>
{else /}
{/if}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Body}</strong></div>
<div class="panel-body">
{$api.body|default='无'}
</div>
</div>
</div><!-- #info -->
<div class="tab-pane" id="sandbox{$api.id}">
<div class="row">
<div class="col-md-12">
{if $api.headerslist}
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Headers}</strong></div>
<div class="panel-body">
<div class="headers">
{foreach name="api['headerslist']" id="param"}
<div class="form-group">
<label class="control-label" for="{$param.name}">{$param.name}</label>
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}">
</div>
{/foreach}
</div>
</div>
</div>
{/if}
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Parameters}</strong></div>
<div class="panel-body">
<form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}">
{if $api.paramslist}
{foreach name="api['paramslist']" id="param"}
<div class="form-group">
<label class="control-label" for="{$param.name}">{$param.name}</label>
<input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}">
</div>
{/foreach}
{else /}
<div class="form-group">
</div>
{/if}
<div class="form-group">
<button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button>
</div>
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.Response}</strong></div>
<div class="panel-body">
<div class="row">
<div class="col-md-12" style="overflow-x:auto">
<pre id="response_headers{$api.id}"></pre>
<pre id="response{$api.id}"></pre>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div>
<div class="panel-body">
{if $api.returnparamslist}
<table class="table table-hover">
<thead>
<tr>
<th>{$lang.Name}</th>
<th>{$lang.Type}</th>
<th>{$lang.Description}</th>
</tr>
</thead>
<tbody>
{foreach name="api['returnparamslist']" id="param"}
<tr>
<td>{$param.name}</td>
<td>{$param.type}</td>
<td>{$param.description}</td>
</tr>
{/foreach}
</tbody>
</table>
{else /}
{/if}
</div>
</div>
</div>
</div>
</div><!-- #sandbox -->
<div class="tab-pane" id="sample{$api.id}">
<div class="row">
<div class="col-md-12">
<pre id="sample_response{$api.id}">{$api.return|default='无'}</pre>
</div>
</div>
</div><!-- #sample -->
</div><!-- .tab-content -->
</div>
</div>
</div>
{/foreach}
{/foreach}
</div>
<hr>
<div class="row mt0 footer">
<div class="col-md-6" align="left">
Generated on {:date('Y-m-d H:i:s')}
</div>
<div class="col-md-6" align="right">
<a href="http://www.fastadmin.net" target="_blank">FastAdmin</a>
</div>
</div>
</div> <!-- /container -->
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script type="text/javascript">
function syntaxHighlight(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
function prepareStr(str) {
try {
return syntaxHighlight(JSON.stringify(JSON.parse(str.replace(/'/g, '"')), null, 2));
} catch (e) {
return str;
}
}
var storage = (function () {
var uid = new Date;
var storage;
var result;
try {
(storage = window.localStorage).setItem(uid, uid);
result = storage.getItem(uid) == uid;
storage.removeItem(uid);
return result && storage;
} catch (exception) {
}
}());
$.fn.serializeObject = function ()
{
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (!this.value) {
return;
}
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
$(document).ready(function () {
if (storage) {
$('#token').val(storage.getItem('token'));
$('#apiUrl').val(storage.getItem('apiUrl'));
}
$('[data-toggle="tooltip"]').tooltip({
placement: 'bottom'
});
$('code[id^=response]').hide();
$.each($('pre[id^=sample_response],pre[id^=sample_post_body]'), function () {
if ($(this).html() == 'NA') {
return;
}
var str = prepareStr($(this).html());
$(this).html(str);
});
$("[data-toggle=popover]").popover({placement: 'right'});
$('[data-toggle=popover]').on('shown.bs.popover', function () {
var $sample = $(this).parent().find(".popover-content"),
str = $(this).data('content');
if (typeof str == "undefined" || str === "") {
return;
}
var str = prepareStr(str);
$sample.html('<pre>' + str + '</pre>');
});
$('body').on('click', '#save_data', function (e) {
if (storage) {
storage.setItem('token', $('#token').val());
storage.setItem('apiUrl', $('#apiUrl').val());
} else {
alert('Your browser does not support local storage');
}
});
$('body').on('click', '.send', function (e) {
e.preventDefault();
var form = $(this).closest('form');
//added /g to get all the matched params instead of only first
var matchedParamsInRoute = $(form).attr('action').match(/[^{]+(?=\})/g);
var theId = $(this).attr('rel');
//keep a copy of action attribute in order to modify the copy
//instead of the initial attribute
var url = $(form).attr('action');
var serializedData = new FormData();
$(form).find('input').each(function (i, input) {
if ($(input).attr('type') == 'file') {
serializedData.append($(input).attr('name'), $(input)[0].files[0]);
} else {
serializedData.append($(input).attr('name'), $(input).val())
}
});
var index, key, value;
if (matchedParamsInRoute) {
for (index = 0; index < matchedParamsInRoute.length; ++index) {
try {
key = matchedParamsInRoute[index];
value = serializedData[key];
if (typeof value == "undefined")
value = "";
url = url.replace("{" + key + "}", value);
delete serializedData[key];
} catch (err) {
console.log(err);
}
}
}
var headers = {};
var token = $('#token').val();
if (token.length > 0) {
headers[token] = token;
}
$("#sandbox" + theId + " .headers input[type=text]").each(function () {
val = $(this).val();
if (val.length > 0) {
headers[$(this).prop('name')] = val;
}
});
$.ajax({
url: $('#apiUrl').val() + url,
data: $(form).attr('method') == 'get' ? $(form).serialize() : serializedData,
type: $(form).attr('method') + '',
dataType: 'json',
contentType: false,
processData: false,
headers: headers,
success: function (data, textStatus, xhr) {
if (typeof data === 'object') {
var str = JSON.stringify(data, null, 2);
$('#response' + theId).html(syntaxHighlight(str));
} else {
$('#response' + theId).html(data || '');
}
$('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
$('#response' + theId).show();
},
error: function (xhr, textStatus, error) {
try {
var str = JSON.stringify($.parseJSON(xhr.responseText), null, 2);
} catch (e) {
var str = xhr.responseText;
}
$('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders());
$('#response' + theId).html(syntaxHighlight(str));
$('#response' + theId).show();
}
});
return false;
});
});
</script>
</body>
</html>
... ...
... ... @@ -580,8 +580,8 @@ class Crud extends Command
}
$formAddElement = $formEditElement = Form::hidden($fieldName, $no, array_merge(['checked' => ''], $attrArr));
$attrArr['id'] = $fieldName . "-switch";
$formAddElement .= sprintf(Form::label("{$attrArr['id']}", "%s abcdefg"), Form::checkbox($fieldName, $yes, $defaultValue === $yes, $attrArr));
$formEditElement .= sprintf(Form::label("{$attrArr['id']}", "%s abcdefg"), Form::checkbox($fieldName, $yes, 0, $attrArr));
$formAddElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class'=>'control-label']), Form::checkbox($fieldName, $yes, $defaultValue === $yes, $attrArr));
$formEditElement .= sprintf(Form::label("{$attrArr['id']}", "%s {:__('Yes')}", ['class'=>'control-label']), Form::checkbox($fieldName, $yes, 0, $attrArr));
$formEditElement = str_replace('type="checkbox"', 'type="checkbox" {in name="' . "\$row.{$field}" . '" value="' . $yes . '"}checked{/in}', $formEditElement);
}
else if ($inputType == 'citypicker')
... ... @@ -963,6 +963,7 @@ EOD;
if ($content || !Lang::has($field))
{
$itemArr = [];
$content = str_replace(',', ',', $content);
if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false)
{
list($fieldLang, $item) = explode(':', $content);
... ... @@ -997,6 +998,7 @@ EOD;
/**
* 读取数据和语言数组列表
* @param array $arr
* @param boolean $withTpl
* @return array
*/
protected function getLangArray($arr, $withTpl = TRUE)
... ... @@ -1035,6 +1037,7 @@ EOD;
protected function getItemArray($item, $field, $comment)
{
$itemArr = [];
$comment = str_replace('', ',', $comment);
if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false)
{
list($fieldLang, $item) = explode(':', $comment);
... ... @@ -1255,7 +1258,7 @@ EOD;
{
$html .= ", operate:'RANGE', addclass:'datetimerange'";
}
else if (in_array($datatype,['float', 'double', 'decimal']))
else if (in_array($datatype, ['float', 'double', 'decimal']))
{
$html .= ", operate:'BETWEEN'";
}
... ...
... ... @@ -80,6 +80,8 @@ class Install extends Command
$config = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $config);
// 写入数据库配置
file_put_contents($dbConfigFile, $config);
\think\Cache::rm('__menu__');
$output->info("Install Successed!");
}
... ...
... ... @@ -4,7 +4,7 @@
官网: http://www.fastadmin.net
演示: http://demo.fastadmin.net
Date: 2017年09月15
Date: 2018年03月07
*/
SET FOREIGN_KEY_CHECKS = 0;
... ... @@ -396,6 +396,9 @@ BEGIN;
INSERT INTO `fa_test` VALUES (1, 0, 12, '12,13', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '<p>我是测试内容</p>', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '描述', '广西壮族自治区/百色市/平果县', 0.00, 0, '2017-07-10', '2017-07-10 18:24:45', 2017, '18:24:45', 1499682285, 1499682526, 1499682526, 0, 1, 'normal', '1');
COMMIT;
-- ----------------------------
-- Table structure for fa_user
-- ----------------------------
DROP TABLE IF EXISTS `fa_user`;
CREATE TABLE `fa_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
... ... @@ -431,10 +434,16 @@ CREATE TABLE `fa_user` (
KEY `mobile` (`mobile`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='会员表';
-- ----------------------------
-- Records of fa_user
-- ----------------------------
BEGIN;
INSERT INTO `fa_user` VALUES (1, 1, 'admin', 'admin', 'c13f62012fd6a8fdf06b3452a94430e5', 'rpR6Bv', 'admin@163.com', '13888888888', '/assets/img/avatar.png', 0, 0, '2017-04-15', '', 0, 1, 1, 1516170492, 1516171614, '127.0.0.1', 0, '127.0.0.1', 1491461418, 0, 1516171614, '', 'normal','');
COMMIT;
-- ----------------------------
-- Table structure for fa_user_group
-- ----------------------------
DROP TABLE IF EXISTS `fa_user_group`;
CREATE TABLE `fa_user_group` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
... ... @@ -446,10 +455,16 @@ CREATE TABLE `fa_user_group` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='会员组表';
-- ----------------------------
-- Records of fa_user_group
-- ----------------------------
BEGIN;
INSERT INTO `fa_user_group` VALUES (1, '默认组', '1,2,3,4,5,6,7,8,9,10,11,12', 1515386468, 1516168298, 'normal');
COMMIT;
-- ----------------------------
-- Table structure for fa_user_rule
-- ----------------------------
DROP TABLE IF EXISTS `fa_user_rule`;
CREATE TABLE `fa_user_rule` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
... ... @@ -465,6 +480,9 @@ CREATE TABLE `fa_user_rule` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COMMENT='会员规则表';
-- ----------------------------
-- Records of fa_user_rule
-- ----------------------------
BEGIN;
INSERT INTO `fa_user_rule` VALUES (1, 0, 'index', '前台', '', 1, 1516168079, 1516168079, 1, 'normal');
INSERT INTO `fa_user_rule` VALUES (2, 0, 'api', 'API接口', '', 1, 1516168062, 1516168062, 2, 'normal');
... ... @@ -480,6 +498,9 @@ INSERT INTO `fa_user_rule` VALUES (11, 4, 'api/user/index', '会员中心', '',
INSERT INTO `fa_user_rule` VALUES (12, 4, 'api/user/profile', '个人资料', '', 0, 1516015012, 1516015012, 3, 'normal');
COMMIT;
-- ----------------------------
-- Table structure for fa_user_score_log
-- ----------------------------
DROP TABLE IF EXISTS `fa_user_score_log`;
CREATE TABLE `fa_user_score_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
... ... @@ -492,6 +513,9 @@ CREATE TABLE `fa_user_score_log` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='会员积分变动表';
-- ----------------------------
-- Table structure for fa_user_token
-- ----------------------------
DROP TABLE IF EXISTS `fa_user_token`;
CREATE TABLE `fa_user_token` (
`token` varchar(50) NOT NULL COMMENT 'Token',
... ... @@ -501,4 +525,30 @@ CREATE TABLE `fa_user_token` (
PRIMARY KEY (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员Token表';
-- ----------------------------
-- Table structure for fa_version
-- ----------------------------
DROP TABLE IF EXISTS `fa_version`;
CREATE TABLE `fa_version` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`oldversion` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '旧版本号',
`newversion` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '新版本号',
`packagesize` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '包大小',
`content` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '升级内容',
`downloadurl` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '下载地址',
`enforce` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '强制更新',
`createtime` int(10) NOT NULL DEFAULT 0 COMMENT '创建时间',
`updatetime` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间',
`weigh` int(10) NOT NULL DEFAULT 0 COMMENT '权重',
`status` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '版本表' ROW_FORMAT = Compact;
-- ----------------------------
-- Table structure for fa_version
-- ----------------------------
BEGIN;
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', '更新内容', 'http://www.fastadmin.net/download.html', 1, 1520425318, 0, 0, 'normal');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
... ...
... ... @@ -56,7 +56,6 @@ class Menu extends Command
{
throw new Exception("There is no menu to delete");
}
$readyMenu = [];
$output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: ");
$line = fgets(STDIN);
if (trim($line) != 'yes')
... ...
... ... @@ -27,6 +27,7 @@ class Min extends Command
->setName('min')
->addOption('module', 'm', Option::VALUE_REQUIRED, 'module name(frontend or backend),use \'all\' when build all modules', null)
->addOption('resource', 'r', Option::VALUE_REQUIRED, 'resource name(js or css),use \'all\' when build all resources', null)
->addOption('optimize', 'o', Option::VALUE_OPTIONAL, 'optimize type(uglify|closure|none)', 'none')
->setDescription('Compress js and css file');
}
... ... @@ -34,6 +35,7 @@ class Min extends Command
{
$module = $input->getOption('module') ?: '';
$resource = $input->getOption('resource') ?: '';
$optimize = $input->getOption('optimize') ?: 'none';
if (!$module || !in_array($module, ['frontend', 'backend', 'all']))
{
... ... @@ -89,6 +91,7 @@ class Min extends Command
'cssBaseUrl' => $this->options['cssBaseUrl'],
'jsBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['jsBaseUrl']),
'cssBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['cssBaseUrl']),
'optimize' => $optimize,
'ds' => DS,
];
... ... @@ -117,11 +120,19 @@ class Min extends Command
$output->info("Compress " . $data["{$res}BaseName"] . ".{$res}");
// 执行压缩
echo exec("{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\"");
$command = "{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\"";
if ($output->isDebug())
{
$output->warning($command);
}
echo exec($command);
}
}
@unlink($tempFile);
if (!$output->isDebug())
{
@unlink($tempFile);
}
$output->info("Build Successed!");
}
... ...
({
cssIn: "{%cssBasePath%}{%cssBaseName%}.css",
out: "{%cssBasePath%}{%cssBaseName%}.min.css",
optimizeCss: "default"
optimizeCss: "default",
optimize: "{%optimize%}"
})
\ No newline at end of file
... ...
... ... @@ -2,7 +2,8 @@
{%config%}
,
optimizeCss: "standard",
optimize: "none", //可使用uglify|closure|none
optimize: "{%optimize%}", //可使用uglify|closure|none
preserveLicenseComments: false,
removeCombined: false,
baseUrl: "{%jsBasePath%}", //JS文件所在的基础目录
name: "{%jsBaseName%}", //来源文件,不包含后缀
... ...
... ... @@ -5,6 +5,7 @@ namespace app\admin\controller;
use app\common\controller\Backend;
use think\addons\AddonException;
use think\addons\Service;
use think\Cache;
use think\Config;
use think\Exception;
... ... @@ -190,6 +191,7 @@ class Addon extends Backend
$action = $action == 'enable' ? $action : 'disable';
//调用启用、禁用的方法
Service::$action($name, $force);
Cache::rm('__menu__');
$this->success(__('Operate successful'));
}
catch (AddonException $e)
... ... @@ -314,6 +316,7 @@ class Addon extends Backend
];
//调用更新的方法
Service::upgrade($name, $extend);
Cache::rm('__menu__');
$this->success(__('Operate successful'));
}
catch (AddonException $e)
... ... @@ -370,7 +373,10 @@ class Addon extends Backend
$list[] = $v;
}
$total = count($list);
$list = array_slice($list, $offset, $limit);
if ($limit)
{
$list = array_slice($list, $offset, $limit);
}
$result = array("total" => $total, "rows" => $list);
$callback = $this->request->get('callback') ? "jsonp" : "json";
... ...
... ... @@ -29,13 +29,21 @@ class Index extends Backend
*/
public function index()
{
//
//左侧菜单
$menulist = $this->auth->getSidebar([
'dashboard' => 'hot',
'addon' => ['new', 'red', 'badge'],
'auth/rule' => 'side',
'auth/rule' => __('Menu'),
'general' => ['new', 'purple'],
], $this->view->site['fixedpage']);
$action = $this->request->request('action');
if ($this->request->isPost())
{
if ($action == 'refreshmenu')
{
$this->success('', null, ['menulist' => $menulist]);
}
}
$this->view->assign('menulist', $menulist);
$this->view->assign('title', __('Home'));
return $this->view->fetch();
... ...
... ... @@ -13,6 +13,6 @@ return [
'Menu tips' => '规则任意,不可重复,仅做层级显示,无需匹配控制器和方法',
'Node tips' => '控制器/方法名',
'The non-menu rule must have parent' => '非菜单规则节点必须有父级',
'If not necessary, use the command line to build rule' => '非必要情况下请直接使用命令行php think menu来生成',
'If not necessary, use the command line to build rule' => '非必要情况下请直接使用命令行<a href="http://doc.fastadmin.net/docs/command.html#一键生成菜单" target="_blank">php think menu</a>来生成',
'Name only supports letters, numbers, underscore and slash' => 'URL规则只能是小写字母、数字、下划线和/组成',
];
... ...
... ... @@ -12,6 +12,8 @@ return [
'Avatar' => '头像',
'Level' => '等级',
'Gender' => '性别',
'Male' => '男',
'FeMale' => '女',
'Birthday' => '生日',
'Bio' => '格言',
'Score' => '积分',
... ...
... ... @@ -273,7 +273,7 @@ class Auth extends \fast\Auth
$groupIds[] = $v['id'];
}
// 取出所有分组
$groupList = model('AuthGroup')->all(['status' => 'normal']);
$groupList = \app\admin\model\AuthGroup::where(['status' => 'normal'])->select();
$objList = [];
foreach ($groups as $K => $v)
{
... ... @@ -310,8 +310,8 @@ class Auth extends \fast\Auth
if (!$this->isSuperAdmin())
{
$groupIds = $this->getChildrenGroupIds(false);
$authGroupList = model('AuthGroupAccess')
->field('uid,group_id')
$authGroupList = \app\admin\model\AuthGroupAccess::
field('uid,group_id')
->where('group_id', 'in', $groupIds)
->select();
... ... @@ -407,7 +407,7 @@ class Auth extends \fast\Auth
$select_id = 0;
$pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader');
// 必须将结果集转换为数组
$ruleList = collection(model('AuthRule')->where('status', 'normal')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray();
$ruleList = collection(\app\admin\model\AuthRule::where('status', 'normal')->where('ismenu', 1)->order('weigh', 'desc')->cache("__menu__")->select())->toArray();
foreach ($ruleList as $k => &$v)
{
if (!in_array($v['name'], $userRule))
... ...
... ... @@ -267,7 +267,8 @@ trait Backend
{
$this->model->where($this->dataLimitField, 'in', $adminIds);
}
$count = $this->model->where($this->model->getPk(), 'in', $ids)->update($values);
$this->model->where($this->model->getPk(), 'in', $ids);
$count = $this->model->allowField(true)->isUpdate(true)->save($values);
if ($count)
{
$this->success();
... ...
... ... @@ -2,6 +2,7 @@
namespace app\admin\model;
use think\Cache;
use think\Model;
class AuthRule extends Model
... ... @@ -13,8 +14,16 @@ class AuthRule extends Model
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
protected static function init()
{
self::afterWrite(function ($row) {
Cache::rm('__menu__');
});
}
public function getTitleAttr($value, $data)
{
return __($value);
}
}
... ...
... ... @@ -10,6 +10,12 @@
.payimg .alipaycode {position:absolute;left:265px;top:442px;}
.payimg .wechatcode {position:absolute;left:660px;top:442px;}
.thumbnail img{width:100%;}
.fixed-table-toolbar .pull-right.search {
min-width: 300px;
}
.status-disabled .noimage {
background:#d2d6de;
}
</style>
<div id="warmtips" class="alert alert-dismissable alert-danger hide">
<button type="button" class="close" data-dismiss="alert">×</button>
... ... @@ -158,10 +164,10 @@
</table>
</script>
<script id="itemtpl" type="text/html">
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3 mt-4">
<% var labelarr = ['primary', 'success', 'info', 'danger', 'warning']; %>
<% var label = labelarr[item.id % 5]; %>
<% var addon = typeof addons[item.name]!= 'undefined' ? addons[item.name] : null; %>
<% var labelarr = ['primary', 'success', 'info', 'danger', 'warning']; %>
<% var label = labelarr[item.id % 5]; %>
<% var addon = typeof addons[item.name]!= 'undefined' ? addons[item.name] : null; %>
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3 mt-4 status-<%=addon ? (addon.state==1?'enabled':'disabled') : 'uninstalled'%>">
<div class="thumbnail addon">
<%if(addon){%>
<span>
... ...
... ... @@ -31,7 +31,7 @@
<label for="icon" class="control-label col-xs-12 col-sm-2">{:__('Icon')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group input-groupp-md">
<input type="text" class="form-control" id="icon" name="row[icon]" value="fa fa-dot" />
<input type="text" class="form-control" id="icon" name="row[icon]" value="fa fa-circle-o" />
<a href="javascript:;" class="btn-search-icon input-group-addon">{:__('Search icon')}</a>
</div>
</div>
... ...
<style>
.bootstrap-table tr td .text-muted {color:#888;}
</style>
<div class="panel panel-default panel-intro">
{:build_heading()}
... ... @@ -6,7 +9,17 @@
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar()}
<a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('auth/rule/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('auth/rule/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('auth/rule/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
<div class="dropdown btn-group {:$auth->check('auth/rule/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
<a href="javascript:;" class="btn btn-danger btn-toggle-all"><i class="fa fa-plus"></i> {:__('Toggle all')}</a>
</div>
<table id="table" class="table table-bordered table-hover"
... ...
<div class="panel panel-default panel-intro">
<div class="panel-heading">
{:build_heading()}
{:build_heading(null,FALSE)}
<ul class="nav nav-tabs">
<li class="active"><a href="#all" data-toggle="tab">{:__('All')}</a></li>
{foreach name="typeList" item="vo"}
... ...
... ... @@ -43,147 +43,15 @@
<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">
<h3 class="control-sidebar-heading">{:__('Recent Activity')}</h3>
<ul class="control-sidebar-menu">
<li>
<a href="javascript:void(0)">
<i class="menu-icon fa fa-birthday-cake bg-red"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Langdon's Birthday</h4>
<p>Will be 23 on April 24th</p>
</div>
</a>
</li>
<li>
<a href="javascript:void(0)">
<i class="menu-icon fa fa-user bg-yellow"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Frodo Updated His Profile</h4>
<p>New phone +1(800)555-1234</p>
</div>
</a>
</li>
<li>
<a href="javascript:void(0)">
<i class="menu-icon fa fa-envelope-o bg-light-blue"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Nora Joined Mailing List</h4>
<p>nora@example.com</p>
</div>
</a>
</li>
<li>
<a href="javascript:void(0)">
<i class="menu-icon fa fa-file-code-o bg-green"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Cron Job 254 Executed</h4>
<p>Execution time 5 seconds</p>
</div>
</a>
</li>
</ul>
<!-- /.control-sidebar-menu -->
<h3 class="control-sidebar-heading">{:__('Tasks Progress')}</h3>
<ul class="control-sidebar-menu">
<li>
<a href="javascript:void(0)">
<h4 class="control-sidebar-subheading">
Custom Template Design
<span class="label label-danger pull-right">70%</span>
</h4>
<div class="progress progress-xxs">
<div class="progress-bar progress-bar-danger" style="width: 70%"></div>
</div>
</a>
</li>
<li>
<a href="javascript:void(0)">
<h4 class="control-sidebar-subheading">
Update Resume
<span class="label label-success pull-right">95%</span>
</h4>
<div class="progress progress-xxs">
<div class="progress-bar progress-bar-success" style="width: 95%"></div>
</div>
</a>
</li>
<li>
<a href="javascript:void(0)">
<h4 class="control-sidebar-subheading">
Laravel Integration
<span class="label label-warning pull-right">50%</span>
</h4>
<div class="progress progress-xxs">
<div class="progress-bar progress-bar-warning" style="width: 50%"></div>
</div>
</a>
</li>
<li>
<a href="javascript:void(0)">
<h4 class="control-sidebar-subheading">
Back End Framework
<span class="label label-primary pull-right">68%</span>
</h4>
<div class="progress progress-xxs">
<div class="progress-bar progress-bar-primary" style="width: 68%"></div>
</div>
</a>
</li>
</ul>
<!-- /.control-sidebar-menu -->
<h4 class="control-sidebar-heading">{:__('Home')}</h4>
</div>
<!-- /.tab-pane -->
<!-- Stats tab content -->
<div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div>
<!-- /.tab-pane -->
<!-- Settings tab content -->
<div class="tab-pane" id="control-sidebar-settings-tab">
<form method="post">
<h3 class="control-sidebar-heading">General Settings</h3>
<!-- /.form-group -->
<div class="form-group">
<label class="control-sidebar-subheading">
Allow mail redirect
<input type="checkbox" class="pull-right" checked>
</label>
<p>
Other sets of options are available
</p>
</div>
<!-- /.form-group -->
<div class="form-group">
<label class="control-sidebar-subheading">
Expose author name in posts
<input type="checkbox" class="pull-right" checked>
</label>
<p>
Allow the user to show his name in blog posts
</p>
</div>
<!-- /.form-group -->
<!-- /.form-group -->
</form>
<h4 class="control-sidebar-heading">{:__('Setting')}</h4>
</div>
<!-- /.tab-pane -->
</div>
... ...
<!-- Logo -->
<a href="javascript:;" class="logo">
<a href="javascript:;" class="logo hidden-xs">
<!-- 迷你模式下Logo的大小为50X50 -->
<span class="logo-mini">{$site.name|mb_substr=0,4,'utf-8'|mb_strtoupper='utf-8'}</span>
<!-- 普通模式下Logo -->
... ...
... ... @@ -29,10 +29,10 @@
<!--如果想始终显示子菜单,则给ul加上show-submenu类即可-->
<ul class="sidebar-menu">
{$menulist}
<li class="header">{:__('Links')}</li>
<li><a href="http://doc.fastadmin.net" target="_blank"><i class="fa fa-list text-red"></i> <span>{:__('Docs')}</span></a></li>
<li><a href="http://forum.fastadmin.net" target="_blank"><i class="fa fa-comment text-yellow"></i> <span>{:__('Forum')}</span></a></li>
<li><a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank"><i class="fa fa-qq text-aqua"></i> <span>{:__('QQ qun')}</span></a></li>
<li class="header" data-rel="external">{:__('Links')}</li>
<li data-rel="external"><a href="http://doc.fastadmin.net" target="_blank"><i class="fa fa-list text-red"></i> <span>{:__('Docs')}</span></a></li>
<li data-rel="external"><a href="http://forum.fastadmin.net" target="_blank"><i class="fa fa-comment text-yellow"></i> <span>{:__('Forum')}</span></a></li>
<li data-rel="external"><a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank"><i class="fa fa-qq text-aqua"></i> <span>{:__('QQ qun')}</span></a></li>
</ul>
</section>
<!-- /.sidebar -->
\ No newline at end of file
... ...
<script src="__CDN__/assets/js/require.js" data-main="__CDN__/assets/js/require-backend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
\ No newline at end of file
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-backend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
\ No newline at end of file
... ...
... ... @@ -6,7 +6,7 @@
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar()}
{:build_toolbar('refresh,add,edit,del')}
<div class="dropdown btn-group {:$auth->check('user/rule/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
... ...
<?php
//配置文件
return [
'exception_handle' => '\\app\\api\\library\\ExceptionHandle',
];
... ...
... ... @@ -2,9 +2,9 @@
namespace app\api\controller;
use app\api\model\Area;
use app\common\controller\Api;
use fast\Version;
use app\common\model\Area;
use app\common\model\Version;
use fast\Random;
use think\Config;
... ...
<?php
namespace app\api\library;
use Exception;
use think\exception\Handle;
/**
* 自定义API模块的错误显示
*/
class ExceptionHandle extends Handle
{
public function render(Exception $e)
{
// 在生产环境下返回code信息
if (!\think\Config::get('app_debug'))
{
$statuscode = $code = 500;
$msg = 'An error occurred';
// 验证异常
if ($e instanceof \think\exception\ValidateException)
{
$code = 0;
$statuscode = 200;
$msg = $e->getError();
}
// Http异常
if ($e instanceof \think\exception\HttpException)
{
$statuscode = $code = $e->getStatusCode();
}
return json(['code' => $code, 'msg' => $msg, 'time' => time(), 'data' => null], $statuscode);
}
//其它此交由系统处理
return parent::render($e);
}
}
... ...
... ... @@ -16,4 +16,5 @@ return [
'app\admin\command\Install',
'app\admin\command\Min',
'app\admin\command\Addon',
'app\admin\command\Api',
];
... ...
... ... @@ -22,7 +22,7 @@ if (!function_exists('__'))
array_shift($vars);
$lang = '';
}
return think\Lang::get($name, $vars, $lang);
return \think\Lang::get($name, $vars, $lang);
}
}
... ... @@ -89,7 +89,7 @@ if (!function_exists('cdnurl'))
*/
function cdnurl($url)
{
return preg_match("/^https?:\/\/(.*)/i", $url) ? $url : think\Config::get('upload.cdnurl') . $url;
return preg_match("/^https?:\/\/(.*)/i", $url) ? $url : \think\Config::get('upload.cdnurl') . $url;
}
}
... ... @@ -208,7 +208,6 @@ if (!function_exists('mb_ucfirst'))
}
if (!function_exists('addtion'))
{
... ... @@ -300,3 +299,37 @@ if (!function_exists('addtion'))
}
}
if (!function_exists('var_export_short'))
{
/**
* 返回打印数组结构
* @param string $var 数组
* @param string $indent 缩进字符
* @return string
*/
function var_export_short($var, $indent = "")
{
switch (gettype($var))
{
case "string":
return '"' . addcslashes($var, "\\\$\"\r\n\t\v\f") . '"';
case "array":
$indexed = array_keys($var) === range(0, count($var) - 1);
$r = [];
foreach ($var as $key => $value)
{
$r[] = "$indent "
. ($indexed ? "" : var_export_short($key) . " => ")
. var_export_short($value, "$indent ");
}
return "[\n" . implode(",\n", $r) . "\n" . $indent . "]";
case "boolean":
return $var ? "TRUE" : "FALSE";
default:
return var_export($var, TRUE);
}
}
}
\ No newline at end of file
... ...
... ... @@ -52,6 +52,11 @@ class Common
{
Config::set('app_trace', false);
}
// 切换多语言
if (Config::get('lang_switch_on') && $request->get('lang'))
{
\think\Cookie::set('think_var', $request->get('lang'));
}
}
public function addonBegin(&$request)
... ...
... ... @@ -91,7 +91,7 @@ class Api
$actionname = strtolower($this->request->action());
// token
$token = $this->request->request('token') ?: $this->request->cookie('token');
$token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token')));
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
// 设置当前请求的URI
... ... @@ -104,7 +104,7 @@ class Api
//检测是否登录
if (!$this->auth->isLogin())
{
$this->error(__('Please login first'));
$this->error(__('Please login first'), null, 401);
}
// 判断是否需要验证权限
if (!$this->auth->match($this->noNeedRight))
... ... @@ -112,7 +112,7 @@ class Api
// 判断控制器和方法判断是否有对应权限
if (!$this->auth->check($path))
{
$this->error(__('You have no permission'));
$this->error(__('You have no permission'), null, 403);
}
}
}
... ... @@ -141,38 +141,40 @@ class Api
* 操作成功返回的数据
* @param string $msg 提示信息
* @param mixed $data 要返回的数据
* @param int $code 错误码,默认为1
* @param string $type 输出类型
* @param array $header 发送的 Header 信息
*/
protected function success($msg = '', $data = '', $type = 'json', array $header = [])
protected function success($msg = '', $data = null, $code = 1, $type = 'json', array $header = [])
{
$this->result($data, 1, $msg, $type, $header);
$this->result($msg, $data, $code, $type, $header);
}
/**
* 操作失败返回的数据
* @param string $msg 提示信息
* @param mixed $data 要返回的数据
* @param int $code 错误码,默认为0
* @param string $type 输出类型
* @param array $header 发送的 Header 信息
*/
protected function error($msg = '', $data = '', $type = 'json', array $header = [])
protected function error($msg = '', $data = null, $code = 0, $type = 'json', array $header = [])
{
$this->result($data, 0, $msg, $type, $header);
$this->result($msg, $data, $code, $type, $header);
}
/**
* 返回封装后的 API 数据到客户端
* @access protected
* @param mixed $msg 提示信息
* @param mixed $data 要返回的数据
* @param int $code 返回的 code
* @param mixed $msg 提示信息
* @param string $type 返回数据格式
* @param array $header 发送的 Header 信息
* @return void
* @throws HttpResponseException
*/
protected function result($data, $code = 0, $msg = '', $type = '', array $header = [])
protected function result($msg, $data = null, $code = 0, $type = 'json', array $header = [])
{
$result = [
'code' => $code,
... ... @@ -181,20 +183,21 @@ class Api
'data' => $data,
];
$type = $type ?: $this->getResponseType();
$response = Response::create($result, $type)->header($header);
if (isset($header['statuscode']))
{
$code = $header['statuscode'];
unset($header['statuscode']);
}
else
{
$code = $code >= 1000 ? 200 : $code;
}
$response = Response::create($result, $type, $code)->header($header);
throw new HttpResponseException($response);
}
/**
* 未找到请求的接口
*/
public function _empty()
{
return $this->error('Api not found');
}
/**
* 前置操作
* @access protected
* @param string $method 前置操作方法名
... ...
... ... @@ -144,6 +144,7 @@ class Backend extends Controller
$url = preg_replace_callback("/([\?|&]+)ref=addtabs(&?)/i", function($matches) {
return $matches[2] == '&' ? $matches[1] : '';
}, $this->request->url());
$url = url($url, '', false);
$this->redirect('index/index', [], 302, ['referer' => $url]);
exit;
}
... ... @@ -290,6 +291,10 @@ class Backend extends Controller
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':
... ... @@ -401,21 +406,21 @@ class Backend extends Controller
//搜索关键词,客户端输入以空格分开,这里接收为数组
$word = (array) $this->request->request("q_word/a");
//当前页
$page = $this->request->request("page");
$page = $this->request->request("pageNumber");
//分页大小
$pagesize = $this->request->request("per_page");
$pagesize = $this->request->request("pageSize");
//搜索条件
$andor = $this->request->request("and_or");
$andor = $this->request->request("andOr");
//排序方式
$orderby = (array) $this->request->request("order_by/a");
$orderby = (array) $this->request->request("orderBy/a");
//显示的字段
$field = $this->request->request("field");
$field = $this->request->request("showField");
//主键
$primarykey = $this->request->request("pkey_name");
$primarykey = $this->request->request("keyField");
//主键值
$primaryvalue = $this->request->request("pkey_value");
$primaryvalue = $this->request->request("keyValue");
//搜索字段
$searchfield = (array) $this->request->request("search_field/a");
$searchfield = (array) $this->request->request("searchField/a");
//自定义搜索条件
$custom = (array) $this->request->request("custom/a");
$order = [];
... ...
... ... @@ -58,8 +58,7 @@ class Frontend extends Controller
$actionname = strtolower($this->request->action());
// token
$token = $this->request->request('token');
$token = $token ? $token : \think\Cookie::get('token');
$token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token')));
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
// 设置当前请求的URI
... ...
<?php
return [
'addon %s not found' => '插件未找到',
'addon %s is disabled' => '插件已禁用',
'addon controller %s not found' => '插件控制器未找到',
'addon action %s not found' => '插件控制器方法未找到',
'addon can not be empty' => '插件不能为空',
'addon %s not found' => '插件未找到',
'addon %s is disabled' => '插件已禁用',
'addon controller %s not found' => '插件控制器未找到',
'addon action %s not found' => '插件控制器方法未找到',
'addon can not be empty' => '插件不能为空',
'Keep login' => '保持会话',
'Forgot password' => '忘记密码?',
'Sign in' => '登入',
'Username' => '用户名',
'User id' => '会员ID',
'Username' => '用户名',
'Nickname' => '昵称',
'Password' => '密码',
'Sign up' => '注 册',
'Sign in' => '登 录',
'Sign out' => '注 销',
'Guest' => '游客',
'Welcome' => '%s,你好!',
'Add' => '添加',
'Edit' => '编辑',
'Delete' => '删除',
'Move' => '移动',
'Name' => '名称',
'Status' => '状态',
'Weigh' => '权重',
'Operate' => '操作',
'Warning' => '温馨提示',
'Default' => '默认',
'Article' => '文章',
'Page' => '单页',
'OK' => '确定',
'Cancel' => '取消',
'Loading' => '加载中',
'More' => '更多',
'Normal' => '正常',
'Hidden' => '隐藏',
'Submit' => '提交',
'Reset' => '重置',
'Execute' => '执行',
'Close' => '关闭',
'Search' => '搜索',
'Refresh' => '刷新',
'First' => '首页',
'Previous' => '上一页',
'Next' => '下一页',
'Last' => '末页',
'None' => '无',
'Home' => '主页',
'Online' => '在线',
'Logout' => '注销',
'Profile' => '个人资料',
'Index' => '首页',
'Hot' => '热门',
'Recommend' => '推荐',
'Dashboard' => '控制台',
'Code' => '编号',
'Message' => '内容',
'Line' => '行号',
'File' => '文件',
'Menu' => '菜单',
'Name' => '名称',
'Weigh' => '权重',
'Type' => '类型',
'Title' => '标题',
'Content' => '内容',
'Status' => '状态',
'Operate' => '操作',
'Append' => '追加',
'Memo' => '备注',
'Parent' => '父级',
'Params' => '参数',
'Permission' => '权限',
'Begin time' => '开始时间',
'End time' => '结束时间',
'Create time' => '创建时间',
'Flag' => '标志',
'Home' => '首页',
'Store' => '插件市场',
'Services' => '服务',
'Download' => '下载',
'Demo' => '演示',
'Donation' => '捐赠',
'Forum' => '社区',
'Docs' => '文档',
'Please login first' => '请登录后再操作',
'Send verification code' => '发送验证码',
'Redirect now' => '立即跳转',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
'Unknown data format' => '未知的数据格式!',
'Network error' => '网络错误!',
'Advanced search' => '高级搜索',
'Invalid parameters' => '未知参数',
'No results were found' => '记录未找到',
'Parameter %s can not be empty' => '参数%s不能为空',
'You have no permission' => '你没有权限访问',
'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中',
'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转',
];
... ...
... ... @@ -67,7 +67,7 @@ class Menu
AuthRule::destroy($ids);
return true;
}
/**
* 启用菜单
* @param string $name
... ... @@ -83,7 +83,7 @@ class Menu
AuthRule::where('id', 'in', $ids)->update(['status' => 'normal']);
return true;
}
/**
* 禁用菜单
* @param string $name
... ... @@ -101,6 +101,28 @@ class Menu
}
/**
* 导出指定名称的菜单规则
* @param string $name
* @return array
*/
public static function export($name)
{
$ids = self::getAuthRuleIdsByName($name);
if (!$ids)
{
return [];
}
$menuList = [];
$menu = AuthRule::getByName($name);
if ($menu)
{
$ruleList = collection(AuthRule::where('id', 'in', $ids)->select())->toArray();
$menuList = Tree::instance()->init($ruleList)->getTreeArray($menu['id']);
}
return $menuList;
}
/**
* 根据名称获取规则IDS
* @param string $name
* @return array
... ... @@ -112,7 +134,7 @@ class Menu
if ($menu)
{
// 必须将结果集转换为数组
$ruleList = collection(model('AuthRule')->order('weigh', 'desc')->field('id,pid,name')->select())->toArray();
$ruleList = collection(AuthRule::order('weigh', 'desc')->field('id,pid,name')->select())->toArray();
// 构造菜单数据
$ids = Tree::instance()->init($ruleList)->getChildrenIds($menu['id'], true);
}
... ...
<?php
namespace app\api\model;
namespace app\common\model;
use think\Cache;
use think\Model;
... ...
... ... @@ -11,7 +11,7 @@ class ScoreLog Extends Model
{
// 表名
protected $name = 'score_log';
protected $name = 'user_score_log';
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
... ...
<?php
namespace app\common\model;
use think\Model;
class Version extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 定义字段类型
protected $type = [
];
/**
* 检测版本号
*
* @param string $version 客户端版本号
* @return array
*/
public static function check($version)
{
$versionlist = self::where('status', 'normal')->cache('__version__')->order('weigh desc,id desc')->select();
foreach ($versionlist as $k => $v)
{
// 版本正常且新版本号不等于验证的版本号且找到匹配的旧版本
if ($v['status'] == 'normal' && $v['newversion'] !== $version && \fast\Version::check($version, $v['oldversion']))
{
$updateversion = $v;
break;
}
}
if (isset($updateversion))
{
$search = ['{version}', '{newversion}', '{downloadurl}', '{url}', '{packagesize}'];
$replace = [$version, $updateversion['newversion'], $updateversion['downloadurl'], $updateversion['downloadurl'], $updateversion['packagesize']];
$upgradetext = str_replace($search, $replace, $updateversion['content']);
return [
"enforce" => $updateversion['enforce'],
"version" => $version,
"newversion" => $updateversion['newversion'],
"downloadurl" => $updateversion['downloadurl'],
"packagesize" => $updateversion['packagesize'],
"upgradetext" => $upgradetext
];
}
return NULL;
}
}
... ...
... ... @@ -253,7 +253,7 @@ return [
//自动检测更新
'checkupdate' => false,
//版本号
'version' => '1.0.0.20180227_beta',
'version' => '1.0.0.20180308_beta',
'api_url' => 'http://api.fastadmin.net',
],
];
... ...
... ... @@ -97,7 +97,7 @@ return [
'Forum' => '社区',
'Docs' => '文档',
'Please login first' => '请登录后再操作',
'Send verification code' => '发验证码',
'Send verification code' => '发验证码',
'Redirect now' => '立即跳转',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
... ...
<script src="__CDN__/assets/js/require.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
\ No newline at end of file
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
\ No newline at end of file
... ...
... ... @@ -174,11 +174,9 @@
$("#mainNav").toggleClass("affix", $(window).height() - $(window).scrollTop() <= 50);
});
//发送版本统计信息
try {
var installed = localStorage.getItem("installed");
console.log(installed);
if (!installed) {
$.ajax({
url: "{$Think.config.fastadmin.api_url}/statistics/installed",
... ...
... ... @@ -8,37 +8,31 @@
"dependencies": {
"jquery": "^2.1.4",
"bootstrap": "^3.3.7",
"font-awesome": "fontawesome#^4.6.1",
"font-awesome": "^4.6.1",
"bootstrap-table": "^1.11.0",
"layer": "*",
"layer": "^3.0",
"jstree": "^3.3.2",
"summernote": "^0.8.2",
"jquery-pjax": "^1.9.6",
"moment": "^2.15.2",
"plupload": "^2.2.0",
"toastr": "^2.1.3",
"devbridge-autocomplete": "^1.2.26",
"jcrop": "jcrop#^2.0.4",
"jquery-qrcode": "*",
"jcrop": "^2.0.4",
"eonasdan-bootstrap-datetimepicker": "^4.17.43",
"bootstrap-select": "^1.11.2",
"require-css": "^0.1.8",
"less": "^2.7.1",
"tableExport.jquery.plugin": "^1.9.0",
"jquery-slimscroll": "slimscroll#^1.3.8",
"jquery-slimscroll": "^1.3.8",
"jquery.cookie": "^1.4.1",
"Sortable": "^1.5.0",
"nice-validator": "^1.1.1",
"art-template": "^3.0.1",
"requirejs-plugins": "^1.0.3",
"bootstrap-daterangepicker": "^2.1.25",
"city-picker":"^1.1.0"
},
"devDependencies": {
"dragsort": "https://github.com/karsonzhang/dragsort.git",
"jquery-addtabs": "https://github.com/karsonzhang/jquery-addtabs.git",
"jquery-cxselect": "https://github.com/karsonzhang/cxSelect.git",
"selectpage": "https://github.com/karsonzhang/selectpage.git"
"city-picker": "^1.1.0",
"fastadmin-cxselect": "~1.4.0",
"fastadmin-dragsort": "~1.0.0",
"fastadmin-addtabs": "~1.0.0",
"fastadmin-selectpage": "~1.0.0"
},
"resolutions": {
"jspdf": "1.1.239 || 1.3.2"
... ...
... ... @@ -10,7 +10,7 @@
"license": "Apache-2.0",
"authors": [
{
"name": "karson",
"name": "Karson",
"email": "karsonzhang@163.com"
}
],
... ... @@ -22,7 +22,7 @@
"topthink/think-captcha": "^1.0",
"mtdowling/cron-expression": "^1.2",
"phpmailer/phpmailer": "^5.2",
"karsonzhang/fastadmin-addons": "dev-master",
"karsonzhang/fastadmin-addons": "~1.1.0",
"overtrue/pinyin": "~3.0",
"phpoffice/phpexcel": "^1.8"
},
... ...
此 diff 太大无法显示。
... ... @@ -9,7 +9,7 @@
@import url("../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css");
@import url("../libs/bootstrap-daterangepicker/daterangepicker.css");
@import url("../libs/nice-validator/dist/jquery.validator.css");
@import url("../libs/selectpage/selectpage.css");
@import url("../libs/fastadmin-selectpage/selectpage.css");
body {
background: #f1f4f6;
}
... ... @@ -44,9 +44,6 @@ body.is-dialog {
position: absolute;
right: 0;
}
.note-dialog .modal {
z-index: 1060;
}
.bootstrap-dialog .modal-dialog {
/*width: 70%;*/
max-width: 885px;
... ... @@ -645,13 +642,16 @@ form.form-horizontal .control-label {
overflow: hidden;
}
.layui-layer-fast .layui-layer-btn a {
background-color: #95a5a6!important;
border-color: #95a5a6!important;
background-color: #95a5a6;
border-color: #95a5a6;
color: #fff!important;
height: 31px;
margin-top: 0;
border: 1px solid transparent;
}
.layui-layer-fast .layui-layer-btn .layui-layer-btn0 {
background-color: #18bc9c!important;
border-color: #18bc9c!important;
background-color: #18bc9c;
border-color: #18bc9c;
}
.layui-layer-fast .layui-layer-footer {
padding: 8px 20px;
... ... @@ -731,6 +731,14 @@ form.form-horizontal .control-label {
.n-bootstrap .input-group > .n-right {
position: absolute;
}
@media (min-width: 564px) {
body.is-dialog .daterangepicker {
min-width: 130px;
}
body.is-dialog .daterangepicker .ranges ul {
width: 130px;
}
}
/*手机版样式*/
@media (max-width: 480px) {
.nav-addtabs {
... ... @@ -739,6 +747,10 @@ form.form-horizontal .control-label {
.fixed-table-toolbar .columns-right.btn-group {
display: none;
}
.fixed .content-wrapper,
.fixed .right-side {
padding-top: 50px;
}
}
/*平板样式*/
@media (max-width: 768px) {
... ...
... ... @@ -41,14 +41,6 @@ body {
-moz-box-shadow: none;
box-shadow: none;
}
.layui-layer-fast {
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation-duration: .3s;
animation-duration: .3s;
-webkit-animation-name: layer-bounceIn;
animation-name: layer-bounceIn;
}
/*修复nice-validator和summernote的编辑框冲突*/
.nice-validator .note-editor .note-editing-area .note-editable {
display: inherit;
... ... @@ -317,6 +309,8 @@ footer.footer {
color: #aaa;
background: #555;
margin-top: 25px;
position: fixed;
bottom: 0;
}
footer.footer ul {
margin: 60px 0 30px 0;
... ...
define(['backend'], function (Backend) {
});
\ No newline at end of file
... ...
... ... @@ -99,6 +99,9 @@ define(['fast', 'moment'], function (Fast, Moment) {
url = url.replace(/\{ids\}/g, ids);
}
return url;
},
refreshmenu: function () {
top.window.$(".sidebar-menu").trigger("refresh");
}
},
init: function () {
... ...
... ... @@ -56,6 +56,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
commonSearch: false,
searchFormVisible: false,
pageSize: 12,
pagination: false,
queryParams: function (params) {
var filter = params.filter ? JSON.parse(params.filter) : {};
var op = params.op ? JSON.parse(params.op) : {};
... ... @@ -110,16 +111,17 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
$("#warmtips").removeClass("hide");
$(".btn-switch,.btn-userinfo").addClass("disabled");
}
// 离线安装
require(['upload'], function (Upload) {
Upload.api.plupload("#plupload-addon", function (data, ret) {
Config['addons'][data.addon.name] = data.addon;
$('.btn-refresh').trigger('click');
Toastr.success(ret.msg);
operate(data.addon.name, 'enable', false);
});
});
//查看插件首页
// 查看插件首页
$(document).on("click", ".btn-addonindex", function () {
if ($(this).attr("href") == 'javascript:;') {
Layer.msg(__('Not installed tips'), {icon: 7});
... ... @@ -128,12 +130,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
return false;
}
});
//切换URL
// 切换URL
$(document).on("click", ".btn-switch", function () {
$(".btn-switch").removeClass("active");
$(this).addClass("active");
table.bootstrapTable('refresh', {url: $(this).data("url"), pageNumber: 1});
});
// 会员信息
$(document).on("click", ".btn-userinfo", function () {
var userinfo = Controller.api.userinfo.get();
... ... @@ -195,46 +199,24 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
}
});
// 点击安装
$(document).on("click", ".btn-install", function () {
var that = this;
var name = $(this).closest(".operate").data("name");
var version = $(this).data("version");
var install = function (name, version, force) {
var userinfo = Controller.api.userinfo.get();
var uid = userinfo ? userinfo.id : 0;
var token = userinfo ? userinfo.token : '';
var install = function (name, force) {
Fast.api.ajax({
url: 'addon/install',
data: {name: name, force: force ? 1 : 0, uid: uid, token: token, version: version, faversion: Config.fastadmin.version}
}, function (data, ret) {
Layer.closeAll();
Config['addons'][data.addon.name] = ret.data.addon;
Layer.alert(__('Online installed tips'), {
btn: [__('OK'), __('Donate')],
title: __('Warning'),
icon: 1,
btn2: function () {
//打赏
Layer.open({
content: Template("paytpl", {payimg: $(that).data("donateimage")}),
shade: 0.8,
area: ['800px', '600px'],
skin: 'layui-layer-msg layui-layer-pay',
title: false,
closeBtn: true,
btn: false,
resize: false,
});
}
});
$('.btn-refresh').trigger('click');
}, function (data, ret) {
//如果是需要购买的插件则弹出二维码提示
if (ret && ret.code === -1) {
//扫码支付
Fast.api.ajax({
url: 'addon/install',
data: {name: name, force: force ? 1 : 0, uid: uid, token: token, version: version, faversion: Config.fastadmin.version}
}, function (data, ret) {
Layer.closeAll();
Config['addons'][data.addon.name] = ret.data.addon;
Layer.alert(__('Online installed tips'), {
btn: [__('OK'), __('Donate')],
title: __('Warning'),
icon: 1,
btn2: function () {
//打赏
Layer.open({
content: Template("paytpl", ret.data),
content: Template("paytpl", {payimg: $(that).data("donateimage")}),
shade: 0.8,
area: ['800px', '600px'],
skin: 'layui-layer-msg layui-layer-pay',
... ... @@ -242,44 +224,157 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
closeBtn: true,
btn: false,
resize: false,
end: function () {
Layer.alert(__('Pay tips'));
}
});
} else if (ret && ret.code === -2) {
//跳转支付
Layer.alert(__('Pay click tips'), {
btn: [__('Pay now'), __('Cancel')],
icon: 0,
success: function (layero) {
$(".layui-layer-btn0", layero).attr("href", ret.data.payurl).attr("target", "_blank");
}
}, function () {
Layer.alert(__('Pay new window tips'), {icon: 0});
});
}
});
$('.btn-refresh').trigger('click');
Fast.api.refreshmenu();
}, function (data, ret) {
//如果是需要购买的插件则弹出二维码提示
if (ret && ret.code === -1) {
//扫码支付
Layer.open({
content: Template("paytpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
skin: 'layui-layer-msg layui-layer-pay',
title: false,
closeBtn: true,
btn: false,
resize: false,
end: function () {
Layer.alert(__('Pay tips'));
}
});
} else if (ret && ret.code === -2) {
//跳转支付
Layer.alert(__('Pay click tips'), {
btn: [__('Pay now'), __('Cancel')],
icon: 0,
success: function (layero) {
$(".layui-layer-btn0", layero).attr("href", ret.data.payurl).attr("target", "_blank");
}
}, function () {
Layer.alert(__('Pay new window tips'), {icon: 0});
});
} else if (ret && ret.code === -3) {
//插件目录发现影响全局的文件
Layer.open({
content: Template("conflicttpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
title: __('Warning'),
btn: [__('Continue install'), __('Cancel')],
end: function () {
} else if (ret && ret.code === -3) {
//插件目录发现影响全局的文件
Layer.open({
content: Template("conflicttpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
title: __('Warning'),
btn: [__('Continue install'), __('Cancel')],
end: function () {
},
yes: function () {
install(name, true);
}
});
},
yes: function () {
install(name, true);
}
});
} else {
Layer.alert(ret.msg);
}
return false;
});
};
var uninstall = function (name, force) {
Fast.api.ajax({
url: 'addon/uninstall',
data: {name: name, force: force ? 1 : 0}
}, function (data, ret) {
delete Config['addons'][name];
Layer.closeAll();
$('.btn-refresh').trigger('click');
Fast.api.refreshmenu();
}, function (data, ret) {
if (ret && ret.code === -3) {
//插件目录发现影响全局的文件
Layer.open({
content: Template("conflicttpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
title: __('Warning'),
btn: [__('Continue uninstall'), __('Cancel')],
end: function () {
},
yes: function () {
uninstall(name, true);
}
});
} else {
Layer.alert(ret.msg);
}
return false;
});
};
var operate = function (name, action, force) {
Fast.api.ajax({
url: 'addon/state',
data: {name: name, action: action, force: force ? 1 : 0}
}, function (data, ret) {
var addon = Config['addons'][name];
addon.state = action === 'enable' ? 1 : 0;
Layer.closeAll();
$('.btn-refresh').trigger('click');
Fast.api.refreshmenu();
}, function (data, ret) {
if (ret && ret.code === -3) {
//插件目录发现影响全局的文件
Layer.open({
content: Template("conflicttpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
title: __('Warning'),
btn: [__('Continue operate'), __('Cancel')],
end: function () {
},
yes: function () {
operate(name, action, true);
}
});
} else {
Layer.alert(ret.msg);
}
return false;
});
};
var upgrade = function (name, version) {
var userinfo = Controller.api.userinfo.get();
var uid = userinfo ? userinfo.id : 0;
var token = userinfo ? userinfo.token : '';
Fast.api.ajax({
url: 'addon/upgrade',
data: {name: name, uid: uid, token: token, version: version, faversion: Config.fastadmin.version}
}, function (data, ret) {
Config['addons'][name].version = version;
Layer.closeAll();
$('.btn-refresh').trigger('click');
Fast.api.refreshmenu();
}, function (data, ret) {
Layer.alert(ret.msg);
return false;
});
};
// 点击安装
$(document).on("click", ".btn-install", function () {
var that = this;
var name = $(this).closest(".operate").data("name");
var version = $(this).data("version");
var userinfo = Controller.api.userinfo.get();
var uid = userinfo ? userinfo.id : 0;
} else {
Layer.alert(ret.msg);
}
return false;
});
};
if ($(that).data("type") !== 'free') {
if (parseInt(uid) === 0) {
return Layer.alert(__('Not login tips'), {
... ... @@ -289,99 +384,36 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
$(".btn-userinfo").trigger("click");
},
btn2: function () {
install(name, false);
install(name, version, false);
}
});
}
}
install(name, false);
install(name, version, false);
});
//点击卸载
// 点击卸载
$(document).on("click", ".btn-uninstall", function () {
var name = $(this).closest(".operate").data("name");
var uninstall = function (name, force) {
Fast.api.ajax({
url: 'addon/uninstall',
data: {name: name, force: force ? 1 : 0}
}, function (data, ret) {
delete Config['addons'][name];
Layer.closeAll();
$('.btn-refresh').trigger('click');
}, function (data, ret) {
if (ret && ret.code === -3) {
//插件目录发现影响全局的文件
Layer.open({
content: Template("conflicttpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
title: __('Warning'),
btn: [__('Continue uninstall'), __('Cancel')],
end: function () {
},
yes: function () {
uninstall(name, true);
}
});
} else {
Layer.alert(ret.msg);
}
return false;
});
};
Layer.confirm(__('Uninstall tips'), function () {
uninstall(name, false);
});
});
//点击配置
// 点击配置
$(document).on("click", ".btn-config", function () {
var name = $(this).closest(".operate").data("name");
Fast.api.open("addon/config?name=" + name, __('Setting'));
});
//点击启用/禁用
// 点击启用/禁用
$(document).on("click", ".btn-enable,.btn-disable", function () {
var name = $(this).closest(".operate").data("name");
var action = $(this).data("action");
var operate = function (name, action, force) {
Fast.api.ajax({
url: 'addon/state',
data: {name: name, action: action, force: force ? 1 : 0}
}, function (data, ret) {
var addon = Config['addons'][name];
addon.state = action === 'enable' ? 1 : 0;
Layer.closeAll();
$('.btn-refresh').trigger('click');
}, function (data, ret) {
if (ret && ret.code === -3) {
//插件目录发现影响全局的文件
Layer.open({
content: Template("conflicttpl", ret.data),
shade: 0.8,
area: ['800px', '600px'],
title: __('Warning'),
btn: [__('Continue operate'), __('Cancel')],
end: function () {
},
yes: function () {
operate(name, action, true);
}
});
} else {
Layer.alert(ret.msg);
}
return false;
});
};
operate(name, action, false);
});
//点击升级
// 点击升级
$(document).on("click", ".btn-upgrade", function () {
if ($(this).closest(".operate").find("a.btn-disable").size() > 0) {
Layer.alert(__('Please disable addon first'), {icon: 7});
... ... @@ -389,24 +421,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
}
var name = $(this).closest(".operate").data("name");
var version = $(this).data("version");
var userinfo = Controller.api.userinfo.get();
var uid = userinfo ? userinfo.id : 0;
var token = userinfo ? userinfo.token : '';
var upgrade = function (name) {
Fast.api.ajax({
url: 'addon/upgrade',
data: {name: name, uid: uid, token: token, version: version, faversion: Config.fastadmin.version}
}, function (data, ret) {
Config['addons'][name].version = version;
Layer.closeAll();
$('.btn-refresh').trigger('click');
}, function (data, ret) {
Layer.alert(ret.msg);
return false;
});
};
Layer.confirm(__('Upgrade tips'), function () {
upgrade(name);
upgrade(name, version);
});
});
... ...
... ... @@ -20,7 +20,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
sortName: 'weigh',
escape:false,
escape: false,
columns: [
[
{field: 'state', checkbox: true, },
... ... @@ -41,10 +41,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
});
// 为表格绑定事件
Table.api.bindevent(table);//当内容渲染完成后
Table.api.bindevent(table);
//默认隐藏所有子节点
//当内容渲染完成后
table.on('post-body.bs.table', function (e, settings, json, xhr) {
//默认隐藏所有子节点
//$("a.btn[data-id][data-pid][data-pid!=0]").closest("tr").hide();
$(".btn-node-sub.disabled").closest("tr").hide();
... ... @@ -57,8 +58,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
$(this).data("shown", !status);
return false;
});
$(".btn-change[data-id],.btn-delone,.btn-dragsort").data("success", function (data, ret) {
Fast.api.refreshmenu();
});
});
//批量删除后的回调
$(".toolbar > .btn-del,.toolbar .btn-more~ul>li>a").data("success", function (e) {
Fast.api.refreshmenu();
});
//展开隐藏一级
$(document.body).on("click", ".btn-toggle", function (e) {
$("a.btn[data-id][data-pid][data-pid!=0].disabled").closest("tr").hide();
... ... @@ -88,21 +96,21 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
api: {
formatter: {
title: function (value, row, index) {
return !row.ismenu ? "<span class='text-muted'>" + value + "</span>" : value;
return !row.ismenu || row.status == 'hidden' ? "<span class='text-muted'>" + value + "</span>" : value;
},
name: function (value, row, index) {
return !row.ismenu ? "<span class='text-muted'>" + value + "</span>" : value;
return !row.ismenu || row.status == 'hidden' ? "<span class='text-muted'>" + value + "</span>" : value;
},
menu: function (value, row, index) {
return "<a href='javascript:;' class='btn btn-" + (value ? "info" : "default") + " btn-xs btn-change' data-id='"
+ row.id + "' data-params='ismenu=" + (value ? 0 : 1) + "'>" + (value ? __('Yes') : __('No')) + "</a>";
},
icon: function (value, row, index) {
return '<i class="' + value + '"></i>';
return '<span class="' + (!row.ismenu || row.status == 'hidden' ? 'text-muted' : '') + '"><i class="' + value + '"></i></span>';
},
subnode: function (value, row, index) {
return '<a href="javascript:;" data-id="' + row['id'] + '" data-pid="' + row['pid'] + '" class="btn btn-xs '
+ (row['haschild'] == 1 ? 'btn-success' : 'btn-default disabled') + ' btn-node-sub"><i class="fa fa-sitemap"></i></a>';
return '<a href="javascript:;" data-id="' + row.id + '" data-pid="' + row.pid + '" class="btn btn-xs '
+ (row.haschild == 1 || row.ismenu == 1 ? 'btn-success' : 'btn-default disabled') + ' btn-node-sub"><i class="fa fa-sitemap"></i></a>';
}
},
bindevent: function () {
... ... @@ -113,7 +121,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
$("input[name='row[ismenu]']:checked").trigger("click");
var iconlist = [];
Form.api.bindevent($("form[role=form]"));
Form.api.bindevent($("form[role=form]"), function (data) {
Fast.api.refreshmenu();
});
$(document).on('click', ".btn-search-icon", function () {
if (iconlist.length == 0) {
$.get(Config.site.cdnurl + "/assets/libs/font-awesome/less/variables.less", function (ret) {
... ...
... ... @@ -178,6 +178,21 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
}
});
//刷新菜单事件
$(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();
$(data.menulist).insertBefore($(".sidebar-menu li:first"));
$("#nav ul li[role='presentation'].active a").trigger('click');
return false;
}, function () {
return false;
});
});
//这一行需要放在点击左侧链接事件之前
var addtabs = Config.referer ? localStorage.getItem("addtabs") : null;
... ... @@ -188,6 +203,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
} else {
$("ul.sidebar-menu li a[url!='javascript:;']:first").trigger("click");
}
//如果是刷新操作则直接返回刷新前的页面
if (Config.referer) {
if (Config.referer === $(addtabs).attr("url")) {
... ... @@ -203,11 +219,6 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
}
}
/**
* List of all the available skins
*
* @type Array
*/
var my_skins = [
"skin-blue",
"skin-white",
... ... @@ -224,19 +235,13 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
];
setup();
/**
* Toggles layout classes
*
* @param String cls the layout class to toggle
* @returns void
*/
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' && false) {
if ($('body').hasClass('fixed') && cls == 'fixed') {
AdminLTE.pushMenu.expandOnHover();
AdminLTE.layout.activate();
}
... ... @@ -244,61 +249,18 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
AdminLTE.controlSidebar._fix($(".control-sidebar"));
}
/**
* Replaces the old skin with the new skin
* @param String cls the new skin class
* @returns Boolean false to prevent link's default action
*/
function change_skin(cls) {
if (!$("body").hasClass(cls)) {
$.each(my_skins, function (i) {
$("body").removeClass(my_skins[i]);
});
$("body").addClass(cls);
store('skin', cls);
$("body").removeClass(my_skins.join(' ')).addClass(cls);
localStorage.setItem('skin', cls);
var cssfile = Config.site.cdnurl + "/assets/css/skins/" + cls + ".css";
$('head').append('<link rel="stylesheet" href="' + cssfile + '" type="text/css" />');
}
return false;
}
/**
* Store a new settings in the browser
*
* @param String name Name of the setting
* @param String val Value of the setting
* @returns void
*/
function store(name, val) {
if (typeof (Storage) !== "undefined") {
localStorage.setItem(name, val);
} else {
window.alert('Please use a modern browser to properly view this template!');
}
}
/**
* Get a prestored setting
*
* @param String name Name of of the setting
* @returns String The value of the setting | null
*/
function get(name) {
if (typeof (Storage) !== "undefined") {
return localStorage.getItem(name);
} else {
window.alert('Please use a modern browser to properly view this template!');
}
}
/**
* Retrieve default settings and apply them to the template
*
* @returns void
*/
function setup() {
var tmp = get('skin');
var tmp = localStorage.getItem('skin');
if (tmp && $.inArray(tmp, my_skins))
change_skin(tmp);
... ...
... ... @@ -28,7 +28,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
{field: 'id', title: __('Id')},
{field: 'pid', title: __('Pid'), visible: false},
{field: 'title', title: __('Title'), align: 'left'},
{field: 'name', title: __('Name')},
{field: 'name', title: __('Name'), align: 'left'},
{field: 'remark', title: __('Remark')},
{field: 'ismenu', title: __('Ismenu'), formatter: Controller.api.formatter.toggle},
{field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime, visible: false},
... ...
... ... @@ -104,7 +104,7 @@
var createFormCommon = function (pColumns, that) {
var htmlForm = [];
var opList = ['=', '>', '>=', '<', '<=', '!=', 'LIKE', 'LIKE %...%', 'NOT LIKE', 'IN', 'NOT IN', 'IN(...)', 'NOT IN(...)', 'BETWEEN', 'NOT BETWEEN', 'RANGE', 'NOT RANGE', 'IS NULL', 'IS NOT NULL'];
var opList = ['=', '>', '>=', '<', '<=', '!=', 'FIND_IN_SET', 'LIKE', 'LIKE %...%', 'NOT LIKE', 'IN', 'NOT IN', 'IN(...)', 'NOT IN(...)', 'BETWEEN', 'NOT BETWEEN', 'RANGE', 'NOT RANGE', 'IS NULL', 'IS NOT NULL'];
htmlForm.push(sprintf('<form class="form-horizontal form-commonsearch" action="%s" >', that.options.actionForm));
htmlForm.push('<fieldset>');
if (that.options.titleForm.length > 0)
... ...
... ... @@ -33,6 +33,7 @@
if (!that.options.templateView) {
return;
}
that.options.cardView = true;
};
... ...
... ... @@ -164,8 +164,7 @@ define(['jquery', 'bootstrap', 'toastr', 'layer', 'lang'], function ($, undefine
options.area = [top.$(".tab-pane.active").width() + "px", top.$(".tab-pane.active").height() + "px"];
options.offset = [top.$(".tab-pane.active").scrollTop() + "px", "0px"];
}
Layer.open(options);
return false;
return Layer.open(options);
},
//关闭窗口并回传数据
close: function (data) {
... ...
define(['frontend'], function (Frontend) {
});
\ No newline at end of file
... ...
... ... @@ -7,7 +7,7 @@ require.config({
}
],
//在打包压缩时将会把include中的模块合并到主文件中
include: ['css', 'layer', 'toastr', 'fast', 'backend', 'table', 'form', 'dragsort', 'drag', 'drop', 'addtabs', 'selectpage'],
include: ['css', 'layer', 'toastr', 'fast', 'backend', 'backend-init', 'table', 'form', 'dragsort', 'drag', 'drop', 'addtabs', 'selectpage'],
paths: {
'lang': "empty:",
'form': 'require-form',
... ... @@ -34,22 +34,20 @@ require.config({
'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/dragsort/jquery.dragsort',
'qrcode': '../libs/jquery-qrcode/jquery.qrcode.min',
'dragsort': '../libs/fastadmin-dragsort/jquery.dragsort',
'sortable': '../libs/Sortable/Sortable.min',
'addtabs': '../libs/jquery-addtabs/jquery.addtabs',
'addtabs': '../libs/fastadmin-addtabs/jquery.addtabs',
'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll',
'summernote': '../libs/summernote/dist/lang/summernote-zh-CN.min',
'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/layer/src/layer',
'layer': '../libs/layer/dist/layer',
'cookie': '../libs/jquery.cookie/jquery.cookie',
'cxselect': '../libs/jquery-cxselect/js/jquery.cxselect',
'cxselect': '../libs/fastadmin-cxselect/js/jquery.cxselect',
'template': '../libs/art-template/dist/template-native',
'selectpage': '../libs/selectpage/selectpage',
'selectpage': '../libs/fastadmin-selectpage/selectpage',
'citypicker': '../libs/city-picker/dist/js/city-picker.min',
'citypicker-data': '../libs/city-picker/dist/js/city-picker.data',
},
... ... @@ -106,7 +104,6 @@ require.config({
],
'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
'bootstrap-select-lang': ['bootstrap-select'],
'summernote': ['../libs/summernote/dist/summernote.min', 'css!../libs/summernote/dist/summernote.css'],
// 'toastr': ['css!../libs/toastr/toastr.min.css'],
'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],
'plupload': {
... ... @@ -116,7 +113,7 @@ require.config({
// '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/selectpage/selectpage.css'],
// 'selectpage': ['css!../libs/fastadmin-selectpage/selectpage.css'],
'citypicker': ['citypicker-data', 'css!../libs/city-picker/dist/css/city-picker.css']
},
baseUrl: requirejs.s.contexts._.config.config.site.cdnurl + '/assets/js/', //资源基础路径
... ... @@ -144,7 +141,7 @@ require(['jquery', 'bootstrap'], function ($, undefined) {
// 初始化
$(function () {
require(['fast'], function (Fast) {
require(['backend', 'addons'], function (Backend, Addons) {
require(['backend', 'backend-init', 'addons'], function (Backend, undefined, Addons) {
//加载相应模块
if (Config.jsname) {
require([Config.jsname], function (Controller) {
... ...
... ... @@ -40,6 +40,9 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
Form.api.submit($(ret), function (data, ret) {
that.holdSubmit(false);
submitBtn.removeClass("disabled");
if (false === $(this).triggerHandler("success.form", [data, ret])) {
return false;
}
if (typeof success === 'function') {
if (false === success.call($(this), data, ret)) {
return false;
... ... @@ -54,6 +57,9 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
return false;
}, function (data, ret) {
that.holdSubmit(false);
if (false === $(this).triggerHandler("error.form", [data, ret])) {
return false;
}
submitBtn.removeClass("disabled");
if (typeof error === 'function') {
if (false === error.call($(this), data, ret)) {
... ... @@ -81,13 +87,25 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
if ($(".selectpage", form).size() > 0) {
require(['selectpage'], function () {
$('.selectpage', form).selectPage({
source: 'ajax/selectpage',
eAjaxSuccess: function (data) {
data.list = typeof data.rows !== 'undefined' ? data.rows : (typeof data.list !== 'undefined' ? data.list : []);
data.totalRow = typeof data.total !== 'undefined' ? data.total : (typeof data.totalRow !== 'undefined' ? data.totalRow : data.list.length);
return data;
}
});
});
//给隐藏的元素添加上validate验证触发事件
$(form).on("change", ".selectpage-input-hidden", function () {
$(document).on("change", ".sp_hidden", function () {
$(this).trigger("validate");
});
$(document).on("change", ".sp_input", function () {
$(this).closest(".sp_container").find(".sp_hidden").trigger("change");
});
$(form).on("reset", function () {
setTimeout(function () {
$('.selectpage', form).selectPageClear();
}, 1);
});
}
},
cxselect: function (form) {
... ... @@ -132,6 +150,48 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
});
}
},
daterangepicker: function (form) {
//绑定日期时间元素事件
if ($(".datetimerange", form).size() > 0) {
require(['bootstrap-daterangepicker'], function () {
var ranges = {};
ranges[__('Today')] = [Moment().startOf('day'), Moment().endOf('day')];
ranges[__('Yesterday')] = [Moment().subtract(1, 'days').startOf('day'), Moment().subtract(1, 'days').endOf('day')];
ranges[__('Last 7 Days')] = [Moment().subtract(6, 'days').startOf('day'), Moment().endOf('day')];
ranges[__('Last 30 Days')] = [Moment().subtract(29, 'days').startOf('day'), Moment().endOf('day')];
ranges[__('This Month')] = [Moment().startOf('month'), Moment().endOf('month')];
ranges[__('Last Month')] = [Moment().subtract(1, 'month').startOf('month'), Moment().subtract(1, 'month').endOf('month')];
var options = {
timePicker: false,
autoUpdateInput: false,
timePickerSeconds: true,
timePicker24Hour: true,
autoApply: true,
locale: {
format: 'YYYY-MM-DD HH:mm:ss',
customRangeLabel: __("Custom Range"),
applyLabel: __("Apply"),
cancelLabel: __("Clear"),
},
ranges: ranges,
};
var origincallback = function (start, end) {
$(this.element).val(start.format(options.locale.format) + " - " + end.format(options.locale.format));
$(this.element).trigger('blur');
};
$(".datetimerange", form).each(function () {
var callback = typeof $(this).data('callback') == 'function' ? $(this).data('callback') : origincallback;
$(this).on('apply.daterangepicker', function (ev, picker) {
callback.call(picker, picker.startDate, picker.endDate);
});
$(this).on('cancel.daterangepicker', function (ev, picker) {
$(this).val('').trigger('blur');
});
$(this).daterangepicker($.extend({}, options, $(this).data()), callback);
});
});
}
},
plupload: function (form) {
//绑定plupload上传元素事件
if ($(".plupload", form).size() > 0) {
... ... @@ -287,6 +347,8 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U
events.selectpicker(form);
events.daterangepicker(form);
events.selectpage(form);
events.cxselect(form);
... ...
... ... @@ -7,7 +7,7 @@ require.config({
}
],
//在打包压缩时将会把include中的模块合并到主文件中
include: ['css', 'layer', 'toastr', 'fast', 'frontend'],
include: ['css', 'layer', 'toastr', 'fast', 'frontend', 'frontend-init'],
paths: {
'lang': "empty:",
'form': 'require-form',
... ... @@ -34,22 +34,20 @@ require.config({
'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/dragsort/jquery.dragsort',
'qrcode': '../libs/jquery-qrcode/jquery.qrcode.min',
'dragsort': '../libs/fastadmin-dragsort/jquery.dragsort',
'sortable': '../libs/Sortable/Sortable.min',
'addtabs': '../libs/jquery-addtabs/jquery.addtabs',
'addtabs': '../libs/fastadmin-addtabs/jquery.addtabs',
'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll',
'summernote': '../libs/summernote/dist/lang/summernote-zh-CN.min',
'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/layer/src/layer',
'layer': '../libs/layer/dist/layer',
'cookie': '../libs/jquery.cookie/jquery.cookie',
'cxselect': '../libs/jquery-cxselect/js/jquery.cxselect',
'cxselect': '../libs/fastadmin-cxselect/js/jquery.cxselect',
'template': '../libs/art-template/dist/template-native',
'selectpage': '../libs/selectpage/selectpage',
'selectpage': '../libs/fastadmin-selectpage/selectpage',
'citypicker': '../libs/city-picker/dist/js/city-picker.min',
'citypicker-data': '../libs/city-picker/dist/js/city-picker.data',
},
... ... @@ -106,7 +104,6 @@ require.config({
],
'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
'bootstrap-select-lang': ['bootstrap-select'],
'summernote': ['../libs/summernote/dist/summernote.min', 'css!../libs/summernote/dist/summernote.css'],
// 'toastr': ['css!../libs/toastr/toastr.min.css'],
'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],
'plupload': {
... ... @@ -116,7 +113,7 @@ require.config({
// '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/selectpage/selectpage.css'],
// 'selectpage': ['css!../libs/fastadmin-selectpage/selectpage.css'],
'citypicker': ['citypicker-data', 'css!../libs/city-picker/dist/css/city-picker.css']
},
baseUrl: requirejs.s.contexts._.config.config.site.cdnurl + '/assets/js/', //资源基础路径
... ...