mirror of https://gitee.com/karson/fastadmin.git
新增命令行一键生成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操作不触发模型事件的BUGpull/48/head v1.0.0.20180308_beta
parent
5155d80295
commit
d863f93d10
8
.bowerrc
8
.bowerrc
|
|
@ -1,3 +1,9 @@
|
|||
{
|
||||
"directory" : "public/assets/libs"
|
||||
"directory" : "public/assets/libs",
|
||||
"ignoredDependencies": [
|
||||
"file-saver",
|
||||
"html2canvas",
|
||||
"jspdf",
|
||||
"jspdf-autotable"
|
||||
]
|
||||
}
|
||||
|
|
@ -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)
|
||||
'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':
|
||||
|
|
@ -256,6 +288,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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace addons\{%addon%}\controller;
|
||||
|
||||
use think\addons\Controller;
|
||||
|
||||
class Index extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->error("当前插件暂无前台页面");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name = {%name%}
|
||||
title = 插件名称
|
||||
title = 插件名称({%name%})
|
||||
intro = FastAdmin插件
|
||||
author = yourname
|
||||
website = http://www.fastadmin.net
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?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' => '响应输出',
|
||||
];
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
<?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]));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,549 @@
|
|||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,460 @@
|
|||
<!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, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
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'";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ class Install extends Command
|
|||
// 写入数据库配置
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$output->isDebug())
|
||||
{
|
||||
@unlink($tempFile);
|
||||
}
|
||||
|
||||
$output->info("Build Successed!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
({
|
||||
cssIn: "{%cssBasePath%}{%cssBaseName%}.css",
|
||||
out: "{%cssBasePath%}{%cssBaseName%}.min.css",
|
||||
optimizeCss: "default"
|
||||
optimizeCss: "default",
|
||||
optimize: "{%optimize%}"
|
||||
})
|
||||
|
|
@ -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);
|
||||
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; %>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
<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"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<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>
|
||||
<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 -->
|
||||
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
<!-- Stats tab content -->
|
||||
<div class="tab-pane" id="control-sidebar-stats-tab">Stats Tab Content</div>
|
||||
<!-- Home tab content -->
|
||||
<div class="tab-pane" id="control-sidebar-home-tab">
|
||||
<h4 class="control-sidebar-heading">{:__('Home')}</h4>
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
<!-- Settings tab content -->
|
||||
<div class="tab-pane" id="control-sidebar-settings-tab">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- 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 -->
|
||||
|
|
@ -1 +1 @@
|
|||
<script src="__CDN__/assets/js/require.js" data-main="__CDN__/assets/js/require-backend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
|
||||
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-backend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?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;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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,19 +183,20 @@ 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6,4 +6,97 @@ return [
|
|||
'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 秒后自动跳转',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -100,6 +100,28 @@ class Menu
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出指定名称的菜单规则
|
||||
* @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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?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';
|
||||
// 定义时间戳字段名
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
<?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' => '操作失败!',
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
<script src="__CDN__/assets/js/require.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
|
||||
<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version}"></script>
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
24
bower.json
24
bower.json
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load 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) {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,3 @@
|
|||
define(['backend'], function (Backend) {
|
||||
|
||||
});
|
||||
|
|
@ -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) : {};
|
||||
|
|
@ -111,15 +112,16 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
$(".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,15 +199,10 @@ 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}
|
||||
|
|
@ -229,6 +228,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
}
|
||||
});
|
||||
$('.btn-refresh').trigger('click');
|
||||
Fast.api.refreshmenu();
|
||||
}, function (data, ret) {
|
||||
//如果是需要购买的插件则弹出二维码提示
|
||||
if (ret && ret.code === -1) {
|
||||
|
|
@ -280,26 +280,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
return false;
|
||||
});
|
||||
};
|
||||
if ($(that).data("type") !== 'free') {
|
||||
if (parseInt(uid) === 0) {
|
||||
return Layer.alert(__('Not login tips'), {
|
||||
title: __('Warning'),
|
||||
btn: [__('Login now'), __('Continue install')],
|
||||
yes: function (index, layero) {
|
||||
$(".btn-userinfo").trigger("click");
|
||||
},
|
||||
btn2: function () {
|
||||
install(name, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
install(name, 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',
|
||||
|
|
@ -308,6 +289,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
delete Config['addons'][name];
|
||||
Layer.closeAll();
|
||||
$('.btn-refresh').trigger('click');
|
||||
Fast.api.refreshmenu();
|
||||
}, function (data, ret) {
|
||||
if (ret && ret.code === -3) {
|
||||
//插件目录发现影响全局的文件
|
||||
|
|
@ -331,21 +313,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
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',
|
||||
|
|
@ -355,6 +323,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
addon.state = action === 'enable' ? 1 : 0;
|
||||
Layer.closeAll();
|
||||
$('.btn-refresh').trigger('click');
|
||||
Fast.api.refreshmenu();
|
||||
}, function (data, ret) {
|
||||
if (ret && ret.code === -3) {
|
||||
//插件目录发现影响全局的文件
|
||||
|
|
@ -378,21 +347,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
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});
|
||||
return false;
|
||||
}
|
||||
var name = $(this).closest(".operate").data("name");
|
||||
var version = $(this).data("version");
|
||||
var upgrade = function (name, 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}
|
||||
|
|
@ -400,13 +359,71 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function
|
|||
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;
|
||||
|
||||
if ($(that).data("type") !== 'free') {
|
||||
if (parseInt(uid) === 0) {
|
||||
return Layer.alert(__('Not login tips'), {
|
||||
title: __('Warning'),
|
||||
btn: [__('Login now'), __('Continue install')],
|
||||
yes: function (index, layero) {
|
||||
$(".btn-userinfo").trigger("click");
|
||||
},
|
||||
btn2: function () {
|
||||
install(name, version, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
install(name, version, false);
|
||||
});
|
||||
|
||||
// 点击卸载
|
||||
$(document).on("click", ".btn-uninstall", function () {
|
||||
var name = $(this).closest(".operate").data("name");
|
||||
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");
|
||||
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});
|
||||
return false;
|
||||
}
|
||||
var name = $(this).closest(".operate").data("name");
|
||||
var version = $(this).data("version");
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
define(['frontend'], function (Frontend) {
|
||||
|
||||
});
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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/', //资源基础路径
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
|||
define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table', 'bootstrap-table-lang', 'bootstrap-table-mobile', 'bootstrap-table-export', 'bootstrap-table-commonsearch', 'bootstrap-table-template'], function ($, undefined, Moment) {
|
||||
define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table', 'bootstrap-table-lang', 'bootstrap-table-export', 'bootstrap-table-commonsearch', 'bootstrap-table-template'], function ($, undefined, Moment) {
|
||||
var Table = {
|
||||
list: {},
|
||||
// Bootstrap-table 基础配置
|
||||
|
|
@ -32,7 +32,6 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
paginationPreText: __("Previous"),
|
||||
paginationNextText: __("Next"),
|
||||
paginationLastText: __("Last"),
|
||||
mobileResponsive: true, //是否自适应移动端
|
||||
cardView: false, //卡片视图
|
||||
checkOnInit: true, //是否在初始化时判断
|
||||
escape: true, //是否对内容进行转义
|
||||
|
|
@ -152,7 +151,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
}
|
||||
});
|
||||
// 处理选中筛选框后按钮的状态统一变更
|
||||
table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table fa.event.check', function () {
|
||||
table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table', function () {
|
||||
var ids = Table.api.selectedids(table);
|
||||
$(Table.config.disabledbtn, toolbar).toggleClass('disabled', !ids.length);
|
||||
});
|
||||
|
|
@ -176,7 +175,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
Fast.api.ajax({
|
||||
url: options.extend.import_url,
|
||||
data: {file: data.url},
|
||||
}, function () {
|
||||
}, function (data, ret) {
|
||||
table.bootstrapTable('refresh');
|
||||
});
|
||||
});
|
||||
|
|
@ -217,7 +216,8 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
$("tbody", table).dragsort({
|
||||
itemSelector: 'tr:visible',
|
||||
dragSelector: "a.btn-dragsort",
|
||||
dragEnd: function () {
|
||||
dragEnd: function (a, b) {
|
||||
var element = $("a.btn-dragsort", this);
|
||||
var data = table.bootstrapTable('getData');
|
||||
var current = data[parseInt($(this).data("index"))];
|
||||
var options = table.bootstrapTable('getOptions');
|
||||
|
|
@ -238,7 +238,21 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
table: options.extend.table
|
||||
}
|
||||
};
|
||||
Fast.api.ajax(params, function (data) {
|
||||
Fast.api.ajax(params, function (data, ret) {
|
||||
var success = $(element).data("success") || $.noop;
|
||||
if (typeof success === 'function') {
|
||||
if (false === success.call(element, data, ret)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
table.bootstrapTable('refresh');
|
||||
}, function () {
|
||||
var error = $(element).data("error") || $.noop;
|
||||
if (typeof error === 'function') {
|
||||
if (false === error.call(element, data, ret)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
table.bootstrapTable('refresh');
|
||||
});
|
||||
},
|
||||
|
|
@ -246,7 +260,9 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
});
|
||||
});
|
||||
$(table).on("click", "input[data-id][name='checkbox']", function (e) {
|
||||
table.trigger('fa.event.check');
|
||||
var ids = $(this).data("id");
|
||||
var row = Table.api.getrowbyid(ids);
|
||||
table.trigger('check.bs.table', [row, this]);
|
||||
});
|
||||
$(table).on("click", "[data-id].btn-change", function (e) {
|
||||
e.preventDefault();
|
||||
|
|
@ -255,14 +271,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
$(table).on("click", "[data-id].btn-edit", function (e) {
|
||||
e.preventDefault();
|
||||
var ids = $(this).data("id");
|
||||
var row = {};
|
||||
var options = table.bootstrapTable("getOptions");
|
||||
$.each(table.bootstrapTable('getData'), function (i, j) {
|
||||
if (j[options.pk] == ids) {
|
||||
row = j;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
var row = Table.api.getrowbyid(ids);
|
||||
row.ids = ids;
|
||||
var url = Table.api.replaceurl(options.extend.edit_url, row, table);
|
||||
Fast.api.open(url, __('Edit'), $(this).data() || {});
|
||||
|
|
@ -293,8 +302,21 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
url = this.replaceurl(url, {ids: ids}, table);
|
||||
var params = typeof data.params !== "undefined" ? (typeof data.params == 'object' ? $.param(data.params) : data.params) : '';
|
||||
var options = {url: url, data: {action: action, ids: ids, params: params}};
|
||||
Fast.api.ajax(options, function (data) {
|
||||
Fast.api.ajax(options, function (data, ret) {
|
||||
var success = $(element).data("success") || $.noop;
|
||||
if (typeof success === 'function') {
|
||||
if (false === success.call(element, data, ret)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
table.bootstrapTable('refresh');
|
||||
}, function (data, ret) {
|
||||
var error = $(element).data("error") || $.noop;
|
||||
if (typeof error === 'function') {
|
||||
if (false === error.call(element, data, ret)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 单元格元素事件
|
||||
|
|
@ -521,6 +543,22 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
|
|||
index = parseInt(index);
|
||||
var data = table.bootstrapTable('getData');
|
||||
return typeof data[index] !== 'undefined' ? data[index] : null;
|
||||
},
|
||||
// 根据行索引获取行数据
|
||||
getrowbyindex: function (table, index) {
|
||||
return Table.api.getrowdata(table, index);
|
||||
},
|
||||
// 根据主键ID获取行数据
|
||||
getrowbyid: function (table, id) {
|
||||
var row = {};
|
||||
var options = table.bootstrapTable("getOptions");
|
||||
$.each(table.bootstrapTable('getData'), function (i, j) {
|
||||
if (j[options.pk] == id) {
|
||||
row = j;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return row;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -15,7 +15,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");
|
||||
|
||||
@main-bg: #f1f4f6;
|
||||
@panel-intro-bg: darken(@main-bg,3%);
|
||||
|
|
@ -54,7 +54,6 @@ body.is-dialog {
|
|||
}
|
||||
}
|
||||
}
|
||||
.note-dialog .modal {z-index:1060;}
|
||||
|
||||
.bootstrap-dialog .modal-dialog {
|
||||
/*width: 70%;*/
|
||||
|
|
@ -668,13 +667,16 @@ form.form-horizontal .control-label {
|
|||
background: #ecf0f1;
|
||||
overflow:hidden;
|
||||
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-btn0{
|
||||
background-color: #18bc9c!important;
|
||||
border-color: #18bc9c!important;
|
||||
background-color: #18bc9c;
|
||||
border-color: #18bc9c;
|
||||
}
|
||||
}
|
||||
.layui-layer-footer {
|
||||
|
|
@ -763,6 +765,14 @@ form.form-horizontal .control-label {
|
|||
position:absolute;
|
||||
}
|
||||
}
|
||||
@media (min-width: 564px){
|
||||
body.is-dialog .daterangepicker {
|
||||
min-width: 130px;
|
||||
}
|
||||
body.is-dialog .daterangepicker .ranges ul {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
/*手机版样式*/
|
||||
@media (max-width: @screen-phone) {
|
||||
|
|
@ -774,6 +784,9 @@ form.form-horizontal .control-label {
|
|||
display:none;
|
||||
}
|
||||
}
|
||||
.fixed .content-wrapper, .fixed .right-side {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
/*平板样式*/
|
||||
@media (max-width: @screen-tablet) {
|
||||
|
|
|
|||
|
|
@ -61,15 +61,6 @@ body {
|
|||
.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;
|
||||
|
|
@ -329,7 +320,7 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
footer.footer{width:100%;color: #aaa;background: #555;margin-top:25px;}
|
||||
footer.footer{width:100%;color: #aaa;background: #555;margin-top:25px;position: fixed;bottom: 0;}
|
||||
footer.footer ul{margin:60px 0 30px 0;padding:0;}
|
||||
footer.footer ul li.f-tit{margin-bottom:10px;font-size: 14px;color: #fff;}
|
||||
footer.footer ul li{line-height: 26px;white-space: nowrap;list-style: none;margin:0;padding:0;}
|
||||
|
|
|
|||
Loading…
Reference in New Issue