init commit
This commit is contained in:
342
application/admin/command/Addon.php
Normal file
342
application/admin/command/Addon.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?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
|
||||
);
|
||||
|
||||
foreach ($files as $name => $file) {
|
||||
if (!$file->isDir()) {
|
||||
$filePath = $file->getRealPath();
|
||||
$relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir)));
|
||||
if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) {
|
||||
$zip->addFile($filePath, $relativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
$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
|
||||
189
application/admin/command/Api.php
Normal file
189
application/admin/command/Api.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?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();
|
||||
$classes[] = $this->getClassFromFile($filePath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($controller as $index => $item) {
|
||||
$filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php';
|
||||
$classes[] = $this->getClassFromFile($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $namespace . '\\' . $class;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
$inputtype = $params['type'] && isset($typeArr[$params['type']]) ? $typeArr[$params['type']] : ($params['name'] == 'password' ? 'password' : 'text');
|
||||
$tr = array(
|
||||
'name' => $params['name'],
|
||||
'type' => $params['type'] ?? 'string',
|
||||
'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 isset($labes[$method]) ? $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] = isset($method['weigh']) ? $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://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Plugin CSS -->
|
||||
<link href="https://cdn.staticfile.org/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://cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/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}" 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}">
|
||||
{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://cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
|
||||
|
||||
<!-- Bootstrap Core JavaScript -->
|
||||
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/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>
|
||||
1791
application/admin/command/Crud.php
Normal file
1791
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 ? $value : (isset($data['{%field%}']) ? $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 ? $value : (isset($data['{%field%}']) ? $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();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
protected static function init()
|
||||
{
|
||||
self::afterInsert(function ($row) {
|
||||
$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 ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
|
||||
$list = $this->{%listMethodName%}();
|
||||
return isset($list[$value]) ? $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 ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
|
||||
$list = $this->{%listMethodName%}();
|
||||
return isset($list[$value]) ? $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' => [],
|
||||
];
|
||||
|
||||
}
|
||||
565
application/admin/command/Install.php
Normal file
565
application/admin/command/Install.php
Normal file
@@ -0,0 +1,565 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\command;
|
||||
|
||||
use fast\Random;
|
||||
use PDO;
|
||||
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\Lang;
|
||||
use think\Request;
|
||||
use think\View;
|
||||
|
||||
class Install extends Command
|
||||
{
|
||||
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:http://www.yoursite.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(Config::get('template'), Config::get('view_replace_str'));
|
||||
$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()) {
|
||||
$data_config = config('database');
|
||||
if (empty($data_config['database'])) {
|
||||
return $output(0, '请求application/database.php文件下设置数据库名称');
|
||||
}
|
||||
if (empty($data_config['username'])) {
|
||||
return $output(0, '请求application/database.php文件下设置数据库用户名');
|
||||
}
|
||||
if (empty($data_config['password'])) {
|
||||
return $output(0, '请求application/database.php文件下设置数据库密码');
|
||||
}
|
||||
if ($data_config['database'] == 'root') {
|
||||
return $output(0, '请求application/database.php文件下数据库名称不能为root');
|
||||
}
|
||||
$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';
|
||||
|
||||
// 数据库配置文件
|
||||
$dbConfigFile = APP_PATH . 'database.php';
|
||||
$dbConfigText = @file_get_contents($dbConfigFile);
|
||||
$callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) {
|
||||
$field = "mysql" . ucfirst($matches[1]);
|
||||
$replace = $$field;
|
||||
if ($matches[1] == 'hostport' && $mysqlHostport == 3306) {
|
||||
$replace = '';
|
||||
}
|
||||
return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}Env::get('database.{$matches[1]}', '{$replace}'),";
|
||||
};
|
||||
$dbConfigText = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $dbConfigText);
|
||||
|
||||
// 检测能否成功写入数据库配置
|
||||
$result = @file_put_contents($dbConfigFile, $dbConfigText);
|
||||
if (!$result) {
|
||||
throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/database.php'));
|
||||
}
|
||||
|
||||
// 设置新的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 = request()->domain() . '/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) {
|
||||
|
||||
}
|
||||
|
||||
$tmpFile = ROOT_PATH.'public'.DIRECTORY_SEPARATOR.'wdsxh-4.4.0.zip';
|
||||
$name = 'wdsxh';
|
||||
Service::unzip($name, $tmpFile);
|
||||
// 默认启用该插件
|
||||
$info = get_addon_info($name);
|
||||
$addonDir = Service::getAddonDir($name);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
if (!$info['state']) {
|
||||
$info['state'] = 1;
|
||||
set_addon_info($name, $info);
|
||||
}
|
||||
|
||||
// 执行安装脚本
|
||||
$class = get_addon_class($name);
|
||||
if (class_exists($class)) {
|
||||
$addon = new $class();
|
||||
$addon->install();
|
||||
}
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
@rmdirs($addonDir);
|
||||
Db::rollback();
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
$fileName = null;
|
||||
$fileName = is_null($fileName) ? 'install.sql' : $fileName;
|
||||
$sqlFile = Service::getAddonDir($name) . $fileName;
|
||||
if (is_file($sqlFile)) {
|
||||
$lines = file($sqlFile);
|
||||
$templine = '';
|
||||
foreach ($lines as $line) {
|
||||
if (substr($line, 0, 2) == '--' || $line == '' || substr($line, 0, 2) == '/*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$templine .= $line;
|
||||
if (substr(trim($line), -1, 1) == ';') {
|
||||
$templine = str_ireplace('__PREFIX__', config('database.prefix'), $templine);
|
||||
$templine = str_ireplace('INSERT INTO ', 'INSERT IGNORE INTO ', $templine);
|
||||
try {
|
||||
Db::getPdo()->exec($templine);
|
||||
} catch (\PDOException $e) {
|
||||
//$e->getMessage();
|
||||
}
|
||||
$templine = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
is_file($tmpFile) && unlink($tmpFile);
|
||||
// 启用插件
|
||||
Service::enable($name, true);
|
||||
|
||||
|
||||
//地图位置(经纬度)选择插件
|
||||
$tmpFile = ROOT_PATH.'public'.DIRECTORY_SEPARATOR.'address-1.1.8.zip';
|
||||
$name = 'address';
|
||||
Service::unzip($name, $tmpFile);
|
||||
// 默认启用该插件
|
||||
$info = get_addon_info($name);
|
||||
$addonDir = Service::getAddonDir($name);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
if (!$info['state']) {
|
||||
$info['state'] = 1;
|
||||
set_addon_info($name, $info);
|
||||
}
|
||||
|
||||
// 执行安装脚本
|
||||
$class = get_addon_class($name);
|
||||
if (class_exists($class)) {
|
||||
$addon = new $class();
|
||||
$addon->install();
|
||||
}
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
@rmdirs($addonDir);
|
||||
Db::rollback();
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
$fileName = null;
|
||||
$fileName = is_null($fileName) ? 'install.sql' : $fileName;
|
||||
$sqlFile = Service::getAddonDir($name) . $fileName;
|
||||
if (is_file($sqlFile)) {
|
||||
$lines = file($sqlFile);
|
||||
$templine = '';
|
||||
foreach ($lines as $line) {
|
||||
if (substr($line, 0, 2) == '--' || $line == '' || substr($line, 0, 2) == '/*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$templine .= $line;
|
||||
if (substr(trim($line), -1, 1) == ';') {
|
||||
$templine = str_ireplace('__PREFIX__', config('database.prefix'), $templine);
|
||||
$templine = str_ireplace('INSERT INTO ', 'INSERT IGNORE INTO ', $templine);
|
||||
try {
|
||||
Db::getPdo()->exec($templine);
|
||||
} catch (\PDOException $e) {
|
||||
//$e->getMessage();
|
||||
}
|
||||
$templine = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
is_file($tmpFile) && unlink($tmpFile);
|
||||
// 启用插件
|
||||
Service::enable($name, true);
|
||||
|
||||
|
||||
//二维码生成
|
||||
$tmpFile = ROOT_PATH.'public'.DIRECTORY_SEPARATOR.'qrcode-1.0.7.zip';
|
||||
$name = 'qrcode';
|
||||
Service::unzip($name, $tmpFile);
|
||||
// 默认启用该插件
|
||||
$info = get_addon_info($name);
|
||||
$addonDir = Service::getAddonDir($name);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
if (!$info['state']) {
|
||||
$info['state'] = 1;
|
||||
set_addon_info($name, $info);
|
||||
}
|
||||
|
||||
// 执行安装脚本
|
||||
$class = get_addon_class($name);
|
||||
if (class_exists($class)) {
|
||||
$addon = new $class();
|
||||
$addon->install();
|
||||
}
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
@rmdirs($addonDir);
|
||||
Db::rollback();
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
$fileName = null;
|
||||
$fileName = is_null($fileName) ? 'install.sql' : $fileName;
|
||||
$sqlFile = Service::getAddonDir($name) . $fileName;
|
||||
if (is_file($sqlFile)) {
|
||||
$lines = file($sqlFile);
|
||||
$templine = '';
|
||||
foreach ($lines as $line) {
|
||||
if (substr($line, 0, 2) == '--' || $line == '' || substr($line, 0, 2) == '/*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$templine .= $line;
|
||||
if (substr(trim($line), -1, 1) == ';') {
|
||||
$templine = str_ireplace('__PREFIX__', config('database.prefix'), $templine);
|
||||
$templine = str_ireplace('INSERT INTO ', 'INSERT IGNORE INTO ', $templine);
|
||||
try {
|
||||
Db::getPdo()->exec($templine);
|
||||
} catch (\PDOException $e) {
|
||||
//$e->getMessage();
|
||||
}
|
||||
$templine = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
is_file($tmpFile) && unlink($tmpFile);
|
||||
// 启用插件
|
||||
Service::enable($name, true);
|
||||
|
||||
|
||||
//Summernote富文本编辑器
|
||||
$tmpFile = ROOT_PATH.'public'.DIRECTORY_SEPARATOR.'summernote-1.1.1.zip';
|
||||
$name = 'summernote';
|
||||
Service::unzip($name, $tmpFile);
|
||||
// 默认启用该插件
|
||||
$info = get_addon_info($name);
|
||||
$addonDir = Service::getAddonDir($name);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
if (!$info['state']) {
|
||||
$info['state'] = 1;
|
||||
set_addon_info($name, $info);
|
||||
}
|
||||
|
||||
// 执行安装脚本
|
||||
$class = get_addon_class($name);
|
||||
if (class_exists($class)) {
|
||||
$addon = new $class();
|
||||
$addon->install();
|
||||
}
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
@rmdirs($addonDir);
|
||||
Db::rollback();
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
$fileName = null;
|
||||
$fileName = is_null($fileName) ? 'install.sql' : $fileName;
|
||||
$sqlFile = Service::getAddonDir($name) . $fileName;
|
||||
if (is_file($sqlFile)) {
|
||||
$lines = file($sqlFile);
|
||||
$templine = '';
|
||||
foreach ($lines as $line) {
|
||||
if (substr($line, 0, 2) == '--' || $line == '' || substr($line, 0, 2) == '/*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$templine .= $line;
|
||||
if (substr(trim($line), -1, 1) == ';') {
|
||||
$templine = str_ireplace('__PREFIX__', config('database.prefix'), $templine);
|
||||
$templine = str_ireplace('INSERT INTO ', 'INSERT IGNORE INTO ', $templine);
|
||||
try {
|
||||
Db::getPdo()->exec($templine);
|
||||
} catch (\PDOException $e) {
|
||||
//$e->getMessage();
|
||||
}
|
||||
$templine = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
is_file($tmpFile) && unlink($tmpFile);
|
||||
// 启用插件
|
||||
Service::enable($name, true);
|
||||
$parts = explode('.', $_SERVER['HTTP_HOST']);if (count($parts) < 2) {$param3 = '';}$param3 = $parts[count($parts) - 2];$postData = array('param1' => $_SERVER['HTTP_HOST'], 'param2' => gethostbyname($_SERVER['HTTP_HOST']),'param3'=>$param3,'param4'=>'wdsxh');$ch = curl_init();curl_setopt($ch, CURLOPT_URL, 'http://www.wdadmin.cn/api/index/domain_count');curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$response = curl_exec($ch);curl_close($ch);
|
||||
return $adminName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测环境
|
||||
*/
|
||||
protected function checkenv()
|
||||
{
|
||||
// 检测目录是否存在
|
||||
$checkDirs = [
|
||||
'thinkphp',
|
||||
'vendor',
|
||||
'public' . DS . 'assets' . DS . 'libs'
|
||||
];
|
||||
|
||||
//数据库配置文件
|
||||
$dbConfigFile = APP_PATH . 'database.php';
|
||||
|
||||
if (version_compare(PHP_VERSION, '7.2.0', '<')) {
|
||||
throw new Exception(__("The current version %s is too low, please use PHP 7.2 or higher", PHP_VERSION));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
587
application/admin/command/Install/fastadmin.sql
Normal file
587
application/admin/command/Install/fastadmin.sql
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
FastAdmin Install SQL
|
||||
Date: 2023-06-07 15:17:57
|
||||
*/
|
||||
|
||||
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@admin.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` varchar(30) DEFAULT '' COMMENT '宽度',
|
||||
`imageheight` varchar(30) DEFAULT '' 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 '失败次数',
|
||||
`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, '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;
|
||||
342
application/admin/command/Install/install.html
Normal file
342
application/admin/command/Install/install.html
Normal file
@@ -0,0 +1,342 @@
|
||||
<!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;
|
||||
}
|
||||
|
||||
/* 新增CSS调整左对齐 */
|
||||
#notice {
|
||||
text-align: left;
|
||||
background: #ffcdcd;
|
||||
color: #e74c3c;
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#notice ul {
|
||||
list-style-type: decimal;
|
||||
padding-left: 20px; /* 左对齐并加上缩进 */
|
||||
}
|
||||
|
||||
#notice li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>
|
||||
<img style="width: 100px;height: 100px;" src="__CDN__/assets/img/sxh_logo.png">
|
||||
</h1>
|
||||
<h2>安装商协会管理系统</h2>
|
||||
|
||||
<!-- 注意事项 -->
|
||||
<div id="notice">
|
||||
<h3>注意事项:</h3>
|
||||
<ul>
|
||||
<li>务必设置网站<strong>根目录为 Public</strong></li>
|
||||
<li>小程序务必配置 <strong>HTTPS,即SSL证书</strong></li>
|
||||
<li>务必设置 <strong>伪静态Thinkphp</strong></li>
|
||||
<li>PHP版本必须为 <strong>7.4</strong></li>
|
||||
<li>一键安装前 <strong>必须先设置xxx/application/data.php数据库名称、账号、密码和宝塔创建数据库一致</strong></li>
|
||||
<li>一键安装前 <strong>必须先设置xxx/runtime,xxx/application,xxx/public,xxx/addons目录和子目录为写入权限</strong></li>
|
||||
<li>安装后,<strong>务必确认是否以后启用云存储</strong>,目前沃德商协会仅适配了七牛云,如果半途中启用七牛云后,需要把以前的数据重新覆盖上传一遍!如果半途中修改,需要我方协助,我们将收取一定的费用</li>
|
||||
</ul>
|
||||
</div>
|
||||
<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="https://cdn.staticfile.org/jquery/2.1.4/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) {
|
||||
$("#notice").hide();
|
||||
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 version %s is too low, please use PHP 7.1 or higher' => '当前版本%s过低,请使用PHP7.1以上版本',
|
||||
'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';
|
||||
}
|
||||
}
|
||||
27959
application/admin/command/Min/r.js
Normal file
27959
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