代码初始化
This commit is contained in:
346
application/admin/command/Addon.php
Normal file
346
application/admin/command/Addon.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
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
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('addon')
|
||||
->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null)
|
||||
->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/uninstall/refresh/package/move)', 'create')
|
||||
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null)
|
||||
->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null)
|
||||
->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null)
|
||||
->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null)
|
||||
->addOption('domain', 'd', Option::VALUE_OPTIONAL, 'domain', null)
|
||||
->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null)
|
||||
->setDescription('Addon manager');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
\think\Config::load(dirname(dirname(__FILE__)) . DS . 'config.php');
|
||||
$name = $input->getOption('name') ?: '';
|
||||
$action = $input->getOption('action') ?: '';
|
||||
if (stripos($name, 'addons' . DS) !== false) {
|
||||
$name = explode(DS, $name)[1];
|
||||
}
|
||||
//强制覆盖
|
||||
$force = $input->getOption('force');
|
||||
//版本
|
||||
$release = $input->getOption('release') ?: '';
|
||||
//uid
|
||||
$uid = $input->getOption('uid') ?: '';
|
||||
//token
|
||||
$token = $input->getOption('token') ?: '';
|
||||
|
||||
include dirname(__DIR__) . DS . 'common.php';
|
||||
|
||||
if (!$name && !in_array($action, ['refresh'])) {
|
||||
throw new Exception('Addon name could not be empty');
|
||||
}
|
||||
if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package', 'move'])) {
|
||||
throw new Exception('Please input correct action name');
|
||||
}
|
||||
|
||||
// 查询一次SQL,判断连接是否正常
|
||||
Db::execute("SELECT 1");
|
||||
|
||||
$addonDir = ADDON_PATH . $name . DS;
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
//非覆盖模式时如果存在则报错
|
||||
if (is_dir($addonDir) && !$force) {
|
||||
throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true ");
|
||||
}
|
||||
//如果存在先移除
|
||||
if (is_dir($addonDir)) {
|
||||
rmdirs($addonDir);
|
||||
}
|
||||
mkdir($addonDir, 0755, true);
|
||||
mkdir($addonDir . DS . 'controller', 0755, true);
|
||||
$menuList = \app\common\library\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),
|
||||
'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu) . ";\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':
|
||||
case 'enable':
|
||||
try {
|
||||
//调用启用、禁用的方法
|
||||
Service::$action($name, 0);
|
||||
} catch (AddonException $e) {
|
||||
if ($e->getCode() != -3) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
if (!$force) {
|
||||
//如果有冲突文件则提醒
|
||||
$data = $e->getData();
|
||||
foreach ($data['conflictlist'] as $k => $v) {
|
||||
$output->warning($v);
|
||||
}
|
||||
$output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: ");
|
||||
$line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
|
||||
if (trim($line) != 'yes') {
|
||||
throw new Exception("Operation is aborted!");
|
||||
}
|
||||
}
|
||||
//调用启用、禁用的方法
|
||||
Service::$action($name, 1);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
$output->info(ucfirst($action) . " Successed!");
|
||||
break;
|
||||
case 'uninstall':
|
||||
//非覆盖模式时如果存在则报错
|
||||
if (!$force) {
|
||||
throw new Exception("If you need to uninstall addon, use the parameter --force=true ");
|
||||
}
|
||||
try {
|
||||
Service::uninstall($name, 0);
|
||||
} catch (AddonException $e) {
|
||||
if ($e->getCode() != -3) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
if (!$force) {
|
||||
//如果有冲突文件则提醒
|
||||
$data = $e->getData();
|
||||
foreach ($data['conflictlist'] as $k => $v) {
|
||||
$output->warning($v);
|
||||
}
|
||||
$output->info("Are you sure you want to delete all those files? Type 'yes' to continue: ");
|
||||
$line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
|
||||
if (trim($line) != 'yes') {
|
||||
throw new Exception("Operation is aborted!");
|
||||
}
|
||||
}
|
||||
Service::uninstall($name, 1);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
|
||||
$output->info("Uninstall Successed!");
|
||||
break;
|
||||
case 'refresh':
|
||||
Service::refresh();
|
||||
$output->info("Refresh Successed!");
|
||||
break;
|
||||
case 'package':
|
||||
$infoFile = $addonDir . 'info.ini';
|
||||
if (!is_file($infoFile)) {
|
||||
throw new Exception(__('Addon info file was not found'));
|
||||
}
|
||||
|
||||
$info = get_addon_info($name);
|
||||
if (!$info) {
|
||||
throw new Exception(__('Addon info file data incorrect'));
|
||||
}
|
||||
$infoname = $info['name'] ?? '';
|
||||
if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) {
|
||||
throw new Exception(__('Addon info name incorrect'));
|
||||
}
|
||||
|
||||
$infoversion = $info['version'] ?? '';
|
||||
if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) {
|
||||
throw new Exception(__('Addon info version incorrect'));
|
||||
}
|
||||
|
||||
$addonTmpDir = RUNTIME_PATH . 'addons' . DS;
|
||||
if (!is_dir($addonTmpDir)) {
|
||||
@mkdir($addonTmpDir, 0755, true);
|
||||
}
|
||||
$addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip';
|
||||
if (!class_exists('ZipArchive')) {
|
||||
throw new Exception(__('ZinArchive not install'));
|
||||
}
|
||||
$zip = new \ZipArchive;
|
||||
$zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
|
||||
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
$addonDir = str_replace(DS, '/', $addonDir);
|
||||
$excludeDirRegex = "/\/(\.git|\.svn|\.vscode|\.idea|unpackage)\//i";
|
||||
foreach ($files as $name => $file) {
|
||||
$filePath = str_replace(DS, '/', $file->getPathname());
|
||||
if ($file->isDir() || preg_match($excludeDirRegex, $filePath))
|
||||
continue;
|
||||
$relativePath = substr($filePath, strlen($addonDir));
|
||||
if (!in_array($file->getFilename(), ['.DS_Store', 'Thumbs.db'])) {
|
||||
$zip->addFile($filePath, $relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
$output->info("Package Resource Path:" . $addonFile);
|
||||
$output->info("Package Successed!");
|
||||
break;
|
||||
case 'move':
|
||||
$movePath = [
|
||||
'adminOnlySelfDir' => ['admin/behavior', 'admin/controller', 'admin/library', 'admin/model', 'admin/validate', 'admin/view'],
|
||||
'adminAllSubDir' => ['admin/lang'],
|
||||
'publicDir' => ['public/assets/addons', 'public/assets/js/backend']
|
||||
];
|
||||
$paths = [];
|
||||
$appPath = str_replace('/', DS, APP_PATH);
|
||||
$rootPath = str_replace('/', DS, ROOT_PATH);
|
||||
foreach ($movePath as $k => $items) {
|
||||
switch ($k) {
|
||||
case 'adminOnlySelfDir':
|
||||
foreach ($items as $v) {
|
||||
$v = str_replace('/', DS, $v);
|
||||
$oldPath = $appPath . $v . DS . $name;
|
||||
$newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $name;
|
||||
$paths[$oldPath] = $newPath;
|
||||
}
|
||||
break;
|
||||
case 'adminAllSubDir':
|
||||
foreach ($items as $v) {
|
||||
$v = str_replace('/', DS, $v);
|
||||
$vPath = $appPath . $v;
|
||||
$list = scandir($vPath);
|
||||
foreach ($list as $_v) {
|
||||
if (!in_array($_v, ['.', '..']) && is_dir($vPath . DS . $_v)) {
|
||||
$oldPath = $appPath . $v . DS . $_v . DS . $name;
|
||||
$newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $_v . DS . $name;
|
||||
$paths[$oldPath] = $newPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'publicDir':
|
||||
foreach ($items as $v) {
|
||||
$v = str_replace('/', DS, $v);
|
||||
$oldPath = $rootPath . $v . DS . $name;
|
||||
$newPath = $rootPath . 'addons' . DS . $name . DS . $v . DS . $name;
|
||||
$paths[$oldPath] = $newPath;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach ($paths as $oldPath => $newPath) {
|
||||
if (is_dir($oldPath)) {
|
||||
if ($force) {
|
||||
if (is_dir($newPath)) {
|
||||
$list = scandir($newPath);
|
||||
foreach ($list as $_v) {
|
||||
if (!in_array($_v, ['.', '..'])) {
|
||||
$file = $newPath . DS . $_v;
|
||||
@chmod($file, 0777);
|
||||
@unlink($file);
|
||||
}
|
||||
}
|
||||
@rmdir($newPath);
|
||||
}
|
||||
}
|
||||
copydirs($oldPath, $newPath);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取创建菜单的数组
|
||||
* @param array $menu
|
||||
* @return array
|
||||
*/
|
||||
protected function getCreateMenu($menu)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($menu as $k => & $v) {
|
||||
$arr = [
|
||||
'name' => $v['name'],
|
||||
'title' => $v['title'],
|
||||
];
|
||||
if ($v['icon'] != 'fa fa-circle-o') {
|
||||
$arr['icon'] = $v['icon'];
|
||||
}
|
||||
if ($v['ismenu']) {
|
||||
$arr['ismenu'] = $v['ismenu'];
|
||||
}
|
||||
if (isset($v['childlist']) && $v['childlist']) {
|
||||
$arr['sublist'] = $this->getCreateMenu($v['childlist']);
|
||||
}
|
||||
$result[] = $arr;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入到文件
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @param string $pathname
|
||||
* @return mixed
|
||||
*/
|
||||
protected function writeToFile($name, $data, $pathname)
|
||||
{
|
||||
$search = $replace = [];
|
||||
foreach ($data as $k => $v) {
|
||||
$search[] = "{%{$k}%}";
|
||||
$replace[] = $v;
|
||||
}
|
||||
$stub = file_get_contents($this->getStub($name));
|
||||
$content = str_replace($search, $replace, $stub);
|
||||
|
||||
if (!is_dir(dirname($pathname))) {
|
||||
mkdir(strtolower(dirname($pathname)), 0755, true);
|
||||
}
|
||||
return file_put_contents($pathname, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础模板
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub($name)
|
||||
{
|
||||
return __DIR__ . '/Addon/stubs/' . $name . '.stub';
|
||||
}
|
||||
}
|
||||
54
application/admin/command/Addon/stubs/addon.stub
Normal file
54
application/admin/command/Addon/stubs/addon.stub
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace addons\{%name%};
|
||||
|
||||
use app\common\library\Menu;
|
||||
use think\Addons;
|
||||
|
||||
/**
|
||||
* 插件
|
||||
*/
|
||||
class {%addonClassName%} extends Addons
|
||||
{
|
||||
|
||||
/**
|
||||
* 插件安装方法
|
||||
* @return bool
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
{%addonInstallMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件卸载方法
|
||||
* @return bool
|
||||
*/
|
||||
public function uninstall()
|
||||
{
|
||||
{%addonUninstallMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件启用方法
|
||||
* @return bool
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
{%addonEnableMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件禁用方法
|
||||
* @return bool
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
{%addonDisableMenu%}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
44
application/admin/command/Addon/stubs/config.stub
Normal file
44
application/admin/command/Addon/stubs/config.stub
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
//配置唯一标识
|
||||
'name' => 'username',
|
||||
//显示的标题
|
||||
'title' => '用户名',
|
||||
//类型
|
||||
'type' => 'string',
|
||||
//分组
|
||||
'group' => '',
|
||||
//动态显示
|
||||
'visible' => '',
|
||||
//数据字典
|
||||
'content' => [
|
||||
],
|
||||
//值
|
||||
'value' => '',
|
||||
//验证规则
|
||||
'rule' => 'required',
|
||||
//错误消息
|
||||
'msg' => '',
|
||||
//提示消息
|
||||
'tip' => '',
|
||||
//成功消息
|
||||
'ok' => '',
|
||||
//扩展信息
|
||||
'extend' => ''
|
||||
],
|
||||
[
|
||||
'name' => 'password',
|
||||
'title' => '密码',
|
||||
'type' => 'string',
|
||||
'content' => [
|
||||
],
|
||||
'value' => '',
|
||||
'rule' => 'required',
|
||||
'msg' => '',
|
||||
'tip' => '',
|
||||
'ok' => '',
|
||||
'extend' => ''
|
||||
],
|
||||
];
|
||||
15
application/admin/command/Addon/stubs/controller.stub
Normal file
15
application/admin/command/Addon/stubs/controller.stub
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace addons\{%addon%}\controller;
|
||||
|
||||
use think\addons\Controller;
|
||||
|
||||
class Index extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->error("当前插件暂无前台页面");
|
||||
}
|
||||
|
||||
}
|
||||
7
application/admin/command/Addon/stubs/info.stub
Normal file
7
application/admin/command/Addon/stubs/info.stub
Normal file
@@ -0,0 +1,7 @@
|
||||
name = {%name%}
|
||||
title = 插件名称{%name%}
|
||||
intro = 插件介绍
|
||||
author = yourname
|
||||
website = https://www.fastadmin.net
|
||||
version = 1.0.0
|
||||
state = 1
|
||||
195
application/admin/command/Api.php
Normal file
195
application/admin/command/Api.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?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('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null)
|
||||
->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn')
|
||||
->addOption('addon', 'a', Option::VALUE_OPTIONAL, 'addon name', null)
|
||||
->addOption('controller', 'r', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name', null)
|
||||
->setDescription('Build Api document from controller');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$apiDir = __DIR__ . DS . 'Api' . DS;
|
||||
|
||||
$force = $input->getOption('force');
|
||||
$url = $input->getOption('url');
|
||||
$language = $input->getOption('language');
|
||||
$template = $input->getOption('template');
|
||||
if (!preg_match("/^([a-z0-9]+)\.html\$/i", $template)) {
|
||||
throw new Exception('template file not correct');
|
||||
}
|
||||
$language = $language ? $language : 'zh-cn';
|
||||
$langFile = $apiDir . 'lang' . DS . $language . '.php';
|
||||
if (!is_file($langFile)) {
|
||||
throw new Exception('language file not found');
|
||||
}
|
||||
$lang = include_once $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 . $template;
|
||||
if (!is_file($template_file)) {
|
||||
throw new Exception('template file not found');
|
||||
}
|
||||
// 额外的类
|
||||
$classes = $input->getOption('class');
|
||||
// 标题
|
||||
$title = $input->getOption('title');
|
||||
// 模块
|
||||
$module = $input->getOption('module');
|
||||
// 插件
|
||||
$addon = $input->getOption('addon');
|
||||
|
||||
$moduleDir = $addonDir = '';
|
||||
if ($addon) {
|
||||
$addonInfo = get_addon_info($addon);
|
||||
if (!$addonInfo) {
|
||||
throw new Exception('addon not found');
|
||||
}
|
||||
$moduleDir = ADDON_PATH . $addon . DS;
|
||||
} else {
|
||||
$moduleDir = APP_PATH . $module . DS;
|
||||
}
|
||||
if (!is_dir($moduleDir)) {
|
||||
throw new Exception('module not found');
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, '7.0.0', '<')) {
|
||||
throw new Exception("Requires PHP version 7.0 or newer");
|
||||
}
|
||||
|
||||
//控制器名
|
||||
$controller = $input->getOption('controller') ?: [];
|
||||
if (!$controller) {
|
||||
$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() && $file->getExtension() == 'php') {
|
||||
$filePath = $file->getRealPath();
|
||||
$className = $this->getClassFromFile($filePath);
|
||||
if ($className) {
|
||||
$classes[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($controller as $index => $item) {
|
||||
$filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php';
|
||||
$className = $this->getClassFromFile($filePath);
|
||||
if ($className) {
|
||||
$classes[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$classes = array_unique(array_filter($classes));
|
||||
|
||||
$config = [
|
||||
'sitename' => config('site.name'),
|
||||
'title' => $title,
|
||||
'author' => config('site.name'),
|
||||
'description' => '',
|
||||
'apiurl' => $url,
|
||||
'language' => $language,
|
||||
];
|
||||
|
||||
$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!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件获取命名空间和类名
|
||||
*
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
protected function getClassFromFile($filename)
|
||||
{
|
||||
$getNext = null;
|
||||
$isNamespace = false;
|
||||
$skipNext = false;
|
||||
$namespace = '';
|
||||
$class = '';
|
||||
foreach (\PhpToken::tokenize(file_get_contents($filename)) as $token) {
|
||||
if (!$token->isIgnorable()) {
|
||||
$name = $token->getTokenName();
|
||||
switch ($name) {
|
||||
case 'T_NAMESPACE':
|
||||
$isNamespace = true;
|
||||
break;
|
||||
case 'T_EXTENDS':
|
||||
case 'T_USE':
|
||||
case 'T_IMPLEMENTS':
|
||||
$skipNext = true;
|
||||
break;
|
||||
case 'T_CLASS':
|
||||
if ($skipNext) {
|
||||
$skipNext = false;
|
||||
} else {
|
||||
$getNext = strtolower(substr($name, 2));
|
||||
}
|
||||
break;
|
||||
case 'T_NAME_QUALIFIED':
|
||||
case 'T_NS_SEPARATOR':
|
||||
case 'T_STRING':
|
||||
case ';':
|
||||
if ($isNamespace) {
|
||||
if ($name == ';') {
|
||||
$isNamespace = false;
|
||||
} else {
|
||||
$namespace .= $token->text;
|
||||
}
|
||||
} elseif ($skipNext) {
|
||||
$skipNext = false;
|
||||
} elseif ($getNext == 'class') {
|
||||
$class = $token->text;
|
||||
$getNext = null;
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$getNext = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$className = $namespace . '\\' . $class;
|
||||
return preg_match('/([a-z0-9_\\]+)([a-z0-9_]+)$/i', $className) ? $className : '';
|
||||
}
|
||||
}
|
||||
25
application/admin/command/Api/lang/zh-cn.php
Normal file
25
application/admin/command/Api/lang/zh-cn.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'Info' => '基础信息',
|
||||
'Sandbox' => '在线测试',
|
||||
'Sampleoutput' => '返回示例',
|
||||
'Headers' => 'Headers',
|
||||
'Parameters' => '参数',
|
||||
'Body' => '正文',
|
||||
'Name' => '名称',
|
||||
'Type' => '类型',
|
||||
'Required' => '必选',
|
||||
'Description' => '描述',
|
||||
'Send' => '提交',
|
||||
'Reset' => '重置',
|
||||
'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中',
|
||||
'Apiurltips' => 'API接口URL',
|
||||
'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中',
|
||||
'Authorization' => '权限',
|
||||
'NeedLogin' => '登录',
|
||||
'NeedRight' => '鉴权',
|
||||
'ReturnHeaders' => '响应头',
|
||||
'ReturnParameters' => '返回参数',
|
||||
'Response' => '响应输出',
|
||||
];
|
||||
259
application/admin/command/Api/library/Builder.php
Normal file
259
application/admin/command/Api/library/Builder.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?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 <karson@fastadmin.net>
|
||||
*/
|
||||
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 = new \think\View(Config::get('template'), Config::get('view_replace_str'));
|
||||
}
|
||||
|
||||
protected function extractAnnotations()
|
||||
{
|
||||
foreach ($this->classes as $class) {
|
||||
$classAnnotation = Extractor::getClassAnnotations($class);
|
||||
// 如果忽略
|
||||
if (isset($classAnnotation['ApiInternal'])) {
|
||||
continue;
|
||||
}
|
||||
Extractor::getClassMethodAnnotations($class);
|
||||
//Extractor::getClassPropertyValues($class);
|
||||
}
|
||||
$allClassAnnotation = Extractor::getAllClassAnnotations();
|
||||
$allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations();
|
||||
//$allClassPropertyValue = Extractor::getAllClassPropertyValues();
|
||||
|
||||
// foreach ($allClassMethodAnnotation as $className => &$methods) {
|
||||
// foreach ($methods as &$method) {
|
||||
// //权重判断
|
||||
// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) {
|
||||
// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh'];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// unset($methods);
|
||||
return [$allClassAnnotation, $allClassMethodAnnotation];
|
||||
}
|
||||
|
||||
protected function generateHeadersTemplate($docs)
|
||||
{
|
||||
if (!isset($docs['ApiHeaders'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$headerslist = array();
|
||||
foreach ($docs['ApiHeaders'] as $params) {
|
||||
$tr = array(
|
||||
'name' => $params['name'] ?? '',
|
||||
'type' => $params['type'] ?? 'string',
|
||||
'sample' => $params['sample'] ?? '',
|
||||
'required' => $params['required'] ?? false,
|
||||
'description' => $params['description'] ?? '',
|
||||
);
|
||||
$headerslist[] = $tr;
|
||||
}
|
||||
|
||||
return $headerslist;
|
||||
}
|
||||
|
||||
protected function generateParamsTemplate($docs)
|
||||
{
|
||||
if (!isset($docs['ApiParams'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$typeArr = [
|
||||
'integer' => 'number',
|
||||
'file' => 'file',
|
||||
];
|
||||
$paramslist = array();
|
||||
foreach ($docs['ApiParams'] as $params) {
|
||||
$type = strtolower($params['type'] ?? 'string');
|
||||
$inputtype = $typeArr[$type] ?? ($params['name'] == 'password' ? 'password' : 'text');
|
||||
$tr = array(
|
||||
'name' => $params['name'],
|
||||
'type' => $type,
|
||||
'inputtype' => $inputtype,
|
||||
'sample' => $params['sample'] ?? '',
|
||||
'required' => $params['required'] ?? true,
|
||||
'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' => $params['sample'] ?? '',
|
||||
'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No',
|
||||
'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' => $params['type'] ?? 'string',
|
||||
'sample' => $params['sample'] ?? '',
|
||||
'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 $labes[$method] ?? $labes['GET'];
|
||||
}
|
||||
|
||||
public function parse()
|
||||
{
|
||||
list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations();
|
||||
|
||||
$sectorArr = [];
|
||||
foreach ($allClassAnnotations as $index => &$allClassAnnotation) {
|
||||
// 如果设置隐藏,则不显示在文档
|
||||
if (isset($allClassAnnotation['ApiInternal'])) {
|
||||
continue;
|
||||
}
|
||||
$sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0];
|
||||
$sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0;
|
||||
}
|
||||
unset($allClassAnnotation);
|
||||
|
||||
arsort($sectorArr);
|
||||
$routes = include_once CONF_PATH . 'route.php';
|
||||
$subdomain = false;
|
||||
if (config('url_domain_deploy') && isset($routes['__domain__']) && isset($routes['__domain__']['api']) && $routes['__domain__']['api']) {
|
||||
$subdomain = true;
|
||||
}
|
||||
$counter = 0;
|
||||
$section = null;
|
||||
$weigh = 0;
|
||||
$docsList = [];
|
||||
foreach ($allClassMethodAnnotations 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;
|
||||
}
|
||||
$route = is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0];
|
||||
if ($subdomain) {
|
||||
$route = substr($route, 4);
|
||||
}
|
||||
$docsList[$section][$name] = [
|
||||
'id' => $counter,
|
||||
'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0],
|
||||
'methodLabel' => $this->generateBadgeForMethod($docs),
|
||||
'section' => $section,
|
||||
'route' => $route,
|
||||
'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][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),
|
||||
'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0],
|
||||
'return' => isset($docs['ApiReturn']) ? (is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0]) : '',
|
||||
'needLogin' => $docs['ApiPermissionLogin'][0],
|
||||
'needRight' => $docs['ApiPermissionRight'][0],
|
||||
];
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
//重建排序
|
||||
foreach ($docsList as $index => &$methods) {
|
||||
$methodSectorArr = [];
|
||||
foreach ($methods as $name => $method) {
|
||||
$methodSectorArr[$name] = $method['weigh'] ?? 0;
|
||||
}
|
||||
arsort($methodSectorArr);
|
||||
$methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods);
|
||||
}
|
||||
$docsList = array_merge(array_flip(array_keys($sectorArr)), $docsList);
|
||||
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]));
|
||||
}
|
||||
}
|
||||
544
application/admin/command/Api/library/Extractor.php
Normal file
544
application/admin/command/Api/library/Extractor.php
Normal file
@@ -0,0 +1,544 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\command\Api\library;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private static $classAnnotationCache;
|
||||
|
||||
private static $classMethodAnnotationCache;
|
||||
|
||||
private static $classPropertyValueCache;
|
||||
|
||||
/**
|
||||
* 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::$classAnnotationCache all annotated elements
|
||||
*/
|
||||
public static function getClassAnnotations($className)
|
||||
{
|
||||
if (!isset(self::$classAnnotationCache[$className])) {
|
||||
$class = new \ReflectionClass($className);
|
||||
$annotationArr = self::parseAnnotations($class->getDocComment());
|
||||
$annotationArr['ApiTitle'] = !isset($annotationArr['ApiTitle'][0]) || !trim($annotationArr['ApiTitle'][0]) ? [$class->getShortName()] : $annotationArr['ApiTitle'];
|
||||
self::$classAnnotationCache[$className] = $annotationArr;
|
||||
}
|
||||
|
||||
return self::$classAnnotationCache[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类所有方法的属性配置
|
||||
* @param $className
|
||||
* @return mixed
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function getClassMethodAnnotations($className)
|
||||
{
|
||||
$class = new \ReflectionClass($className);
|
||||
|
||||
foreach ($class->getMethods() as $object) {
|
||||
self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name);
|
||||
}
|
||||
|
||||
return self::$classMethodAnnotationCache[$className];
|
||||
}
|
||||
|
||||
public static function getClassPropertyValues($className)
|
||||
{
|
||||
$class = new \ReflectionClass($className);
|
||||
|
||||
foreach ($class->getProperties() as $object) {
|
||||
self::$classPropertyValueCache[$className][$object->name] = self::getClassPropertyValue($className, $object->name);
|
||||
}
|
||||
|
||||
return self::$classMethodAnnotationCache[$className];
|
||||
}
|
||||
|
||||
public static function getAllClassAnnotations()
|
||||
{
|
||||
return self::$classAnnotationCache;
|
||||
}
|
||||
|
||||
public static function getAllClassMethodAnnotations()
|
||||
{
|
||||
return self::$classMethodAnnotationCache;
|
||||
}
|
||||
|
||||
public static function getAllClassPropertyValues()
|
||||
{
|
||||
return self::$classPropertyValueCache;
|
||||
}
|
||||
|
||||
public static function getClassPropertyValue($className, $property)
|
||||
{
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$reflectionClass = new \ReflectionClass($className);
|
||||
$reflectionProperty = $reflectionClass->getProperty($property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
return $reflectionProperty->getValue($reflectionClass->newInstanceWithoutConstructor());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
$methodAnnotations['ApiTitle'] = !isset($methodAnnotations['ApiTitle'][0]) || !trim($methodAnnotations['ApiTitle'][0]) ? [$method->getName()] : $methodAnnotations['ApiTitle'];
|
||||
|
||||
$classAnnotations = self::parseAnnotations($dockblockClass);
|
||||
$classAnnotations['ApiTitle'] = !isset($classAnnotations['ApiTitle'][0]) || !trim($classAnnotations['ApiTitle'][0]) ? [$class->getShortName()] : $classAnnotations['ApiTitle'];
|
||||
|
||||
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);
|
||||
|
||||
if (!isset($methodAnnotations['ApiMethod'])) {
|
||||
$methodAnnotations['ApiMethod'] = ['get'];
|
||||
}
|
||||
if (!isset($methodAnnotations['ApiWeigh'])) {
|
||||
$methodAnnotations['ApiWeigh'] = [0];
|
||||
}
|
||||
if (!isset($methodAnnotations['ApiSummary'])) {
|
||||
$methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle'];
|
||||
}
|
||||
|
||||
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['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'] : $classAnnotations['ApiTitle'];
|
||||
}
|
||||
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'] = !$methodAnnotations['ApiPermissionLogin'][0] ? [false] : [!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) {
|
||||
$name = $matches['name'][$i];
|
||||
$value = '';
|
||||
// annotations has arguments
|
||||
if (isset($matches['args'][$i])) {
|
||||
$argsParts = trim($matches['args'][$i]);
|
||||
if ($name == 'ApiReturn') {
|
||||
$value = $argsParts;
|
||||
} elseif ($matches['args'][$i] != '') {
|
||||
$argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts);
|
||||
$value = self::parseArgs($argsParts);
|
||||
if (is_string($value)) {
|
||||
$value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$annotations[$name][] = $value;
|
||||
}
|
||||
}
|
||||
if (stripos($docblock, '@ApiInternal') !== false) {
|
||||
$annotations['ApiInternal'] = [true];
|
||||
}
|
||||
if (!isset($annotations['ApiTitle'])) {
|
||||
preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr);
|
||||
$title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : '';
|
||||
$annotations['ApiTitle'] = [$title];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
654
application/admin/command/Api/template/index.html
Normal file
654
application/admin/command/Api/template/index.html
Normal file
@@ -0,0 +1,654 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<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="">
|
||||
<title>{$config.title}</title>
|
||||
|
||||
<!-- Bootstrap Core CSS -->
|
||||
<link href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Plugin CSS -->
|
||||
<link href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 70px; margin-bottom: 15px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: "Roboto", "SF Pro SC", "SF Pro Display", "SF Pro Icons", "PingFang SC", BlinkMacSystemFont, -apple-system, "Segoe UI", "Microsoft Yahei", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
h2 { font-size: 1.2em; }
|
||||
hr { margin-top: 10px; }
|
||||
.tab-pane { padding-top: 10px; }
|
||||
.mt0 { margin-top: 0px; }
|
||||
.footer { font-size: 12px; color: #666; }
|
||||
.docs-list .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;}
|
||||
.list-group.panel > .list-group-item {
|
||||
}
|
||||
.list-group-item:last-child {
|
||||
border-radius:0;
|
||||
}
|
||||
h4.panel-title a {
|
||||
font-weight:normal;
|
||||
font-size:14px;
|
||||
}
|
||||
h4.panel-title a .text-muted {
|
||||
font-size:12px;
|
||||
font-weight:normal;
|
||||
font-family: 'Verdana';
|
||||
}
|
||||
#sidebar {
|
||||
width: 220px;
|
||||
position: fixed;
|
||||
margin-left: -240px;
|
||||
overflow-y:auto;
|
||||
}
|
||||
#sidebar > .list-group {
|
||||
margin-bottom:0;
|
||||
}
|
||||
#sidebar > .list-group > a{
|
||||
text-indent:0;
|
||||
}
|
||||
#sidebar .child > a .tag{
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 11px;
|
||||
}
|
||||
#sidebar .child > a .pull-right{
|
||||
margin-left:3px;
|
||||
}
|
||||
#sidebar .child {
|
||||
border:1px solid #ddd;
|
||||
border-bottom:none;
|
||||
}
|
||||
#sidebar .child:last-child {
|
||||
border-bottom:1px solid #ddd;
|
||||
}
|
||||
#sidebar .child > a {
|
||||
border:0;
|
||||
min-height: 40px;
|
||||
}
|
||||
#sidebar .list-group a.current {
|
||||
background:#f5f5f5;
|
||||
}
|
||||
@media (max-width: 1620px){
|
||||
#sidebar {
|
||||
margin:0;
|
||||
}
|
||||
#accordion {
|
||||
padding-left:235px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px){
|
||||
#sidebar {
|
||||
display: none;
|
||||
}
|
||||
#accordion {
|
||||
padding-left:0px;
|
||||
}
|
||||
}
|
||||
.label-primary {
|
||||
background-color: #248aff;
|
||||
}
|
||||
.docs-list .panel .panel-body .table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
</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="./" 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.example.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">
|
||||
<!-- menu -->
|
||||
<div id="sidebar">
|
||||
<div class="list-group panel">
|
||||
{foreach name="docsList" id="docs"}
|
||||
<a href="#{$key|md5|substr=0,8}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a>
|
||||
<div class="child collapse" id="{$key|md5|substr=0,8}">
|
||||
{foreach name="docs" id="api" }
|
||||
<a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title}
|
||||
<span class="tag">
|
||||
{if $api.needRight}
|
||||
<span class="label label-danger pull-right">鉴</span>
|
||||
{/if}
|
||||
{if $api.needLogin}
|
||||
<span class="label label-success pull-right noneedlogin">登</span>
|
||||
{/if}
|
||||
</span>
|
||||
</a>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-group docs-list" 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" id="heading-{$api.id}">
|
||||
<h4 class="panel-title">
|
||||
<span class="label {$api.methodLabel}">{$api.method|strtoupper}</span>
|
||||
<a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></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.Authorization}</strong></div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{$lang.NeedLogin}</td>
|
||||
<td>{$api.needLogin?'是':'否'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{$lang.NeedRight}</td>
|
||||
<td>{$api.needRight?'是':'否'}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</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.inputtype|default='text'}" 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 class="pull-right">
|
||||
<a href="javascript:" class="btn btn-xs btn-info btn-append">追加</a>
|
||||
</div>
|
||||
</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.inputtype|default='text'}" 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 form-group-submit">
|
||||
<button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button>
|
||||
<button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</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">
|
||||
|
||||
</div>
|
||||
<div class="col-md-6" align="right">
|
||||
Generated on {:date('Y-m-d H:i:s')} <a href="./" target="_blank">{$config.sitename}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js"></script>
|
||||
|
||||
<!-- Bootstrap Core JavaScript -->
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/twitter-bootstrap/3.4.1/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) {
|
||||
storage.getItem('token') && $('#token').val(storage.getItem('token'));
|
||||
storage.getItem('apiUrl') && $('#apiUrl').val(storage.getItem('apiUrl'));
|
||||
}
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip({
|
||||
placement: 'bottom'
|
||||
});
|
||||
|
||||
$(window).on("resize", function(){
|
||||
$("#sidebar").css("max-height", $(window).height()-80);
|
||||
});
|
||||
|
||||
$(window).trigger("resize");
|
||||
|
||||
$(document).on("click", "#sidebar .list-group > .list-group-item", function(){
|
||||
$("#sidebar .list-group > .list-group-item").removeClass("current");
|
||||
$(this).addClass("current");
|
||||
});
|
||||
$(document).on("click", "#sidebar .child a", function(){
|
||||
var heading = $("#heading-"+$(this).data("id"));
|
||||
if(!heading.next().hasClass("in")){
|
||||
$("a", heading).trigger("click");
|
||||
}
|
||||
$("html,body").animate({scrollTop:heading.offset().top-70});
|
||||
});
|
||||
|
||||
$('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>');
|
||||
});
|
||||
|
||||
$(document).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');
|
||||
}
|
||||
});
|
||||
$(document).on('click', '.btn-append', function (e) {
|
||||
$($("#appendtpl").html()).insertBefore($(this).closest(".panel").find(".form-group-submit"));
|
||||
return false;
|
||||
});
|
||||
$(document).on('click', '.btn-remove', function (e) {
|
||||
$(this).closest(".form-group").remove();
|
||||
return false;
|
||||
});
|
||||
$(document).on('keyup', '.input-custom-name', function (e) {
|
||||
$(this).closest(".row").find(".input-custom-value").attr("name", $(this).val());
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).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 method = $(form).prop('method').toLowerCase() || 'get';
|
||||
|
||||
var formData = new FormData();
|
||||
|
||||
$(form).find('input').each(function (i, input) {
|
||||
if ($(input).attr('type').toLowerCase() == 'file') {
|
||||
formData.append($(input).attr('name'), $(input)[0].files[0]);
|
||||
method = 'post';
|
||||
} else {
|
||||
formData.append($(input).attr('name'), $(input).val())
|
||||
}
|
||||
});
|
||||
|
||||
var index, key, value;
|
||||
|
||||
if (matchedParamsInRoute) {
|
||||
var params = {};
|
||||
formData.forEach(function(value, key){
|
||||
params[key] = value;
|
||||
});
|
||||
for (index = 0; index < matchedParamsInRoute.length; ++index) {
|
||||
try {
|
||||
key = matchedParamsInRoute[index];
|
||||
value = params[key];
|
||||
if (typeof value == "undefined")
|
||||
value = "";
|
||||
url = url.replace("\{" + key + "\}", value);
|
||||
formData.delete(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: method == 'get' ? $(form).serialize() : formData,
|
||||
type: method,
|
||||
dataType: 'json',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
headers: headers,
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
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>
|
||||
<script type="text/html" id="appendtpl">
|
||||
<div class="form-group">
|
||||
<label class="control-label">自定义</label>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<input type="text" class="form-control input-sm input-custom-name" placeholder="名称">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="form-control input-sm input-custom-value" placeholder="值">
|
||||
</div>
|
||||
<div class="col-xs-2 text-center">
|
||||
<a href="javascript:" class="btn btn-sm btn-danger btn-remove">删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1795
application/admin/command/Crud.php
Normal file
1795
application/admin/command/Crud.php
Normal file
File diff suppressed because it is too large
Load Diff
10
application/admin/command/Crud/stubs/add.stub
Normal file
10
application/admin/command/Crud/stubs/add.stub
Normal file
@@ -0,0 +1,10 @@
|
||||
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
|
||||
|
||||
{%addList%}
|
||||
<div class="form-group layer-footer">
|
||||
<label class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
37
application/admin/command/Crud/stubs/controller.stub
Normal file
37
application/admin/command/Crud/stubs/controller.stub
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace {%controllerNamespace%};
|
||||
|
||||
use app\common\controller\Backend;
|
||||
|
||||
/**
|
||||
* {%tableComment%}
|
||||
*
|
||||
* @icon {%iconName%}
|
||||
*/
|
||||
class {%controllerName%} extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* {%modelName%}模型对象
|
||||
* @var \{%modelNamespace%}\{%modelName%}
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->model = new \{%modelNamespace%}\{%modelName%};
|
||||
{%controllerAssignList%}
|
||||
}
|
||||
|
||||
{%controllerImport%}
|
||||
|
||||
/**
|
||||
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||
*/
|
||||
|
||||
{%controllerIndex%}
|
||||
}
|
||||
34
application/admin/command/Crud/stubs/controllerindex.stub
Normal file
34
application/admin/command/Crud/stubs/controllerindex.stub
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//当前是否为关联查询
|
||||
$this->relationSearch = {%relationSearch%};
|
||||
//设置过滤方法
|
||||
$this->request->filter(['strip_tags', 'trim']);
|
||||
if ($this->request->isAjax()) {
|
||||
//如果发送的来源是Selectpage,则转发到Selectpage
|
||||
if ($this->request->request('keyField')) {
|
||||
return $this->selectpage();
|
||||
}
|
||||
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
||||
|
||||
$list = $this->model
|
||||
{%relationWithList%}
|
||||
->where($where)
|
||||
->order($sort, $order)
|
||||
->paginate($limit);
|
||||
|
||||
foreach ($list as $row) {
|
||||
{%visibleFieldList%}
|
||||
{%relationVisibleFieldList%}
|
||||
}
|
||||
|
||||
$result = array("total" => $list->total(), "rows" => $list->items());
|
||||
|
||||
return json($result);
|
||||
}
|
||||
return $this->view->fetch();
|
||||
}
|
||||
10
application/admin/command/Crud/stubs/edit.stub
Normal file
10
application/admin/command/Crud/stubs/edit.stub
Normal file
@@ -0,0 +1,10 @@
|
||||
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
|
||||
|
||||
{%editList%}
|
||||
<div class="form-group layer-footer">
|
||||
<label class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
6
application/admin/command/Crud/stubs/html/checkbox.stub
Normal file
6
application/admin/command/Crud/stubs/html/checkbox.stub
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
<div class="checkbox">
|
||||
{foreach name="{%fieldList%}" item="vo"}
|
||||
<label for="{%fieldName%}-{$key}"><input id="{%fieldName%}-{$key}" name="{%fieldName%}" type="checkbox" value="{$key}" {in name="key" value="{%selectedValue%}"}checked{/in} /> {$vo}</label>
|
||||
{/foreach}
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
<dl class="list-unstyled fieldlist" data-name="{%fieldName%}" data-template="{%fieldName%}tpl">
|
||||
<dd>
|
||||
<ins>{:__('{%itemValue%}')}</ins>
|
||||
</dd>
|
||||
<dd>
|
||||
<ins><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></ins>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<textarea name="{%fieldName%}" class="form-control hide" cols="30" rows="5">{%fieldValue%}</textarea>
|
||||
<script id="{%fieldName%}tpl" type="text/html">
|
||||
<dd class="form-inline">
|
||||
<ins><input type="text" name="<%=name%>[<%=index%>][value]" class="form-control" size="15" value="<%=row%>"/></ins>
|
||||
<ins>
|
||||
<span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
|
||||
<span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
|
||||
</ins>
|
||||
</dd>
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
<table class="table fieldlist" data-name="{%fieldName%}" data-template="{%fieldName%}tpl">
|
||||
<tr>
|
||||
{%theadList%}
|
||||
<td width="90">{:__('Operate')}</td>
|
||||
</tr>
|
||||
<tr><td colspan="{%colspan%}">
|
||||
<a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a>
|
||||
<textarea name="{%fieldName%}" class="form-control hide" cols="30" rows="5">{%fieldValue%}</textarea>
|
||||
</td></tr>
|
||||
</table>
|
||||
<script type="text/html" id="{%fieldName%}tpl">
|
||||
<tr>
|
||||
{%tbodyList%}
|
||||
<td width="90">
|
||||
<span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span>
|
||||
<span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
10
application/admin/command/Crud/stubs/html/fieldlist.stub
Normal file
10
application/admin/command/Crud/stubs/html/fieldlist.stub
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<dl class="fieldlist" data-name="{%fieldName%}">
|
||||
<dd>
|
||||
<ins>{:__('{%itemKey%}')}</ins>
|
||||
<ins>{:__('{%itemValue%}')}</ins>
|
||||
</dd>
|
||||
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
|
||||
<textarea name="{%fieldName%}" class="form-control hide" cols="30" rows="5">{%fieldValue%}</textarea>
|
||||
</dl>
|
||||
|
||||
10
application/admin/command/Crud/stubs/html/heading-html.stub
Normal file
10
application/admin/command/Crud/stubs/html/heading-html.stub
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<div class="panel-heading">
|
||||
{:build_heading(null,FALSE)}
|
||||
<ul class="nav nav-tabs" data-field="{%field%}">
|
||||
<li class="{:$Think.get.{%field%} === null ? 'active' : ''}"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
|
||||
{foreach name="{%fieldName%}List" item="vo"}
|
||||
<li class="{:$Think.get.{%field%} === (string)$key ? 'active' : ''}"><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="dropdown btn-group {:$auth->check('{%controllerUrl%}/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">
|
||||
{foreach name="{%fieldName%}List" item="vo"}
|
||||
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:" data-params="{%field%}={$key}">{:__('Set {%field%} to ' . $key)}</a></li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</div>
|
||||
6
application/admin/command/Crud/stubs/html/radio.stub
Normal file
6
application/admin/command/Crud/stubs/html/radio.stub
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
<div class="radio">
|
||||
{foreach name="{%fieldList%}" item="vo"}
|
||||
<label for="{%fieldName%}-{$key}"><input id="{%fieldName%}-{$key}" name="{%fieldName%}" type="radio" value="{$key}" {in name="key" value="{%selectedValue%}"}checked{/in} /> {$vo}</label>
|
||||
{/foreach}
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('{%controllerUrl%}/recyclebin')?'':'hide'}" href="{%controllerUrl%}/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>
|
||||
6
application/admin/command/Crud/stubs/html/select.stub
Normal file
6
application/admin/command/Crud/stubs/html/select.stub
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
<select {%attrStr%}>
|
||||
{foreach name="{%fieldList%}" item="vo"}
|
||||
<option value="{$key}" {in name="key" value="{%selectedValue%}"}selected{/in}>{$vo}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
5
application/admin/command/Crud/stubs/html/switch.stub
Normal file
5
application/admin/command/Crud/stubs/html/switch.stub
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
<input {%attrStr%} name="{%fieldName%}" type="hidden" value="{%fieldValue%}">
|
||||
<a href="javascript:;" data-toggle="switcher" class="btn-switcher" data-input-id="c-{%field%}" data-yes="{%fieldYes%}" data-no="{%fieldNo%}" >
|
||||
<i class="fa fa-toggle-on text-success {%fieldSwitchClass%} fa-2x"></i>
|
||||
</a>
|
||||
29
application/admin/command/Crud/stubs/index.stub
Normal file
29
application/admin/command/Crud/stubs/index.stub
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="panel panel-default panel-intro">
|
||||
{%headingHtml%}
|
||||
|
||||
<div class="panel-body">
|
||||
<div id="myTabContent" class="tab-content">
|
||||
<div class="tab-pane fade active in" id="one">
|
||||
<div class="widget-body no-padding">
|
||||
<div id="toolbar" class="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('{%controllerUrl%}/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('{%controllerUrl%}/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('{%controllerUrl%}/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
|
||||
{%importHtml%}
|
||||
|
||||
{%multipleHtml%}
|
||||
|
||||
{%recyclebinHtml%}
|
||||
</div>
|
||||
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
|
||||
data-operate-edit="{:$auth->check('{%controllerUrl%}/edit')}"
|
||||
data-operate-del="{:$auth->check('{%controllerUrl%}/del')}"
|
||||
width="100%">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
48
application/admin/command/Crud/stubs/javascript.stub
Normal file
48
application/admin/command/Crud/stubs/javascript.stub
Normal file
@@ -0,0 +1,48 @@
|
||||
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
|
||||
|
||||
var Controller = {
|
||||
index: function () {
|
||||
// 初始化表格参数配置
|
||||
Table.api.init({
|
||||
extend: {
|
||||
index_url: '{%controllerUrl%}/index' + location.search,
|
||||
add_url: '{%controllerUrl%}/add',
|
||||
edit_url: '{%controllerUrl%}/edit',
|
||||
del_url: '{%controllerUrl%}/del',
|
||||
multi_url: '{%controllerUrl%}/multi',
|
||||
import_url: '{%controllerUrl%}/import',
|
||||
table: '{%table%}',
|
||||
}
|
||||
});
|
||||
|
||||
var table = $("#table");
|
||||
|
||||
// 初始化表格
|
||||
table.bootstrapTable({
|
||||
url: $.fn.bootstrapTable.defaults.extend.index_url,
|
||||
pk: '{%pk%}',
|
||||
sortName: '{%order%}',{%fixedColumnsJs%}
|
||||
columns: [
|
||||
[
|
||||
{%javascriptList%}
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
// 为表格绑定事件
|
||||
Table.api.bindevent(table);
|
||||
},{%recyclebinJs%}
|
||||
add: function () {
|
||||
Controller.api.bindevent();
|
||||
},
|
||||
edit: function () {
|
||||
Controller.api.bindevent();
|
||||
},
|
||||
api: {
|
||||
bindevent: function () {
|
||||
Form.api.bindevent($("form[role=form]"));
|
||||
}
|
||||
}
|
||||
};
|
||||
return Controller;
|
||||
});
|
||||
5
application/admin/command/Crud/stubs/lang.stub
Normal file
5
application/admin/command/Crud/stubs/lang.stub
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
{%langList%}
|
||||
];
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
public function {%methodName%}($value, $data)
|
||||
{
|
||||
$value = $value ?: ($data['{%field%}'] ?? '');
|
||||
$valueArr = explode(',', $value);
|
||||
$list = $this->{%listMethodName%}();
|
||||
return implode(',', array_intersect_key($list, array_flip($valueArr)));
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
public function {%methodName%}($value, $data)
|
||||
{
|
||||
$value = $value ?: ($data['{%field%}'] ?? '');
|
||||
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
|
||||
}
|
||||
1
application/admin/command/Crud/stubs/mixins/enum.stub
Normal file
1
application/admin/command/Crud/stubs/mixins/enum.stub
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
4
application/admin/command/Crud/stubs/mixins/import.stub
Normal file
4
application/admin/command/Crud/stubs/mixins/import.stub
Normal file
@@ -0,0 +1,4 @@
|
||||
public function import()
|
||||
{
|
||||
parent::import();
|
||||
}
|
||||
10
application/admin/command/Crud/stubs/mixins/modelinit.stub
Normal file
10
application/admin/command/Crud/stubs/mixins/modelinit.stub
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
protected static function init()
|
||||
{
|
||||
self::afterInsert(function ($row) {
|
||||
if (!$row['{%order%}']) {
|
||||
$pk = $row->getPk();
|
||||
$row->getQuery()->where($pk, $row[$pk])->update(['{%order%}' => $row[$pk]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
public function {%relationMethod%}s()
|
||||
{
|
||||
return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}');
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
public function {%relationMethod%}()
|
||||
{
|
||||
return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
public function {%methodName%}($value, $data)
|
||||
{
|
||||
$value = $value ?: ($data['{%field%}'] ?? '');
|
||||
$valueArr = explode(',', $value);
|
||||
$list = $this->{%listMethodName%}();
|
||||
return implode(',', array_intersect_key($list, array_flip($valueArr)));
|
||||
}
|
||||
7
application/admin/command/Crud/stubs/mixins/radio.stub
Normal file
7
application/admin/command/Crud/stubs/mixins/radio.stub
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
public function {%methodName%}($value, $data)
|
||||
{
|
||||
$value = $value ?: ($data['{%field%}'] ?? '');
|
||||
$list = $this->{%listMethodName%}();
|
||||
return $list[$value] ?? '';
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
recyclebin: function () {
|
||||
// 初始化表格参数配置
|
||||
Table.api.init({
|
||||
extend: {
|
||||
'dragsort_url': ''
|
||||
}
|
||||
});
|
||||
|
||||
var table = $("#table");
|
||||
|
||||
// 初始化表格
|
||||
table.bootstrapTable({
|
||||
url: '{%controllerUrl%}/recyclebin' + location.search,
|
||||
pk: 'id',
|
||||
sortName: 'id',
|
||||
columns: [
|
||||
[
|
||||
{checkbox: true},
|
||||
{field: 'id', title: __('Id')},{%recyclebinTitleJs%}
|
||||
{
|
||||
field: '{%deleteTimeField%}',
|
||||
title: __('Deletetime'),
|
||||
operate: 'RANGE',
|
||||
addclass: 'datetimerange',
|
||||
formatter: Table.api.formatter.datetime
|
||||
},
|
||||
{
|
||||
field: 'operate',
|
||||
width: '140px',
|
||||
title: __('Operate'),
|
||||
table: table,
|
||||
events: Table.api.events.operate,
|
||||
buttons: [
|
||||
{
|
||||
name: 'Restore',
|
||||
text: __('Restore'),
|
||||
classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
|
||||
icon: 'fa fa-rotate-left',
|
||||
url: '{%controllerUrl%}/restore',
|
||||
refresh: true
|
||||
},
|
||||
{
|
||||
name: 'Destroy',
|
||||
text: __('Destroy'),
|
||||
classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
|
||||
icon: 'fa fa-times',
|
||||
url: '{%controllerUrl%}/destroy',
|
||||
refresh: true
|
||||
}
|
||||
],
|
||||
formatter: Table.api.formatter.operate
|
||||
}
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
// 为表格绑定事件
|
||||
Table.api.bindevent(table);
|
||||
},
|
||||
7
application/admin/command/Crud/stubs/mixins/select.stub
Normal file
7
application/admin/command/Crud/stubs/mixins/select.stub
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
public function {%methodName%}($value, $data)
|
||||
{
|
||||
$value = $value ?: ($data['{%field%}'] ?? '');
|
||||
$list = $this->{%listMethodName%}();
|
||||
return $list[$value] ?? '';
|
||||
}
|
||||
40
application/admin/command/Crud/stubs/model.stub
Normal file
40
application/admin/command/Crud/stubs/model.stub
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace {%modelNamespace%};
|
||||
|
||||
use think\Model;
|
||||
{%softDeleteClassPath%}
|
||||
|
||||
class {%modelName%} extends Model
|
||||
{
|
||||
|
||||
{%softDelete%}
|
||||
|
||||
{%modelConnection%}
|
||||
|
||||
// 表名
|
||||
protected ${%modelTableType%} = '{%modelTableTypeName%}';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%};
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = {%createTime%};
|
||||
protected $updateTime = {%updateTime%};
|
||||
protected $deleteTime = {%deleteTime%};
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
{%appendAttrList%}
|
||||
];
|
||||
|
||||
{%modelInit%}
|
||||
|
||||
{%getEnumList%}
|
||||
|
||||
{%getAttrList%}
|
||||
|
||||
{%setAttrList%}
|
||||
|
||||
{%relationMethodList%}
|
||||
}
|
||||
25
application/admin/command/Crud/stubs/recyclebin.stub
Normal file
25
application/admin/command/Crud/stubs/recyclebin.stub
Normal file
@@ -0,0 +1,25 @@
|
||||
<div class="panel panel-default panel-intro">
|
||||
{:build_heading()}
|
||||
|
||||
<div class="panel-body">
|
||||
<div id="myTabContent" class="tab-content">
|
||||
<div class="tab-pane fade active in" id="one">
|
||||
<div class="widget-body no-padding">
|
||||
<div id="toolbar" class="toolbar">
|
||||
{:build_toolbar('refresh')}
|
||||
<a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
|
||||
<a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
|
||||
<a class="btn btn-success btn-restoreall {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
|
||||
<a class="btn btn-danger btn-destroyall {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
|
||||
</div>
|
||||
<table id="table" class="table table-striped table-bordered table-hover"
|
||||
data-operate-restore="{:$auth->check('{%controllerUrl%}/restore')}"
|
||||
data-operate-destroy="{:$auth->check('{%controllerUrl%}/destroy')}"
|
||||
width="100%">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
12
application/admin/command/Crud/stubs/relationmodel.stub
Normal file
12
application/admin/command/Crud/stubs/relationmodel.stub
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace {%modelNamespace%};
|
||||
|
||||
use think\Model;
|
||||
|
||||
class {%relationName%} extends Model
|
||||
{
|
||||
// 表名
|
||||
protected ${%relationTableType%} = '{%relationTableTypeName%}';
|
||||
|
||||
}
|
||||
27
application/admin/command/Crud/stubs/validate.stub
Normal file
27
application/admin/command/Crud/stubs/validate.stub
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace {%validateNamespace%};
|
||||
|
||||
use think\Validate;
|
||||
|
||||
class {%validateName%} extends Validate
|
||||
{
|
||||
/**
|
||||
* 验证规则
|
||||
*/
|
||||
protected $rule = [
|
||||
];
|
||||
/**
|
||||
* 提示消息
|
||||
*/
|
||||
protected $message = [
|
||||
];
|
||||
/**
|
||||
* 验证场景
|
||||
*/
|
||||
protected $scene = [
|
||||
'add' => [],
|
||||
'edit' => [],
|
||||
];
|
||||
|
||||
}
|
||||
341
application/admin/command/Install.php
Normal file
341
application/admin/command/Install.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use fast\Random;
|
||||
use PDO;
|
||||
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\Lang;
|
||||
use think\Request;
|
||||
use think\View;
|
||||
|
||||
class Install extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* 最低PHP版本
|
||||
* @var string
|
||||
*/
|
||||
protected $minPhpVersion = '7.4.0';
|
||||
|
||||
protected $model = null;
|
||||
/**
|
||||
* @var \think\View 视图类实例
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var \think\Request Request 实例
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$config = Config::get('database');
|
||||
$this
|
||||
->setName('install')
|
||||
->addOption('hostname', 'a', Option::VALUE_OPTIONAL, 'mysql hostname', $config['hostname'])
|
||||
->addOption('hostport', 'o', Option::VALUE_OPTIONAL, 'mysql hostport', $config['hostport'])
|
||||
->addOption('database', 'd', Option::VALUE_OPTIONAL, 'mysql database', $config['database'])
|
||||
->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix'])
|
||||
->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username'])
|
||||
->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password'])
|
||||
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', false)
|
||||
->setDescription('New installation of FastAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 命令行安装
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
|
||||
// 覆盖安装
|
||||
$force = $input->getOption('force');
|
||||
$hostname = $input->getOption('hostname');
|
||||
$hostport = $input->getOption('hostport');
|
||||
$database = $input->getOption('database');
|
||||
$prefix = $input->getOption('prefix');
|
||||
$username = $input->getOption('username');
|
||||
$password = $input->getOption('password');
|
||||
|
||||
$installLockFile = INSTALL_PATH . "install.lock";
|
||||
if (is_file($installLockFile) && !$force) {
|
||||
throw new Exception("\nFastAdmin already installed!\nIf you need to reinstall again, use the parameter --force=true ");
|
||||
}
|
||||
|
||||
$adminUsername = 'admin';
|
||||
$adminPassword = Random::alnum(10);
|
||||
$adminEmail = 'admin@admin.com';
|
||||
$siteName = __('My Website');
|
||||
|
||||
$adminName = $this->installation($hostname, $hostport, $database, $username, $password, $prefix, $adminUsername, $adminPassword, $adminEmail, $siteName);
|
||||
if ($adminName) {
|
||||
$output->highlight("Admin url:https://www.example.com/{$adminName}");
|
||||
}
|
||||
|
||||
$output->highlight("Admin username:{$adminUsername}");
|
||||
$output->highlight("Admin password:{$adminPassword}");
|
||||
|
||||
\think\Cache::rm('__menu__');
|
||||
|
||||
$output->info("Install Successed!");
|
||||
}
|
||||
|
||||
/**
|
||||
* PC端安装
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->view = View::instance(array_merge(Config::get('template'), ['tpl_cache' => false]));
|
||||
$this->request = Request::instance();
|
||||
|
||||
define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
|
||||
|
||||
$lang = $this->request->langset();
|
||||
$lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn';
|
||||
|
||||
if (!$lang || in_array($lang, ['zh-cn', 'zh-hans-cn'])) {
|
||||
Lang::load(INSTALL_PATH . 'zh-cn.php');
|
||||
}
|
||||
|
||||
$installLockFile = INSTALL_PATH . "install.lock";
|
||||
|
||||
if (is_file($installLockFile)) {
|
||||
echo __('The system has been installed. If you need to reinstall, please remove %s first', 'install.lock');
|
||||
exit;
|
||||
}
|
||||
$output = function ($code, $msg, $url = null, $data = null) {
|
||||
return json(['code' => $code, 'msg' => $msg, 'url' => $url, 'data' => $data]);
|
||||
};
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$mysqlHostname = $this->request->post('mysqlHostname', '127.0.0.1');
|
||||
$mysqlHostport = $this->request->post('mysqlHostport', '3306');
|
||||
$hostArr = explode(':', $mysqlHostname);
|
||||
if (count($hostArr) > 1) {
|
||||
$mysqlHostname = $hostArr[0];
|
||||
$mysqlHostport = $hostArr[1];
|
||||
}
|
||||
$mysqlUsername = $this->request->post('mysqlUsername', 'root');
|
||||
$mysqlPassword = $this->request->post('mysqlPassword', '');
|
||||
$mysqlDatabase = $this->request->post('mysqlDatabase', '');
|
||||
$mysqlPrefix = $this->request->post('mysqlPrefix', 'fa_');
|
||||
$adminUsername = $this->request->post('adminUsername', 'admin');
|
||||
$adminPassword = $this->request->post('adminPassword', '');
|
||||
$adminPasswordConfirmation = $this->request->post('adminPasswordConfirmation', '');
|
||||
$adminEmail = $this->request->post('adminEmail', 'admin@admin.com');
|
||||
$siteName = $this->request->post('siteName', __('My Website'));
|
||||
|
||||
if ($adminPassword !== $adminPasswordConfirmation) {
|
||||
return $output(0, __('The two passwords you entered did not match'));
|
||||
}
|
||||
|
||||
$adminName = '';
|
||||
try {
|
||||
$adminName = $this->installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail, $siteName);
|
||||
} catch (\PDOException $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
} catch (\Exception $e) {
|
||||
return $output(0, $e->getMessage());
|
||||
}
|
||||
return $output(1, __('Install Successed'), null, ['adminName' => $adminName]);
|
||||
}
|
||||
$errInfo = '';
|
||||
try {
|
||||
$this->checkenv();
|
||||
} catch (\Exception $e) {
|
||||
$errInfo = $e->getMessage();
|
||||
}
|
||||
return $this->view->fetch(INSTALL_PATH . "install.html", ['errInfo' => $errInfo]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行安装
|
||||
*/
|
||||
protected function installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail = null, $siteName = null)
|
||||
{
|
||||
$this->checkenv();
|
||||
|
||||
if ($mysqlDatabase == '') {
|
||||
throw new Exception(__('Please input correct database'));
|
||||
}
|
||||
if (!preg_match("/^\w{3,12}$/", $adminUsername)) {
|
||||
throw new Exception(__('Please input correct username'));
|
||||
}
|
||||
if (!preg_match("/^[\S]{6,16}$/", $adminPassword)) {
|
||||
throw new Exception(__('Please input correct password'));
|
||||
}
|
||||
$weakPasswordArr = ['123456', '12345678', '123456789', '654321', '111111', '000000', 'password', 'qwerty', 'abc123', '1qaz2wsx'];
|
||||
if (in_array($adminPassword, $weakPasswordArr)) {
|
||||
throw new Exception(__('Password is too weak'));
|
||||
}
|
||||
if ($siteName == '' || preg_match("/fast" . "admin/i", $siteName)) {
|
||||
throw new Exception(__('Please input correct website'));
|
||||
}
|
||||
|
||||
$sql = file_get_contents(INSTALL_PATH . 'fastadmin.sql');
|
||||
|
||||
$sql = str_replace("`fa_", "`{$mysqlPrefix}", $sql);
|
||||
|
||||
// 先尝试能否自动创建数据库
|
||||
$config = Config::get('database');
|
||||
try {
|
||||
$pdo = new PDO("{$config['type']}:host={$mysqlHostname}" . ($mysqlHostport ? ";port={$mysqlHostport}" : ''), $mysqlUsername, $mysqlPassword);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->query("CREATE DATABASE IF NOT EXISTS `{$mysqlDatabase}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;");
|
||||
|
||||
// 连接install命令中指定的数据库
|
||||
$instance = Db::connect([
|
||||
'type' => "{$config['type']}",
|
||||
'hostname' => "{$mysqlHostname}",
|
||||
'hostport' => "{$mysqlHostport}",
|
||||
'database' => "{$mysqlDatabase}",
|
||||
'username' => "{$mysqlUsername}",
|
||||
'password' => "{$mysqlPassword}",
|
||||
'prefix' => "{$mysqlPrefix}",
|
||||
]);
|
||||
|
||||
// 查询一次SQL,判断连接是否正常
|
||||
$instance->execute("SELECT 1");
|
||||
|
||||
// 调用原生PDO对象进行批量查询
|
||||
$instance->getPdo()->exec($sql);
|
||||
} catch (\PDOException $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
// 后台入口文件
|
||||
$adminFile = ROOT_PATH . 'public' . DS . 'admin.php';
|
||||
|
||||
// 数据库配置文件
|
||||
$envSampleFile = ROOT_PATH . '.env.sample';
|
||||
$envFile = ROOT_PATH . '.env';
|
||||
if (!file_exists($envFile)) {
|
||||
if (!copy($envSampleFile, $envFile)) {
|
||||
throw new Exception(__('Failed to copy %s to %s', '.env.sample', '.env'));
|
||||
}
|
||||
}
|
||||
|
||||
$envText = @file_get_contents($envFile);
|
||||
|
||||
$callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) {
|
||||
$field = "mysql" . ucfirst($matches[1]);
|
||||
$replace = $$field;
|
||||
return "{$matches[1]} = {$replace}";
|
||||
};
|
||||
$envText = preg_replace_callback("/(hostname|database|username|password|hostport|prefix)\s*=\s*(.*)/", $callback, $envText);
|
||||
|
||||
// 检测能否成功写入数据库配置
|
||||
$result = @file_put_contents($envFile, $envText);
|
||||
if (!$result) {
|
||||
throw new Exception(__('The current permissions are insufficient to write the file %s', '.env'));
|
||||
}
|
||||
|
||||
// 设置新的Token随机密钥key
|
||||
$oldTokenKey = config('token.key');
|
||||
$newTokenKey = \fast\Random::alnum(32);
|
||||
$coreConfigFile = CONF_PATH . 'config.php';
|
||||
$coreConfigText = @file_get_contents($coreConfigFile);
|
||||
$coreConfigText = preg_replace("/'key'(\s+)=>(\s+)'{$oldTokenKey}'/", "'key'\$1=>\$2'{$newTokenKey}'", $coreConfigText);
|
||||
|
||||
$result = @file_put_contents($coreConfigFile, $coreConfigText);
|
||||
if (!$result) {
|
||||
throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/config.php'));
|
||||
}
|
||||
|
||||
$avatar = '/assets/img/avatar.png';
|
||||
// 变更默认管理员密码
|
||||
$adminPassword = $adminPassword ? $adminPassword : Random::alnum(8);
|
||||
$adminEmail = $adminEmail ? $adminEmail : "admin@admin.com";
|
||||
$newSalt = substr(md5(uniqid(true)), 0, 6);
|
||||
$newPassword = md5(md5($adminPassword) . $newSalt);
|
||||
$data = ['username' => $adminUsername, 'email' => $adminEmail, 'avatar' => $avatar, 'password' => $newPassword, 'salt' => $newSalt];
|
||||
$instance->name('admin')->where('username', 'admin')->update($data);
|
||||
|
||||
// 变更前台默认用户的密码,随机生成
|
||||
$newSalt = substr(md5(uniqid(true)), 0, 6);
|
||||
$newPassword = md5(md5(Random::alnum(8)) . $newSalt);
|
||||
$instance->name('user')->where('username', 'admin')->update(['avatar' => $avatar, 'password' => $newPassword, 'salt' => $newSalt]);
|
||||
|
||||
// 修改后台入口
|
||||
$adminName = '';
|
||||
if (is_file($adminFile)) {
|
||||
$adminName = Random::alpha(10) . '.php';
|
||||
rename($adminFile, ROOT_PATH . 'public' . DS . $adminName);
|
||||
}
|
||||
|
||||
//修改站点名称
|
||||
if ($siteName != config('site.name')) {
|
||||
$instance->name('config')->where('name', 'name')->update(['value' => $siteName]);
|
||||
$siteConfigFile = CONF_PATH . 'extra' . DS . 'site.php';
|
||||
$siteConfig = include $siteConfigFile;
|
||||
$configList = $instance->name("config")->select();
|
||||
foreach ($configList as $k => $value) {
|
||||
if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) {
|
||||
$value['value'] = is_array($value['value']) ? $value['value'] : explode(',', $value['value']);
|
||||
}
|
||||
if ($value['type'] == 'array') {
|
||||
$value['value'] = (array)json_decode($value['value'], true);
|
||||
}
|
||||
$siteConfig[$value['name']] = $value['value'];
|
||||
}
|
||||
$siteConfig['name'] = $siteName;
|
||||
file_put_contents($siteConfigFile, '<?php' . "\n\nreturn " . var_export_short($siteConfig) . ";\n");
|
||||
}
|
||||
|
||||
$installLockFile = INSTALL_PATH . "install.lock";
|
||||
//检测能否成功写入lock文件
|
||||
$result = @file_put_contents($installLockFile, 1);
|
||||
if (!$result) {
|
||||
throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/admin/command/Install/install.lock'));
|
||||
}
|
||||
|
||||
try {
|
||||
//删除安装脚本
|
||||
@unlink(ROOT_PATH . 'public' . DS . 'install.php');
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
return $adminName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测环境
|
||||
*/
|
||||
protected function checkenv()
|
||||
{
|
||||
// 检测目录是否存在
|
||||
$checkDirs = [
|
||||
'thinkphp',
|
||||
'vendor',
|
||||
'public' . DS . 'assets' . DS . 'libs'
|
||||
];
|
||||
|
||||
//数据库配置文件
|
||||
$dbConfigFile = APP_PATH . 'database.php';
|
||||
|
||||
if (version_compare(PHP_VERSION, $this->minPhpVersion, '<')) {
|
||||
throw new Exception(__("The current PHP %s is too low, please use PHP %s or higher", PHP_VERSION, $this->minPhpVersion));
|
||||
}
|
||||
if (!extension_loaded("PDO")) {
|
||||
throw new Exception(__("PDO is not currently installed and cannot be installed"));
|
||||
}
|
||||
if (!is_really_writable($dbConfigFile)) {
|
||||
throw new Exception(__('The current permissions are insufficient to write the configuration file application/database.php'));
|
||||
}
|
||||
foreach ($checkDirs as $k => $v) {
|
||||
if (!is_dir(ROOT_PATH . $v)) {
|
||||
throw new Exception(__('Please go to the official website to download the full package or resource package and try to install'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
588
application/admin/command/Install/fastadmin.sql
Normal file
588
application/admin/command/Install/fastadmin.sql
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
FastAdmin Install SQL
|
||||
Date: 2024-09-03 15:05:25
|
||||
*/
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_admin
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_admin` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(20) DEFAULT '' COMMENT '用户名',
|
||||
`nickname` varchar(50) DEFAULT '' COMMENT '昵称',
|
||||
`password` varchar(32) DEFAULT '' COMMENT '密码',
|
||||
`salt` varchar(30) DEFAULT '' COMMENT '密码盐',
|
||||
`avatar` varchar(255) DEFAULT '' COMMENT '头像',
|
||||
`email` varchar(100) DEFAULT '' COMMENT '电子邮箱',
|
||||
`mobile` varchar(11) DEFAULT '' COMMENT '手机号码',
|
||||
`loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数',
|
||||
`logintime` bigint(16) DEFAULT NULL COMMENT '登录时间',
|
||||
`loginip` varchar(50) DEFAULT NULL COMMENT '登录IP',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`token` varchar(59) DEFAULT '' COMMENT 'Session标识',
|
||||
`status` varchar(30) NOT NULL DEFAULT 'normal' COMMENT '状态',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='管理员表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_admin
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_admin` VALUES (1, 'admin', 'Admin', '', '', '/assets/img/avatar.png', 'admin@example.com', '', 0, 1491635035, '127.0.0.1',1491635035, 1491635035, '', 'normal');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_admin_log
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_admin_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID',
|
||||
`username` varchar(30) DEFAULT '' COMMENT '管理员名字',
|
||||
`url` varchar(1500) DEFAULT '' COMMENT '操作页面',
|
||||
`title` varchar(100) DEFAULT '' COMMENT '日志标题',
|
||||
`content` longtext NOT NULL COMMENT '内容',
|
||||
`ip` varchar(50) DEFAULT '' COMMENT 'IP',
|
||||
`useragent` varchar(255) DEFAULT '' COMMENT 'User-Agent',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '操作时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name` (`username`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='管理员日志表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_area
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_area` (
|
||||
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`pid` int(10) DEFAULT NULL COMMENT '父id',
|
||||
`shortname` varchar(100) DEFAULT NULL COMMENT '简称',
|
||||
`name` varchar(100) DEFAULT NULL COMMENT '名称',
|
||||
`mergename` varchar(255) DEFAULT NULL COMMENT '全称',
|
||||
`level` tinyint(4) DEFAULT NULL COMMENT '层级:1=省,2=市,3=区/县',
|
||||
`pinyin` varchar(100) DEFAULT NULL COMMENT '拼音',
|
||||
`code` varchar(100) DEFAULT NULL COMMENT '长途区号',
|
||||
`zip` varchar(100) DEFAULT NULL COMMENT '邮编',
|
||||
`first` varchar(50) DEFAULT NULL COMMENT '首字母',
|
||||
`lng` varchar(100) DEFAULT NULL COMMENT '经度',
|
||||
`lat` varchar(100) DEFAULT NULL COMMENT '纬度',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `pid` (`pid`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='地区表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_attachment
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_attachment` (
|
||||
`id` int(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`category` varchar(50) DEFAULT '' COMMENT '类别',
|
||||
`admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID',
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID',
|
||||
`url` varchar(255) DEFAULT '' COMMENT '物理路径',
|
||||
`imagewidth` int(10) unsigned DEFAULT 0 COMMENT '宽度',
|
||||
`imageheight` int(10) unsigned DEFAULT 0 COMMENT '高度',
|
||||
`imagetype` varchar(30) DEFAULT '' COMMENT '图片类型',
|
||||
`imageframes` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '图片帧数',
|
||||
`filename` varchar(100) DEFAULT '' COMMENT '文件名称',
|
||||
`filesize` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '文件大小',
|
||||
`mimetype` varchar(100) DEFAULT '' COMMENT 'mime类型',
|
||||
`extparam` varchar(255) DEFAULT '' COMMENT '透传数据',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建日期',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`uploadtime` bigint(16) DEFAULT NULL COMMENT '上传时间',
|
||||
`storage` varchar(100) NOT NULL DEFAULT 'local' COMMENT '存储位置',
|
||||
`sha1` varchar(40) DEFAULT '' COMMENT '文件 sha1编码',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='附件表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_attachment
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_attachment` VALUES (1, '', 1, 0, '/assets/img/qrcode.png', '150', '150', 'png', 0, 'qrcode.png', 21859, 'image/png', '', 1491635035, 1491635035, 1491635035, 'local', '17163603d0263e4838b9387ff2cd4877e8b018f6');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_auth_group
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_auth_group` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父组别',
|
||||
`name` varchar(100) DEFAULT '' COMMENT '组名',
|
||||
`rules` text NOT NULL COMMENT '规则ID',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`status` varchar(30) DEFAULT '' COMMENT '状态',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='分组表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_auth_group
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_auth_group` VALUES (1, 0, 'Admin group', '*', 1491635035, 1491635035, 'normal');
|
||||
INSERT INTO `fa_auth_group` VALUES (2, 1, 'Second group', '13,14,16,15,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63,64,65,1,9,10,11,7,6,8,2,4,5', 1491635035, 1491635035, 'normal');
|
||||
INSERT INTO `fa_auth_group` VALUES (3, 2, 'Third group', '1,4,9,10,11,13,14,15,16,17,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63,64,65,5', 1491635035, 1491635035, 'normal');
|
||||
INSERT INTO `fa_auth_group` VALUES (4, 1, 'Second group 2', '1,4,13,14,15,16,17,55,56,57,58,59,60,61,62,63,64,65', 1491635035, 1491635035, 'normal');
|
||||
INSERT INTO `fa_auth_group` VALUES (5, 2, 'Third group 2', '1,2,6,7,8,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34', 1491635035, 1491635035, 'normal');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_auth_group_access
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_auth_group_access` (
|
||||
`uid` int(10) unsigned NOT NULL COMMENT '会员ID',
|
||||
`group_id` int(10) unsigned NOT NULL COMMENT '级别ID',
|
||||
UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
|
||||
KEY `uid` (`uid`),
|
||||
KEY `group_id` (`group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='权限分组表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_auth_group_access
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_auth_group_access` VALUES (1, 1);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_auth_rule
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_auth_rule` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`type` enum('menu','file') NOT NULL DEFAULT 'file' COMMENT 'menu为菜单,file为权限节点',
|
||||
`pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID',
|
||||
`name` varchar(100) DEFAULT '' COMMENT '规则名称',
|
||||
`title` varchar(50) DEFAULT '' COMMENT '规则名称',
|
||||
`icon` varchar(50) DEFAULT '' COMMENT '图标',
|
||||
`url` varchar(255) DEFAULT '' COMMENT '规则URL',
|
||||
`condition` varchar(255) DEFAULT '' COMMENT '条件',
|
||||
`remark` varchar(255) DEFAULT '' COMMENT '备注',
|
||||
`ismenu` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否为菜单',
|
||||
`menutype` enum('addtabs','blank','dialog','ajax') DEFAULT NULL COMMENT '菜单类型',
|
||||
`extend` varchar(255) DEFAULT '' COMMENT '扩展属性',
|
||||
`py` varchar(30) DEFAULT '' COMMENT '拼音首字母',
|
||||
`pinyin` varchar(100) DEFAULT '' COMMENT '拼音',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
|
||||
`status` varchar(30) DEFAULT '' COMMENT '状态',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`) USING BTREE,
|
||||
KEY `pid` (`pid`),
|
||||
KEY `weigh` (`weigh`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='节点表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_auth_rule
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_auth_rule` VALUES (1, 'file', 0, 'dashboard', 'Dashboard', 'fa fa-dashboard', '', '', 'Dashboard tips', 1, NULL, '', 'kzt', 'kongzhitai', 1491635035, 1491635035, 143, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (2, 'file', 0, 'general', 'General', 'fa fa-cogs', '', '', '', 1, NULL, '', 'cggl', 'changguiguanli', 1491635035, 1491635035, 137, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (3, 'file', 0, 'category', 'Category', 'fa fa-leaf', '', '', 'Category tips', 0, NULL, '', 'flgl', 'fenleiguanli', 1491635035, 1491635035, 119, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (4, 'file', 0, 'addon', 'Addon', 'fa fa-rocket', '', '', 'Addon tips', 1, NULL, '', 'cjgl', 'chajianguanli', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (5, 'file', 0, 'auth', 'Auth', 'fa fa-group', '', '', '', 1, NULL, '', 'qxgl', 'quanxianguanli', 1491635035, 1491635035, 99, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (6, 'file', 2, 'general/config', 'Config', 'fa fa-cog', '', '', 'Config tips', 1, NULL, '', 'xtpz', 'xitongpeizhi', 1491635035, 1491635035, 60, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (7, 'file', 2, 'general/attachment', 'Attachment', 'fa fa-file-image-o', '', '', 'Attachment tips', 1, NULL, '', 'fjgl', 'fujianguanli', 1491635035, 1491635035, 53, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (8, 'file', 2, 'general/profile', 'Profile', 'fa fa-user', '', '', '', 1, NULL, '', 'grzl', 'gerenziliao', 1491635035, 1491635035, 34, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (9, 'file', 5, 'auth/admin', 'Admin', 'fa fa-user', '', '', 'Admin tips', 1, NULL, '', 'glygl', 'guanliyuanguanli', 1491635035, 1491635035, 118, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (10, 'file', 5, 'auth/adminlog', 'Admin log', 'fa fa-list-alt', '', '', 'Admin log tips', 1, NULL, '', 'glyrz', 'guanliyuanrizhi', 1491635035, 1491635035, 113, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (11, 'file', 5, 'auth/group', 'Group', 'fa fa-group', '', '', 'Group tips', 1, NULL, '', 'jsz', 'juesezu', 1491635035, 1491635035, 109, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (12, 'file', 5, 'auth/rule', 'Rule', 'fa fa-bars', '', '', 'Rule tips', 1, NULL, '', 'cdgz', 'caidanguize', 1491635035, 1491635035, 104, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (13, 'file', 1, 'dashboard/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 136, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (14, 'file', 1, 'dashboard/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 135, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (15, 'file', 1, 'dashboard/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 133, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (16, 'file', 1, 'dashboard/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 134, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (17, 'file', 1, 'dashboard/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 132, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (18, 'file', 6, 'general/config/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 52, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (19, 'file', 6, 'general/config/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 51, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (20, 'file', 6, 'general/config/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 50, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (21, 'file', 6, 'general/config/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 49, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (22, 'file', 6, 'general/config/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 48, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (23, 'file', 7, 'general/attachment/index', 'View', 'fa fa-circle-o', '', '', 'Attachment tips', 0, NULL, '', '', '', 1491635035, 1491635035, 59, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (24, 'file', 7, 'general/attachment/select', 'Select attachment', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 58, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (25, 'file', 7, 'general/attachment/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 57, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (26, 'file', 7, 'general/attachment/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 56, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (27, 'file', 7, 'general/attachment/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 55, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (28, 'file', 7, 'general/attachment/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 54, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (29, 'file', 8, 'general/profile/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 33, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (30, 'file', 8, 'general/profile/update', 'Update profile', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 32, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (31, 'file', 8, 'general/profile/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 31, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (32, 'file', 8, 'general/profile/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 30, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (33, 'file', 8, 'general/profile/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 29, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (34, 'file', 8, 'general/profile/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 28, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (35, 'file', 3, 'category/index', 'View', 'fa fa-circle-o', '', '', 'Category tips', 0, NULL, '', '', '', 1491635035, 1491635035, 142, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (36, 'file', 3, 'category/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 141, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (37, 'file', 3, 'category/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 140, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (38, 'file', 3, 'category/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 139, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (39, 'file', 3, 'category/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 138, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (40, 'file', 9, 'auth/admin/index', 'View', 'fa fa-circle-o', '', '', 'Admin tips', 0, NULL, '', '', '', 1491635035, 1491635035, 117, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (41, 'file', 9, 'auth/admin/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 116, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (42, 'file', 9, 'auth/admin/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 115, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (43, 'file', 9, 'auth/admin/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 114, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (44, 'file', 10, 'auth/adminlog/index', 'View', 'fa fa-circle-o', '', '', 'Admin log tips', 0, NULL, '', '', '', 1491635035, 1491635035, 112, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (45, 'file', 10, 'auth/adminlog/detail', 'Detail', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 111, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (46, 'file', 10, 'auth/adminlog/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 110, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (47, 'file', 11, 'auth/group/index', 'View', 'fa fa-circle-o', '', '', 'Group tips', 0, NULL, '', '', '', 1491635035, 1491635035, 108, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (48, 'file', 11, 'auth/group/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 107, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (49, 'file', 11, 'auth/group/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 106, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (50, 'file', 11, 'auth/group/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 105, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (51, 'file', 12, 'auth/rule/index', 'View', 'fa fa-circle-o', '', '', 'Rule tips', 0, NULL, '', '', '', 1491635035, 1491635035, 103, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (52, 'file', 12, 'auth/rule/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 102, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (53, 'file', 12, 'auth/rule/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 101, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (54, 'file', 12, 'auth/rule/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 100, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (55, 'file', 4, 'addon/index', 'View', 'fa fa-circle-o', '', '', 'Addon tips', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (56, 'file', 4, 'addon/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (57, 'file', 4, 'addon/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (58, 'file', 4, 'addon/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (59, 'file', 4, 'addon/downloaded', 'Local addon', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (60, 'file', 4, 'addon/state', 'Update state', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (63, 'file', 4, 'addon/config', 'Setting', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (64, 'file', 4, 'addon/refresh', 'Refresh', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (65, 'file', 4, 'addon/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (66, 'file', 0, 'user', 'User', 'fa fa-user-circle', '', '', '', 1, NULL, '', 'hygl', 'huiyuanguanli', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (67, 'file', 66, 'user/user', 'User', 'fa fa-user', '', '', '', 1, NULL, '', 'hygl', 'huiyuanguanli', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (68, 'file', 67, 'user/user/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (69, 'file', 67, 'user/user/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (70, 'file', 67, 'user/user/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (71, 'file', 67, 'user/user/del', 'Del', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (72, 'file', 67, 'user/user/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (73, 'file', 66, 'user/group', 'User group', 'fa fa-users', '', '', '', 1, NULL, '', 'hyfz', 'huiyuanfenzu', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (74, 'file', 73, 'user/group/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (75, 'file', 73, 'user/group/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (76, 'file', 73, 'user/group/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (77, 'file', 73, 'user/group/del', 'Del', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (78, 'file', 73, 'user/group/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (79, 'file', 66, 'user/rule', 'User rule', 'fa fa-circle-o', '', '', '', 1, NULL, '', 'hygz', 'huiyuanguize', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (80, 'file', 79, 'user/rule/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (81, 'file', 79, 'user/rule/del', 'Del', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (82, 'file', 79, 'user/rule/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (83, 'file', 79, 'user/rule/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
INSERT INTO `fa_auth_rule` VALUES (84, 'file', 79, 'user/rule/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_category
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_category` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID',
|
||||
`type` varchar(30) DEFAULT '' COMMENT '栏目类型',
|
||||
`name` varchar(30) DEFAULT '',
|
||||
`nickname` varchar(50) DEFAULT '',
|
||||
`flag` set('hot','index','recommend') DEFAULT '',
|
||||
`image` varchar(100) DEFAULT '' COMMENT '图片',
|
||||
`keywords` varchar(255) DEFAULT '' COMMENT '关键字',
|
||||
`description` varchar(255) DEFAULT '' COMMENT '描述',
|
||||
`diyname` varchar(30) DEFAULT '' COMMENT '自定义名称',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
|
||||
`status` varchar(30) DEFAULT '' COMMENT '状态',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `weigh` (`weigh`,`id`),
|
||||
KEY `pid` (`pid`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='分类表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_category
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_category` VALUES (1, 0, 'page', '官方新闻', 'news', 'recommend', '/assets/img/qrcode.png', '', '', 'news', 1491635035, 1491635035, 1, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (2, 0, 'page', '移动应用', 'mobileapp', 'hot', '/assets/img/qrcode.png', '', '', 'mobileapp', 1491635035, 1491635035, 2, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (3, 2, 'page', '微信公众号', 'wechatpublic', 'index', '/assets/img/qrcode.png', '', '', 'wechatpublic', 1491635035, 1491635035, 3, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (4, 2, 'page', 'Android开发', 'android', 'recommend', '/assets/img/qrcode.png', '', '', 'android', 1491635035, 1491635035, 4, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (5, 0, 'page', '软件产品', 'software', 'recommend', '/assets/img/qrcode.png', '', '', 'software', 1491635035, 1491635035, 5, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (6, 5, 'page', '网站建站', 'website', 'recommend', '/assets/img/qrcode.png', '', '', 'website', 1491635035, 1491635035, 6, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (7, 5, 'page', '企业管理软件', 'company', 'index', '/assets/img/qrcode.png', '', '', 'company', 1491635035, 1491635035, 7, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (8, 6, 'page', 'PC端', 'website-pc', 'recommend', '/assets/img/qrcode.png', '', '', 'website-pc', 1491635035, 1491635035, 8, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (9, 6, 'page', '移动端', 'website-mobile', 'recommend', '/assets/img/qrcode.png', '', '', 'website-mobile', 1491635035, 1491635035, 9, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (10, 7, 'page', 'CRM系统 ', 'company-crm', 'recommend', '/assets/img/qrcode.png', '', '', 'company-crm', 1491635035, 1491635035, 10, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (11, 7, 'page', 'SASS平台软件', 'company-sass', 'recommend', '/assets/img/qrcode.png', '', '', 'company-sass', 1491635035, 1491635035, 11, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (12, 0, 'test', '测试1', 'test1', 'recommend', '/assets/img/qrcode.png', '', '', 'test1', 1491635035, 1491635035, 12, 'normal');
|
||||
INSERT INTO `fa_category` VALUES (13, 0, 'test', '测试2', 'test2', 'recommend', '/assets/img/qrcode.png', '', '', 'test2', 1491635035, 1491635035, 13, 'normal');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_config
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_config` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(30) DEFAULT '' COMMENT '变量名',
|
||||
`group` varchar(30) DEFAULT '' COMMENT '分组',
|
||||
`title` varchar(100) DEFAULT '' COMMENT '变量标题',
|
||||
`tip` varchar(100) DEFAULT '' COMMENT '变量描述',
|
||||
`type` varchar(30) DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file',
|
||||
`visible` varchar(255) DEFAULT '' COMMENT '可见条件',
|
||||
`value` text COMMENT '变量值',
|
||||
`content` text COMMENT '变量字典数据',
|
||||
`rule` varchar(100) DEFAULT '' COMMENT '验证规则',
|
||||
`extend` varchar(255) DEFAULT '' COMMENT '扩展属性',
|
||||
`setting` varchar(255) DEFAULT '' COMMENT '配置',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='系统配置';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_config
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_config` VALUES (1, 'name', 'basic', 'Site name', '请填写站点名称', 'string', '', '我的网站', '', 'required', '', '');
|
||||
INSERT INTO `fa_config` VALUES (2, 'beian', 'basic', 'Beian', '粤ICP备15000000号-1', 'string', '', '', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (3, 'cdnurl', 'basic', 'Cdn url', '如果全站静态资源使用第三方云储存请配置该值', 'string', '', '', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (4, 'version', 'basic', 'Version', '如果静态资源有变动请重新配置该值', 'string', '', '1.0.1', '', 'required', '', '');
|
||||
INSERT INTO `fa_config` VALUES (5, 'timezone', 'basic', 'Timezone', '', 'string', '', 'Asia/Shanghai', '', 'required', '', '');
|
||||
INSERT INTO `fa_config` VALUES (6, 'forbiddenip', 'basic', 'Forbidden ip', '一行一条记录', 'text', '', '', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array', '', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', '', '');
|
||||
INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '请输入左侧菜单栏存在的链接', 'string', '', 'dashboard', '', 'required', '', '');
|
||||
INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '', '1', '[\"请选择\",\"SMTP\"]', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', '', 'smtp.qq.com', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '', '465', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '', '', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码或授权码)', 'password', '', '', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', '(SMTP验证方式[推荐SSL])', 'select', '', '2', '[\"无\",\"TLS\",\"SSL\"]', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '', '', '', '', '', '');
|
||||
INSERT INTO `fa_config` VALUES (18, 'attachmentcategory', 'dictionary', 'Attachment category', '', 'array', '', '{\"category1\":\"Category1\",\"category2\":\"Category2\",\"custom\":\"Custom\"}', '', '', '', '');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_ems
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_ems` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`event` varchar(30) DEFAULT '' COMMENT '事件',
|
||||
`email` varchar(100) DEFAULT '' COMMENT '邮箱',
|
||||
`code` varchar(10) DEFAULT '' COMMENT '验证码',
|
||||
`times` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '验证次数',
|
||||
`ip` varchar(30) DEFAULT '' COMMENT 'IP',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='邮箱验证码表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_sms
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_sms` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`event` varchar(30) DEFAULT '' COMMENT '事件',
|
||||
`mobile` varchar(20) DEFAULT '' COMMENT '手机号',
|
||||
`code` varchar(10) DEFAULT '' COMMENT '验证码',
|
||||
`times` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '验证次数',
|
||||
`ip` varchar(30) DEFAULT '' COMMENT 'IP',
|
||||
`createtime` bigint(16) unsigned DEFAULT '0' COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='短信验证码表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_test
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_test` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`user_id` int(10) DEFAULT '0' COMMENT '会员ID',
|
||||
`admin_id` int(10) DEFAULT '0' COMMENT '管理员ID',
|
||||
`category_id` int(10) unsigned DEFAULT '0' COMMENT '分类ID(单选)',
|
||||
`category_ids` varchar(100) COMMENT '分类ID(多选)',
|
||||
`tags` varchar(255) DEFAULT '' COMMENT '标签',
|
||||
`week` enum('monday','tuesday','wednesday') COMMENT '星期(单选):monday=星期一,tuesday=星期二,wednesday=星期三',
|
||||
`flag` set('hot','index','recommend') DEFAULT '' COMMENT '标志(多选):hot=热门,index=首页,recommend=推荐',
|
||||
`genderdata` enum('male','female') DEFAULT 'male' COMMENT '性别(单选):male=男,female=女',
|
||||
`hobbydata` set('music','reading','swimming') COMMENT '爱好(多选):music=音乐,reading=读书,swimming=游泳',
|
||||
`title` varchar(100) DEFAULT '' COMMENT '标题',
|
||||
`content` text COMMENT '内容',
|
||||
`image` varchar(100) DEFAULT '' COMMENT '图片',
|
||||
`images` varchar(1500) DEFAULT '' COMMENT '图片组',
|
||||
`attachfile` varchar(100) DEFAULT '' COMMENT '附件',
|
||||
`keywords` varchar(255) DEFAULT '' COMMENT '关键字',
|
||||
`description` varchar(255) DEFAULT '' COMMENT '描述',
|
||||
`city` varchar(100) DEFAULT '' COMMENT '省市',
|
||||
`array` varchar(255) DEFAULT '' COMMENT '数组:value=值',
|
||||
`json` varchar(255) DEFAULT '' COMMENT '配置:key=名称,value=值',
|
||||
`multiplejson` varchar(1500) DEFAULT '' COMMENT '二维数组:title=标题,intro=介绍,author=作者,age=年龄',
|
||||
`price` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '价格',
|
||||
`views` int(10) unsigned DEFAULT '0' COMMENT '点击',
|
||||
`workrange` varchar(100) DEFAULT '' COMMENT '时间区间',
|
||||
`startdate` date DEFAULT NULL COMMENT '开始日期',
|
||||
`activitytime` datetime DEFAULT NULL COMMENT '活动时间(datetime)',
|
||||
`year` year(4) DEFAULT NULL COMMENT '年',
|
||||
`times` time DEFAULT NULL COMMENT '时间',
|
||||
`refreshtime` bigint(16) DEFAULT NULL COMMENT '刷新时间',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
|
||||
`weigh` int(10) DEFAULT '0' COMMENT '权重',
|
||||
`switch` tinyint(1) DEFAULT '0' COMMENT '开关',
|
||||
`status` enum('normal','hidden') DEFAULT 'normal' COMMENT '状态',
|
||||
`state` enum('0','1','2') DEFAULT '1' COMMENT '状态值:0=禁用,1=正常,2=推荐',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='测试表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_test
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_test` VALUES (1, 1, 1, 12, '12,13', '互联网,计算机', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '<p>我是测试内容</p>', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '我是一篇测试文章描述,内容过多时将自动隐藏', '广西壮族自治区/百色市/平果县', '[\"a\",\"b\"]', '{\"a\":\"1\",\"b\":\"2\"}', '[{\"title\":\"标题一\",\"intro\":\"介绍一\",\"author\":\"小明\",\"age\":\"21\"}]', 0.00, 0, '2020-10-01 00:00:00 - 2021-10-31 23:59:59', '2017-07-10', '2017-07-10 18:24:45', 2017, '18:24:45', 1491635035, 1491635035, 1491635035, NULL, 0, 1, 'normal', '1');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_user
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_user` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`group_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '组别ID',
|
||||
`username` varchar(32) DEFAULT '' COMMENT '用户名',
|
||||
`nickname` varchar(50) DEFAULT '' COMMENT '昵称',
|
||||
`password` varchar(32) DEFAULT '' COMMENT '密码',
|
||||
`salt` varchar(30) DEFAULT '' COMMENT '密码盐',
|
||||
`email` varchar(100) DEFAULT '' COMMENT '电子邮箱',
|
||||
`mobile` varchar(11) DEFAULT '' COMMENT '手机号',
|
||||
`avatar` varchar(255) DEFAULT '' COMMENT '头像',
|
||||
`level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '等级',
|
||||
`gender` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '性别',
|
||||
`birthday` date DEFAULT NULL COMMENT '生日',
|
||||
`bio` varchar(100) DEFAULT '' COMMENT '格言',
|
||||
`money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '余额',
|
||||
`score` int(10) NOT NULL DEFAULT '0' COMMENT '积分',
|
||||
`successions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '连续登录天数',
|
||||
`maxsuccessions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '最大连续登录天数',
|
||||
`prevtime` bigint(16) DEFAULT NULL COMMENT '上次登录时间',
|
||||
`logintime` bigint(16) DEFAULT NULL COMMENT '登录时间',
|
||||
`loginip` varchar(50) DEFAULT '' COMMENT '登录IP',
|
||||
`loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数',
|
||||
`loginfailuretime` bigint(16) DEFAULT NULL COMMENT '最后登录失败时间',
|
||||
`joinip` varchar(50) DEFAULT '' COMMENT '加入IP',
|
||||
`jointime` bigint(16) DEFAULT NULL COMMENT '加入时间',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`token` varchar(50) DEFAULT '' COMMENT 'Token',
|
||||
`status` varchar(30) DEFAULT '' COMMENT '状态',
|
||||
`verification` varchar(255) DEFAULT '' COMMENT '验证',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `username` (`username`),
|
||||
KEY `email` (`email`),
|
||||
KEY `mobile` (`mobile`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='会员表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_user
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_user` VALUES (1, 1, 'admin', 'admin', '', '', 'admin@163.com', '13000000000', '', 0, 0, '2017-04-08', '', 0, 0, 1, 1, 1491635035, 1491635035, '127.0.0.1', 0, 1491635035,'127.0.0.1', 1491635035, 0, 1491635035, '', 'normal','');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_user_group
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_user_group` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(50) DEFAULT '' COMMENT '组名',
|
||||
`rules` text COMMENT '权限节点',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '添加时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`status` enum('normal','hidden') DEFAULT NULL COMMENT '状态',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci 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', 1491635035, 1491635035, 'normal');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_user_money_log
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_user_money_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID',
|
||||
`money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更余额',
|
||||
`before` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更前余额',
|
||||
`after` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更后余额',
|
||||
`memo` varchar(255) DEFAULT '' COMMENT '备注',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='会员余额变动表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_user_rule
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_user_rule` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`pid` int(10) DEFAULT NULL COMMENT '父ID',
|
||||
`name` varchar(50) DEFAULT NULL COMMENT '名称',
|
||||
`title` varchar(50) DEFAULT '' COMMENT '标题',
|
||||
`remark` varchar(100) DEFAULT NULL COMMENT '备注',
|
||||
`ismenu` tinyint(1) DEFAULT NULL COMMENT '是否菜单',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`weigh` int(10) DEFAULT '0' COMMENT '权重',
|
||||
`status` enum('normal','hidden') DEFAULT NULL COMMENT '状态',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='会员规则表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of fa_user_rule
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `fa_user_rule` VALUES (1, 0, 'index', 'Frontend', '', 1, 1491635035, 1491635035, 1, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (2, 0, 'api', 'API Interface', '', 1, 1491635035, 1491635035, 2, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (3, 1, 'user', 'User Module', '', 1, 1491635035, 1491635035, 12, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (4, 2, 'user', 'User Module', '', 1, 1491635035, 1491635035, 11, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (5, 3, 'index/user/login', 'Login', '', 0, 1491635035, 1491635035, 5, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (6, 3, 'index/user/register', 'Register', '', 0, 1491635035, 1491635035, 7, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (7, 3, 'index/user/index', 'User Center', '', 0, 1491635035, 1491635035, 9, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (8, 3, 'index/user/profile', 'Profile', '', 0, 1491635035, 1491635035, 4, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (9, 4, 'api/user/login', 'Login', '', 0, 1491635035, 1491635035, 6, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (10, 4, 'api/user/register', 'Register', '', 0, 1491635035, 1491635035, 8, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (11, 4, 'api/user/index', 'User Center', '', 0, 1491635035, 1491635035, 10, 'normal');
|
||||
INSERT INTO `fa_user_rule` VALUES (12, 4, 'api/user/profile', 'Profile', '', 0, 1491635035, 1491635035, 3, 'normal');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_user_score_log
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_user_score_log` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID',
|
||||
`score` int(10) NOT NULL DEFAULT '0' COMMENT '变更积分',
|
||||
`before` int(10) NOT NULL DEFAULT '0' COMMENT '变更前积分',
|
||||
`after` int(10) NOT NULL DEFAULT '0' COMMENT '变更后积分',
|
||||
`memo` varchar(255) DEFAULT '' COMMENT '备注',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='会员积分变动表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_user_token
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_user_token` (
|
||||
`token` varchar(50) NOT NULL COMMENT 'Token',
|
||||
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`expiretime` bigint(16) DEFAULT NULL COMMENT '过期时间',
|
||||
PRIMARY KEY (`token`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='会员Token表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for fa_version
|
||||
-- ----------------------------
|
||||
CREATE TABLE `fa_version` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`oldversion` varchar(30) DEFAULT '' COMMENT '旧版本号',
|
||||
`newversion` varchar(30) DEFAULT '' COMMENT '新版本号',
|
||||
`packagesize` varchar(30) DEFAULT '' COMMENT '包大小',
|
||||
`content` varchar(500) DEFAULT '' COMMENT '升级内容',
|
||||
`downloadurl` varchar(255) DEFAULT '' COMMENT '下载地址',
|
||||
`enforce` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '强制更新',
|
||||
`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
|
||||
`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
|
||||
`weigh` int(10) NOT NULL DEFAULT 0 COMMENT '权重',
|
||||
`status` varchar(30) DEFAULT '' COMMENT '状态',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='版本表';
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
316
application/admin/command/Install/install.html
Normal file
316
application/admin/command/Install/install.html
Normal file
@@ -0,0 +1,316 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>{:__('Installing FastAdmin')}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
|
||||
<style>
|
||||
body {
|
||||
background: #f1f6fd;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body, input, button {
|
||||
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, 'Microsoft Yahei', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #7E96B3;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #4e73df;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
font-weight: normal;
|
||||
color: #3C5675;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group .form-field:first-child input {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.form-group .form-field:last-child input {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.form-field input {
|
||||
background: #fff;
|
||||
margin: 0 0 2px;
|
||||
border: 2px solid transparent;
|
||||
transition: background 0.2s, border-color 0.2s, color 0.2s;
|
||||
width: 100%;
|
||||
padding: 15px 15px 15px 180px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-field input:focus {
|
||||
border-color: #4e73df;
|
||||
background: #fff;
|
||||
color: #444;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-field label {
|
||||
float: left;
|
||||
width: 160px;
|
||||
text-align: right;
|
||||
margin-right: -160px;
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
font-size: 14px;
|
||||
pointer-events: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
button, .btn {
|
||||
background: #3C5675;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
padding: 15px 30px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
height: 52px;
|
||||
line-height: 52px;
|
||||
}
|
||||
|
||||
.form-buttons .btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#error, .error, #success, .success, #warmtips, .warmtips {
|
||||
background: #D83E3E;
|
||||
color: #fff;
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#success {
|
||||
background: #3C5675;
|
||||
}
|
||||
|
||||
#error a, .error a {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#warmtips {
|
||||
background: #ffcdcd;
|
||||
font-size: 14px;
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
#warmtips a {
|
||||
background: #ffffff7a;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-top: 10px;
|
||||
color: #e21a1a;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>
|
||||
<svg width="80px" height="96px" viewBox="0 0 768 830" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="logo" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M64.433651,605.899968 C20.067302,536.265612 0,469.698785 0,389.731348 C0,174.488668 171.922656,0 384,0 C596.077344,0 768,174.488668 768,389.731348 C768,469.698785 747.932698,536.265612 703.566349,605.899968 C614.4,753.480595 441.6,870.4 384,870.4 C326.4,870.4 153.6,753.480595 64.433651,605.899968 L64.433651,605.899968 Z"
|
||||
id="body" fill="#4e73df"></path>
|
||||
<path d="M429.648991,190.816 L430.160991,190.816 L429.648991,190.816 L429.648991,190.816 Z M429.648991,156 L427.088991,156 C419.408991,157.024 411.728991,160.608 404.560991,168.8 L403.024991,170.848 L206.928991,429.92 C198.736991,441.184 197.712991,453.984 204.368991,466.784 C210.512991,478.048 222.288991,485.728 235.600991,485.728 L336.464991,486.24 L304.208991,673.632 C301.648991,689.504 310.352991,705.376 325.200991,712.032 C329.808991,714.08 334.416991,714.592 339.536991,714.592 C349.776991,714.592 358.992991,709.472 366.160991,700.256 L561.744991,419.168 C569.936991,407.904 570.960991,395.104 564.304991,382.304 C557.648991,369.504 547.408991,363.36 533.072991,363.36 L432.208991,363.36 L463.952991,199.008 C464.464991,196.448 464.976991,193.376 464.976991,190.816 C464.976991,171.872 449.104991,156 431.184991,156 L429.648991,156 L429.648991,156 Z"
|
||||
id="flash" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</h1>
|
||||
<h2>{:__('Installing FastAdmin')}</h2>
|
||||
<div>
|
||||
|
||||
<form method="post">
|
||||
{if $errInfo}
|
||||
<div class="error">
|
||||
{$errInfo}
|
||||
</div>
|
||||
{/if}
|
||||
<div id="error" style="display:none"></div>
|
||||
<div id="success" style="display:none"></div>
|
||||
<div id="warmtips" style="display:none"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<label>{:__('Mysql Hostname')}</label>
|
||||
<input type="text" name="mysqlHostname" value="127.0.0.1" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Mysql Database')}</label>
|
||||
<input type="text" name="mysqlDatabase" value="" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Mysql Username')}</label>
|
||||
<input type="text" name="mysqlUsername" value="root" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Mysql Password')}</label>
|
||||
<input type="password" name="mysqlPassword">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Mysql Prefix')}</label>
|
||||
<input type="text" name="mysqlPrefix" value="fa_">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Mysql Hostport')}</label>
|
||||
<input type="number" name="mysqlHostport" value="3306">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<label>{:__('Admin Username')}</label>
|
||||
<input name="adminUsername" value="admin" required=""/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Admin Email')}</label>
|
||||
<input name="adminEmail" value="admin@admin.com" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Admin Password')}</label>
|
||||
<input type="password" name="adminPassword" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>{:__('Repeat Password')}</label>
|
||||
<input type="password" name="adminPasswordConfirmation" required="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<label>{:__('Website')}</label>
|
||||
<input type="text" name="siteName" value="{:__('My Website')}" required=""/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<!--@formatter:off-->
|
||||
<button type="submit" {:$errInfo?'disabled':''}>{:__('Install now')}</button>
|
||||
<!--@formatter:on-->
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="__ROOT__/assets/libs/jquery/dist/jquery.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('form :input:first').select();
|
||||
|
||||
$('form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var form = this;
|
||||
var $error = $("#error");
|
||||
var $success = $("#success");
|
||||
var $button = $(this).find('button')
|
||||
.text("{:__('Installing')}")
|
||||
.prop('disabled', true);
|
||||
$.ajax({
|
||||
url: "",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: $(this).serialize(),
|
||||
success: function (ret) {
|
||||
if (ret.code == 1) {
|
||||
var data = ret.data;
|
||||
$error.hide();
|
||||
$(".form-group", form).remove();
|
||||
$button.remove();
|
||||
$("#success").text(ret.msg).show();
|
||||
|
||||
$buttons = $(".form-buttons", form);
|
||||
$("<a class='btn' href='./'>{:__('Home')}</a>").appendTo($buttons);
|
||||
|
||||
if (typeof data.adminName !== 'undefined') {
|
||||
var url = location.href.replace(/install\.php/, data.adminName);
|
||||
$("#warmtips").html("{:__('Security tips')}" + '<a href="' + url + '">' + url + '</a>').show();
|
||||
$('<a class="btn" href="' + url + '" id="btn-admin" style="background:#4e73df">' + "{:__('Dashboard')}" + '</a>').appendTo($buttons);
|
||||
}
|
||||
localStorage.setItem("fastep", "installed");
|
||||
} else {
|
||||
$error.show().text(ret.msg);
|
||||
$button.prop('disabled', false).text("{:__('Install now')}");
|
||||
$("html,body").animate({
|
||||
scrollTop: 0
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
$error.show().text(xhr.responseText);
|
||||
$button.prop('disabled', false).text("{:__('Install now')}");
|
||||
$("html,body").animate({
|
||||
scrollTop: 0
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1
application/admin/command/Install/install.lock
Normal file
1
application/admin/command/Install/install.lock
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
35
application/admin/command/Install/zh-cn.php
Normal file
35
application/admin/command/Install/zh-cn.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
return [
|
||||
'Warning' => '温馨提示',
|
||||
'Installing FastAdmin' => '安装FastAdmin',
|
||||
'Mysql Hostname' => 'MySQL 数据库地址',
|
||||
'Mysql Database' => 'MySQL 数据库名',
|
||||
'Mysql Username' => 'MySQL 用户名',
|
||||
'Mysql Password' => 'MySQL 密码',
|
||||
'Mysql Prefix' => 'MySQL 数据表前缀',
|
||||
'Mysql Hostport' => 'MySQL 端口号',
|
||||
'Admin Username' => '管理员用户名',
|
||||
'Admin Email' => '管理员Email',
|
||||
'Admin Password' => '管理员密码',
|
||||
'Repeat Password' => '重复管理员密码',
|
||||
'Website' => '网站名称',
|
||||
'My Website' => '我的网站',
|
||||
'Install now' => '点击安装',
|
||||
'Installing' => '安装中...',
|
||||
'Home' => '访问首页',
|
||||
'Dashboard' => '进入后台',
|
||||
'Go back' => '返回上一页',
|
||||
'Install Successed' => '安装成功!',
|
||||
'Security tips' => '温馨提示:请将以下后台登录入口添加到你的收藏夹,为了你的站点安全,不要泄漏或发送给他人!如有泄漏请及时修改!',
|
||||
'Please input correct database' => '请输入正确的数据库名',
|
||||
'Please input correct username' => '用户名只能由3-30位数字、字母、下划线组合',
|
||||
'Please input correct password' => '密码长度必须在6-30位之间,不能包含空格',
|
||||
'Password is too weak' => '密码太简单,请重新输入',
|
||||
'The two passwords you entered did not match' => '两次输入的密码不一致',
|
||||
'Please input correct website' => '网站名称输入不正确',
|
||||
'The current PHP %s is too low, please use PHP %s or higher' => '当前版本PHP %s过低,请使用PHP %s及以上版本',
|
||||
'PDO is not currently installed and cannot be installed' => '当前未开启PDO,无法进行安装',
|
||||
'The current permissions are insufficient to write the file %s' => '当前权限不足,无法写入文件%s',
|
||||
'Please go to the official website to download the full package or resource package and try to install' => '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装',
|
||||
'The system has been installed. If you need to reinstall, please remove %s first' => '当前已经安装成功,如果需要重新安装,请手动移除%s文件',
|
||||
];
|
||||
327
application/admin/command/Menu.php
Normal file
327
application/admin/command/Menu.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use app\admin\model\AuthRule;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
|
||||
class Menu extends Command
|
||||
{
|
||||
protected $model = null;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('menu')
|
||||
->addOption('controller', 'c', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name,use \'all-controller\' when build all menu', null)
|
||||
->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '')
|
||||
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force delete menu,without tips', null)
|
||||
->addOption('equal', 'e', Option::VALUE_OPTIONAL, 'the controller must be equal', null)
|
||||
->setDescription('Build auth menu from controller');
|
||||
//要执行的controller必须一样,不适用模糊查询
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$this->model = new AuthRule();
|
||||
$adminPath = dirname(__DIR__) . DS;
|
||||
//控制器名
|
||||
$controller = $input->getOption('controller') ?: '';
|
||||
if (!$controller) {
|
||||
throw new Exception("please input controller name");
|
||||
}
|
||||
$force = $input->getOption('force');
|
||||
//是否为删除模式
|
||||
$delete = $input->getOption('delete');
|
||||
//是否控制器完全匹配
|
||||
$equal = $input->getOption('equal');
|
||||
|
||||
|
||||
if ($delete) {
|
||||
if (in_array('all-controller', $controller)) {
|
||||
throw new Exception("could not delete all menu");
|
||||
}
|
||||
$ids = [];
|
||||
$list = $this->model->where(function ($query) use ($controller, $equal) {
|
||||
foreach ($controller as $index => $item) {
|
||||
if (stripos($item, '_') !== false) {
|
||||
$item = Loader::parseName($item, 1);
|
||||
}
|
||||
if (stripos($item, '/') !== false) {
|
||||
$controllerArr = explode('/', $item);
|
||||
end($controllerArr);
|
||||
$key = key($controllerArr);
|
||||
$controllerArr[$key] = Loader::parseName($controllerArr[$key]);
|
||||
} else {
|
||||
$controllerArr = [Loader::parseName($item)];
|
||||
}
|
||||
$item = str_replace('_', '\_', implode('/', $controllerArr));
|
||||
if ($equal) {
|
||||
$query->whereOr('name', 'eq', $item);
|
||||
} else {
|
||||
$query->whereOr('name', 'like', strtolower($item) . "%");
|
||||
}
|
||||
}
|
||||
})->select();
|
||||
foreach ($list as $k => $v) {
|
||||
$output->warning($v->name);
|
||||
$ids[] = $v->id;
|
||||
}
|
||||
if (!$ids) {
|
||||
throw new Exception("There is no menu to delete");
|
||||
}
|
||||
if (!$force) {
|
||||
$output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: ");
|
||||
$line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
|
||||
if (trim($line) != 'yes') {
|
||||
throw new Exception("Operation is aborted!");
|
||||
}
|
||||
}
|
||||
AuthRule::destroy($ids);
|
||||
|
||||
Cache::rm("__menu__");
|
||||
$output->info("Delete Successed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array('all-controller', $controller)) {
|
||||
foreach ($controller as $index => $item) {
|
||||
if (stripos($item, '_') !== false) {
|
||||
$item = Loader::parseName($item, 1);
|
||||
}
|
||||
if (stripos($item, '/') !== false) {
|
||||
$controllerArr = explode('/', $item);
|
||||
end($controllerArr);
|
||||
$key = key($controllerArr);
|
||||
$controllerArr[$key] = ucfirst($controllerArr[$key]);
|
||||
} else {
|
||||
$controllerArr = [ucfirst($item)];
|
||||
}
|
||||
$adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php';
|
||||
if (!is_file($adminPath)) {
|
||||
$output->error("controller not found");
|
||||
return;
|
||||
}
|
||||
$this->importRule($item);
|
||||
}
|
||||
} else {
|
||||
$authRuleList = AuthRule::select();
|
||||
//生成权限规则备份文件
|
||||
file_put_contents(RUNTIME_PATH . 'authrule.json', json_encode(collection($authRuleList)->toArray()));
|
||||
|
||||
$this->model->where('id', '>', 0)->delete();
|
||||
$controllerDir = $adminPath . 'controller' . DS;
|
||||
// 扫描新的节点信息并导入
|
||||
$treelist = $this->import($this->scandir($controllerDir));
|
||||
}
|
||||
Cache::rm("__menu__");
|
||||
$output->info("Build Successed!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归扫描文件夹
|
||||
* @param string $dir
|
||||
* @return array
|
||||
*/
|
||||
public function scandir($dir)
|
||||
{
|
||||
$result = [];
|
||||
$cdir = scandir($dir);
|
||||
foreach ($cdir as $value) {
|
||||
if (!in_array($value, array(".", ".."))) {
|
||||
if (is_dir($dir . DS . $value)) {
|
||||
$result[$value] = $this->scandir($dir . DS . $value);
|
||||
} else {
|
||||
$result[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入规则节点
|
||||
* @param array $dirarr
|
||||
* @param array $parentdir
|
||||
* @return array
|
||||
*/
|
||||
public function import($dirarr, $parentdir = [])
|
||||
{
|
||||
$menuarr = [];
|
||||
foreach ($dirarr as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
//当前是文件夹
|
||||
$nowparentdir = array_merge($parentdir, [$k]);
|
||||
$this->import($v, $nowparentdir);
|
||||
} else {
|
||||
//只匹配PHP文件
|
||||
if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) {
|
||||
continue;
|
||||
}
|
||||
//导入文件
|
||||
$controller = ($parentdir ? implode('/', $parentdir) . '/' : '') . $matchone[1];
|
||||
$this->importRule($controller);
|
||||
}
|
||||
}
|
||||
|
||||
return $menuarr;
|
||||
}
|
||||
|
||||
protected function importRule($controller)
|
||||
{
|
||||
$controller = str_replace('\\', '/', $controller);
|
||||
if (stripos($controller, '/') !== false) {
|
||||
$controllerArr = explode('/', $controller);
|
||||
end($controllerArr);
|
||||
$key = key($controllerArr);
|
||||
$controllerArr[$key] = ucfirst($controllerArr[$key]);
|
||||
} else {
|
||||
$key = 0;
|
||||
$controllerArr = [ucfirst($controller)];
|
||||
}
|
||||
$classSuffix = Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
|
||||
$className = "\\app\\admin\\controller\\" . implode("\\", $controllerArr) . $classSuffix;
|
||||
|
||||
$pathArr = $controllerArr;
|
||||
array_unshift($pathArr, '', 'application', 'admin', 'controller');
|
||||
$classFile = ROOT_PATH . implode(DS, $pathArr) . $classSuffix . ".php";
|
||||
$classContent = file_get_contents($classFile);
|
||||
$uniqueName = uniqid("FastAdmin") . $classSuffix;
|
||||
$classContent = str_replace("class " . $controllerArr[$key] . $classSuffix . " ", 'class ' . $uniqueName . ' ', $classContent);
|
||||
$classContent = preg_replace("/namespace\s(.*);/", 'namespace ' . __NAMESPACE__ . ";", $classContent);
|
||||
|
||||
//临时的类文件
|
||||
$tempClassFile = __DIR__ . DS . $uniqueName . ".php";
|
||||
file_put_contents($tempClassFile, $classContent);
|
||||
$className = "\\app\\admin\\command\\" . $uniqueName;
|
||||
|
||||
//删除临时文件
|
||||
register_shutdown_function(function () use ($tempClassFile) {
|
||||
if ($tempClassFile) {
|
||||
//删除临时文件
|
||||
@unlink($tempClassFile);
|
||||
}
|
||||
});
|
||||
|
||||
//反射机制调用类的注释和方法名
|
||||
$reflector = new ReflectionClass($className);
|
||||
|
||||
//只匹配公共的方法
|
||||
$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
$classComment = $reflector->getDocComment();
|
||||
//判断是否有启用软删除
|
||||
$softDeleteMethods = ['destroy', 'restore', 'recyclebin'];
|
||||
$withSofeDelete = false;
|
||||
$modelRegexArr = ["/\\\$this\->model\s*=\s*model\(['|\"](\w+)['|\"]\);/", "/\\\$this\->model\s*=\s*new\s+([a-zA-Z\\\]+);/"];
|
||||
$modelRegex = preg_match($modelRegexArr[0], $classContent) ? $modelRegexArr[0] : $modelRegexArr[1];
|
||||
preg_match_all($modelRegex, $classContent, $matches);
|
||||
if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) {
|
||||
\think\Request::instance()->module('admin');
|
||||
$model = model($matches[1][0]);
|
||||
if (in_array('trashed', get_class_methods($model))) {
|
||||
$withSofeDelete = true;
|
||||
}
|
||||
}
|
||||
//忽略的类
|
||||
if (stripos($classComment, "@internal") !== false) {
|
||||
return;
|
||||
}
|
||||
preg_match_all('#(@.*?)\n#s', $classComment, $annotations);
|
||||
$controllerIcon = 'fa fa-circle-o';
|
||||
$controllerRemark = '';
|
||||
//判断注释中是否设置了icon值
|
||||
if (isset($annotations[1])) {
|
||||
foreach ($annotations[1] as $tag) {
|
||||
if (stripos($tag, '@icon') !== false) {
|
||||
$controllerIcon = substr($tag, stripos($tag, ' ') + 1);
|
||||
}
|
||||
if (stripos($tag, '@remark') !== false) {
|
||||
$controllerRemark = substr($tag, stripos($tag, ' ') + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
//过滤掉其它字符
|
||||
$controllerTitle = trim(preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $classComment));
|
||||
|
||||
//导入中文语言包
|
||||
\think\Lang::load(dirname(__DIR__) . DS . 'lang/zh-cn.php');
|
||||
|
||||
//先导入菜单的数据
|
||||
$pid = 0;
|
||||
foreach ($controllerArr as $k => $v) {
|
||||
$key = $k + 1;
|
||||
//驼峰转下划线
|
||||
$controllerNameArr = array_slice($controllerArr, 0, $key);
|
||||
foreach ($controllerNameArr as &$val) {
|
||||
$val = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $val), "_"));
|
||||
}
|
||||
unset($val);
|
||||
$name = implode('/', $controllerNameArr);
|
||||
$title = (!isset($controllerArr[$key]) ? $controllerTitle : '');
|
||||
$icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list');
|
||||
$remark = (!isset($controllerArr[$key]) ? $controllerRemark : '');
|
||||
$title = $title ? $title : $v;
|
||||
$rulemodel = $this->model->get(['name' => $name]);
|
||||
if (!$rulemodel) {
|
||||
$this->model
|
||||
->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal'])
|
||||
->isUpdate(false)
|
||||
->save();
|
||||
$pid = $this->model->id;
|
||||
} else {
|
||||
$pid = $rulemodel->id;
|
||||
}
|
||||
}
|
||||
$ruleArr = [];
|
||||
foreach ($methods as $m => $n) {
|
||||
//过滤特殊的类
|
||||
if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') {
|
||||
continue;
|
||||
}
|
||||
//未启用软删除时过滤相关方法
|
||||
if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) {
|
||||
continue;
|
||||
}
|
||||
//只匹配符合的方法
|
||||
if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) {
|
||||
unset($methods[$m]);
|
||||
continue;
|
||||
}
|
||||
$comment = $reflector->getMethod($n->name)->getDocComment();
|
||||
//忽略的方法
|
||||
if (stripos($comment, "@internal") !== false) {
|
||||
continue;
|
||||
}
|
||||
//过滤掉其它字符
|
||||
$comment = preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $comment);
|
||||
|
||||
$title = $comment ? $comment : ucfirst($n->name);
|
||||
|
||||
//获取主键,作为AuthRule更新依据
|
||||
$id = $this->getAuthRulePK($name . "/" . strtolower($n->name));
|
||||
|
||||
$ruleArr[] = array('id' => $id, 'pid' => $pid, 'name' => $name . "/" . strtolower($n->name), 'icon' => 'fa fa-circle-o', 'title' => $title, 'ismenu' => 0, 'status' => 'normal');
|
||||
}
|
||||
$this->model->isUpdate(false)->saveAll($ruleArr);
|
||||
}
|
||||
|
||||
//获取主键
|
||||
protected function getAuthRulePK($name)
|
||||
{
|
||||
if (!empty($name)) {
|
||||
$id = $this->model
|
||||
->where('name', $name)
|
||||
->value('id');
|
||||
return $id ? $id : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
163
application/admin/command/Min.php
Normal file
163
application/admin/command/Min.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\Exception;
|
||||
|
||||
class Min extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* 路径和文件名配置
|
||||
*/
|
||||
protected $options = [
|
||||
'cssBaseUrl' => 'public/assets/css/',
|
||||
'cssBaseName' => '{module}',
|
||||
'jsBaseUrl' => 'public/assets/js/',
|
||||
'jsBaseName' => 'require-{module}',
|
||||
];
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->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');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$module = $input->getOption('module') ?: '';
|
||||
$resource = $input->getOption('resource') ?: '';
|
||||
$optimize = $input->getOption('optimize') ?: 'none';
|
||||
|
||||
if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) {
|
||||
throw new Exception('Please input correct module name');
|
||||
}
|
||||
if (!$resource || !in_array($resource, ['js', 'css', 'all'])) {
|
||||
throw new Exception('Please input correct resource name');
|
||||
}
|
||||
|
||||
$moduleArr = $module == 'all' ? ['frontend', 'backend'] : [$module];
|
||||
$resourceArr = $resource == 'all' ? ['js', 'css'] : [$resource];
|
||||
|
||||
$minPath = __DIR__ . DS . 'Min' . DS;
|
||||
$publicPath = ROOT_PATH . 'public' . DS;
|
||||
$tempFile = $minPath . 'temp.js';
|
||||
|
||||
$nodeExec = '';
|
||||
|
||||
if (!$nodeExec) {
|
||||
if (IS_WIN) {
|
||||
// Winsows下请手动配置配置该值,一般将该值配置为 '"C:\Program Files\nodejs\node.exe"',除非你的Node安装路径有变更
|
||||
$nodeExec = 'C:\Program Files\nodejs\node.exe';
|
||||
if (file_exists($nodeExec)) {
|
||||
$nodeExec = '"' . $nodeExec . '"';
|
||||
} else {
|
||||
// 如果 '"C:\Program Files\nodejs\node.exe"' 不存在,可能是node安装路径有变更
|
||||
// 但安装node会自动配置环境变量,直接执行 '"node.exe"' 提高第一次使用压缩打包的成功率
|
||||
$nodeExec = '"node.exe"';
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$nodeExec = exec("which node");
|
||||
if (!$nodeExec) {
|
||||
throw new Exception("node environment not found!please install node first!");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($moduleArr as $mod) {
|
||||
foreach ($resourceArr as $res) {
|
||||
$data = [
|
||||
'publicPath' => $publicPath,
|
||||
'jsBaseName' => str_replace('{module}', $mod, $this->options['jsBaseName']),
|
||||
'jsBaseUrl' => $this->options['jsBaseUrl'],
|
||||
'cssBaseName' => str_replace('{module}', $mod, $this->options['cssBaseName']),
|
||||
'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,
|
||||
];
|
||||
|
||||
//源文件
|
||||
$from = $data["{$res}BasePath"] . $data["{$res}BaseName"] . '.' . $res;
|
||||
if (!is_file($from)) {
|
||||
$output->error("{$res} source file not found!file:{$from}");
|
||||
continue;
|
||||
}
|
||||
if ($res == "js") {
|
||||
$content = file_get_contents($from);
|
||||
preg_match("/require\.config\(\{[\r\n]?[\n]?+(.*?)[\r\n]?[\n]?}\);/is", $content, $matches);
|
||||
if (!isset($matches[1])) {
|
||||
$output->error("js config not found!");
|
||||
continue;
|
||||
}
|
||||
$config = preg_replace("/(urlArgs|baseUrl):(.*)\n/", '', $matches[1]);
|
||||
$config = preg_replace("/('tableexport'):(.*)\,\n/", "'tableexport': 'empty:',\n", $config);
|
||||
$data['config'] = $config;
|
||||
}
|
||||
// 生成压缩文件
|
||||
$this->writeToFile($res, $data, $tempFile);
|
||||
|
||||
$output->info("Compress " . $data["{$res}BaseName"] . ".{$res}");
|
||||
|
||||
// 执行压缩
|
||||
$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!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入到文件
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
* @param string $pathname
|
||||
* @return mixed
|
||||
*/
|
||||
protected function writeToFile($name, $data, $pathname)
|
||||
{
|
||||
$search = $replace = [];
|
||||
foreach ($data as $k => $v) {
|
||||
$search[] = "{%{$k}%}";
|
||||
$replace[] = $v;
|
||||
}
|
||||
$stub = file_get_contents($this->getStub($name));
|
||||
$content = str_replace($search, $replace, $stub);
|
||||
|
||||
if (!is_dir(dirname($pathname))) {
|
||||
mkdir(strtolower(dirname($pathname)), 0755, true);
|
||||
}
|
||||
return file_put_contents($pathname, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础模板
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function getStub($name)
|
||||
{
|
||||
return __DIR__ . DS . 'Min' . DS . 'stubs' . DS . $name . '.stub';
|
||||
}
|
||||
}
|
||||
30431
application/admin/command/Min/r.js
Normal file
30431
application/admin/command/Min/r.js
Normal file
File diff suppressed because one or more lines are too long
6
application/admin/command/Min/stubs/css.stub
Normal file
6
application/admin/command/Min/stubs/css.stub
Normal file
@@ -0,0 +1,6 @@
|
||||
({
|
||||
cssIn: "{%cssBasePath%}{%cssBaseName%}.css",
|
||||
out: "{%cssBasePath%}{%cssBaseName%}.min.css",
|
||||
optimizeCss: "default",
|
||||
optimize: "{%optimize%}"
|
||||
})
|
||||
11
application/admin/command/Min/stubs/js.stub
Normal file
11
application/admin/command/Min/stubs/js.stub
Normal file
@@ -0,0 +1,11 @@
|
||||
({
|
||||
{%config%}
|
||||
,
|
||||
optimizeCss: "standard",
|
||||
optimize: "{%optimize%}", //可使用uglify|closure|none
|
||||
preserveLicenseComments: false,
|
||||
removeCombined: false,
|
||||
baseUrl: "{%jsBasePath%}", //JS文件所在的基础目录
|
||||
name: "{%jsBaseName%}", //来源文件,不包含后缀
|
||||
out: "{%jsBasePath%}{%jsBaseName%}.min.js" //目标文件
|
||||
});
|
||||
Reference in New Issue
Block a user