Browse Source

插件:数据库管理

lizhen 1 day ago
parent
commit
fd68336f05

+ 1 - 0
addons/database/.addonrc

@@ -0,0 +1 @@
+{"files":["application\\admin\\controller\\general\\Database.php","application\\admin\\lang\\zh-cn\\general\\database.php","application\\admin\\view\\general\\database\\index.html","public\\assets\\js\\backend\\general\\database.js"],"license":"regular","licenseto":"19079","licensekey":"L7WSE2v6KbMqmdNa LM8S69k160nex4L6bmGxwA==","domains":["shequ.com"],"licensecodes":[],"validations":["f3dae4d9846343538fa9ff48f78d7f00"],"menus":["general\/database","general\/database\/index","general\/database\/query"]}

+ 63 - 0
addons/database/Database.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace addons\database;
+
+use app\common\library\Menu;
+use think\Addons;
+
+/**
+ * 数据库插件
+ */
+class Database extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+        $menu = [
+            [
+                'name'    => 'general/database',
+                'title'   => '数据库管理',
+                'icon'    => 'fa fa-database',
+                'remark'  => '可进行一些简单的数据库表优化或修复,查看表结构和数据,也可以进行SQL语句的操作',
+                'sublist' => [
+                    ['name' => 'general/database/index', 'title' => '查看'],
+                    ['name' => 'general/database/query', 'title' => '查询'],
+                ]
+            ]
+        ];
+        Menu::create($menu, 'general');
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+
+        Menu::delete('general/database');
+        return true;
+    }
+
+    /**
+     * 插件启用方法
+     */
+    public function enable()
+    {
+        Menu::enable('general/database');
+    }
+
+    /**
+     * 插件禁用方法
+     */
+    public function disable()
+    {
+        Menu::disable('general/database');
+    }
+
+}

+ 17 - 0
addons/database/config.php

@@ -0,0 +1,17 @@
+<?php
+
+return array(
+    array(
+        'name'    => '__tips__',
+        'title'   => '温馨提示',
+        'type'    => '',
+        'content' =>
+            array(),
+        'value'   => '请做好数据库离线备份工作,建议此插件仅用于开发阶段,项目正式上线建议卸载此插件',
+        'rule'    => '',
+        'msg'     => '',
+        'tip'     => '',
+        'ok'      => '',
+        'extend'  => '',
+    ),
+);

+ 16 - 0
addons/database/controller/Index.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace addons\database\controller;
+
+use think\addons\Controller;
+
+class Index extends Controller
+{
+
+    public function index()
+    {
+        $this->error("当前插件暂无前台页面");
+    }
+
+}
+

+ 10 - 0
addons/database/info.ini

@@ -0,0 +1,10 @@
+name = database
+title = 数据库管理
+intro = 数据库管理插件
+author = FastAdmin
+website = https://www.fastadmin.net
+version = 1.0.15
+state = 1
+url = /addons/database
+license = regular
+licenseto = 19079

+ 219 - 0
addons/database/library/Backup.php

@@ -0,0 +1,219 @@
+<?php
+
+namespace addons\database\library;
+
+use Exception;
+use fast\Random;
+use PDO;
+use ZipArchive;
+
+class Backup
+{
+
+    private $host = '';
+    private $user = '';
+    private $name = '';
+    private $pass = '';
+    private $port = '';
+    private $tables = ['*'];
+    private $ignoreTables = [];
+    private $db;
+    private $ds = "\n";
+
+    public function __construct($host = null, $user = null, $name = null, $pass = null, $port = 3306)
+    {
+        if ($host !== null) {
+            $this->host = $host;
+            $this->name = $name;
+            $this->port = $port;
+            $this->pass = $pass;
+            $this->user = $user;
+        }
+        $this->db = new PDO('mysql:host=' . $this->host . ';dbname=' . $this->name . '; port=' . $port, $this->user, $this->pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
+
+        $this->db->exec('SET NAMES "utf8"');
+    }
+
+    /**
+     * 设置备份表
+     * @param $table
+     * @return $this
+     */
+    public function setTable($table)
+    {
+        if ($table) {
+            $this->tables = is_array($table) ? $table : explode(',', $table);
+        }
+        return $this;
+    }
+
+    /**
+     * 设置忽略备份的表
+     * @param $table
+     * @return $this
+     */
+    public function setIgnoreTable($table)
+    {
+        if ($table) {
+            $this->ignoreTables = is_array($table) ? $table : explode(',', preg_replace('/\s+/', '', $table));
+        }
+        return $this;
+    }
+
+    public function backup($backUpdir = 'download/')
+    {
+        $sql = $this->_init();
+        $zip = new ZipArchive();
+        $date = date('YmdHis');
+        if (!is_dir($backUpdir)) {
+            @mkdir($backUpdir, 0755);
+        }
+        $name = "backup-{$this->name}-{$date}-" . Random::alnum(6);
+        $filename = $backUpdir . $name . ".zip";
+
+        if ($zip->open($filename, ZIPARCHIVE::CREATE) !== true) {
+            throw new Exception("Could not open <$filename>\n");
+        }
+        $zip->addFromString($name . ".sql", $sql);
+        $zip->close();
+    }
+
+    private function _init()
+    {
+        # COUNT
+        $ct = 0;
+        # CONTENT
+        $sqldump = '';
+        # COPYRIGHT & OPTIONS
+        $sqldump .= "-- SQL Dump by Erik Edgren\n";
+        $sqldump .= "-- version 1.0\n";
+        $sqldump .= "--\n";
+        $sqldump .= "-- SQL Dump created: " . date('F jS, Y \@ g:i a') . "\n\n";
+        $sqldump .= "SET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";";
+        $sqldump .= "\n\n\n\n-- --------------------------------------------------------\n\n\n\n";
+        $tables = $this->db->query("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
+        # LOOP: Get the tables
+        foreach ($tables as $table) {
+            // 忽略表
+            if (in_array($table[0], $this->ignoreTables)) {
+                continue;
+            }
+            # COUNT
+            $ct++;
+            /** ** ** ** ** **/
+            # DATABASE: Count the rows in each tables
+            $count_rows = $this->db->prepare("SELECT * FROM `" . $table[0] . "`");
+            $count_rows->execute();
+            $c_rows = $count_rows->columnCount();
+            # DATABASE: Count the columns in each tables
+            $count_columns = $this->db->prepare("SELECT COUNT(*) FROM `" . $table[0] . "`");
+            $count_columns->execute();
+            $c_columns = $count_columns->fetchColumn();
+            /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
+            # MYSQL DUMP: Remove tables if they exists
+            $sqldump .= "--\n";
+            $sqldump .= "-- Remove the table if it exists\n";
+            $sqldump .= "--\n\n";
+            $sqldump .= "DROP TABLE IF EXISTS `" . $table[0] . "`;\n\n\n";
+            /** ** ** ** ** **/
+            # MYSQL DUMP: Create table if they do not exists
+            $sqldump .= "--\n";
+            $sqldump .= "-- Create the table if it not exists\n";
+            $sqldump .= "--\n\n";
+            # LOOP: Get the fields for the table
+            foreach ($this->db->query("SHOW CREATE TABLE `" . $table[0] . "`") as $field) {
+                $sqldump .= str_replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS', $field['Create Table']);
+            }
+            # MYSQL DUMP: New rows
+            $sqldump .= ";\n\n\n";
+            /** ** ** ** ** **/
+            # CHECK: There are one or more columns
+            if ($c_columns != 0) {
+                # MYSQL DUMP: List the data for each table
+                $sqldump .= "--\n";
+                $sqldump .= "-- List the data for the table\n";
+                $sqldump .= "--\n\n";
+                # MYSQL DUMP: Insert into each table
+                $sqldump .= "INSERT INTO `" . $table[0] . "` (";
+                # ARRAY
+                $rows = [];
+                $numeric = [];
+                # LOOP: Get the tables
+                foreach ($this->db->query("DESCRIBE `" . $table[0] . "`") as $row) {
+                    $rows[] = "`" . $row[0] . "`";
+                    $numeric[] = (bool)preg_match('#^[^(]*(BYTE|COUNTER|SERIAL|INT|LONG$|CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER)#i', $row[1]);
+                }
+                $sqldump .= implode(', ', $rows);
+                $sqldump .= ") VALUES\n";
+                # COUNT
+                $c = 0;
+                # LOOP: Get the tables
+                foreach ($this->db->query("SELECT * FROM `" . $table[0] . "`") as $data) {
+                    # COUNT
+                    $c++;
+                    /** ** ** ** ** **/
+                    $sqldump .= "(";
+                    # ARRAY
+                    $cdata = [];
+                    # LOOP
+                    for ($i = 0; $i < $c_rows; $i++) {
+                        $value = $data[$i];
+
+                        if (is_null($value)) {
+                            $cdata[] = "NULL";
+                        } elseif ($numeric[$i]) {
+                            $cdata[] = $value;
+                        } else {
+                            $cdata[] = $this->db->quote($value);
+                        }
+                    }
+                    $sqldump .= implode(', ', $cdata);
+                    $sqldump .= ")";
+                    $sqldump .= ($c % 600 != 0 ? ($c_columns != $c ? ',' : ';') : '');
+                    # CHECK
+                    if ($c % 600 == 0) {
+                        $sqldump .= ";\n\n";
+                    } else {
+                        $sqldump .= "\n";
+                    }
+                    # CHECK
+                    if ($c % 600 == 0) {
+                        $sqldump .= "INSERT INTO `" . $table[0] . "`(";
+                        # ARRAY
+                        $rows = [];
+                        # LOOP: Get the tables
+                        foreach ($this->db->query("DESCRIBE `" . $table[0] . "`") as $row) {
+                            $rows[] = "`" . $row[0] . "`";
+                        }
+                        $sqldump .= implode(', ', $rows);
+                        $sqldump .= ") VALUES\n";
+                    }
+                }
+            }
+        }
+
+        $sqldump .= "\n\n\n";
+        // Backup views
+        $tables = $this->db->query("SHOW FULL TABLES WHERE Table_Type = 'VIEW'");
+        # LOOP: Get the tables
+        foreach ($tables as $table) {
+            // 忽略表
+            if (in_array($table[0], $this->ignoreTables)) {
+                continue;
+            }
+            foreach ($this->db->query("SHOW CREATE VIEW `" . $table[0] . "`") as $field) {
+                $sqldump .= "--\n";
+                $sqldump .= "-- Remove the view if it exists\n";
+                $sqldump .= "--\n\n";
+                $sqldump .= "DROP VIEW IF EXISTS `{$field[0]}`;\n\n";
+                $sqldump .= "--\n";
+                $sqldump .= "-- Create the view if it not exists\n";
+                $sqldump .= "--\n\n";
+                $sqldump .= "{$field[1]};\n\n";
+            }
+        }
+        return $sqldump;
+
+    }
+
+}

+ 204 - 0
application/admin/controller/general/Database.php

@@ -0,0 +1,204 @@
+<?php
+
+namespace app\admin\controller\general;
+
+use addons\database\library\Backup;
+use app\common\controller\Backend;
+use think\Db;
+use think\Debug;
+use think\Exception;
+use think\exception\PDOException;
+use ZipArchive;
+
+/**
+ * 数据库管理
+ *
+ * @icon   fa fa-database
+ * @remark 可在线进行一些简单的数据库表优化或修复,查看表结构和数据。也可以进行SQL语句的操作
+ */
+class Database extends Backend
+{
+    protected $noNeedRight = ['backuplist'];
+
+    public function _initialize()
+    {
+        if (!config("app_debug")) {
+            $this->error("数据库管理插件只允许在开发环境下使用");
+        }
+        return parent::_initialize();
+    }
+
+    /**
+     * 查看
+     */
+    public function index()
+    {
+        $tables_data_length = $tables_index_length = $tables_free_length = $tables_data_count = 0;
+        $tables = $list = [];
+        $list = Db::query("SHOW TABLES");
+        foreach ($list as $key => $row) {
+            $tables[] = ['name' => reset($row), 'rows' => 0];
+        }
+        $data['tables'] = $tables;
+        $data['saved_sql'] = [];
+        $this->view->assign($data);
+        return $this->view->fetch();
+    }
+
+    /**
+     * SQL查询
+     */
+    public function query()
+    {
+        $do_action = $this->request->post('do_action');
+
+        echo '<style type="text/css">
+            xmp,body{margin:0;padding:0;line-height:18px;font-size:13px;font-family:"Helvetica Neue", Helvetica, Microsoft Yahei, Hiragino Sans GB, WenQuanYi Micro Hei, sans-serif;}
+            hr{height:1px;margin:5px 1px;background:#e3e3e3;border:none;}
+            </style>';
+        if ($do_action == '') {
+            exit(__('Invalid parameters'));
+        }
+
+        $tablename = $this->request->post("tablename/a", []);
+
+        if (in_array($do_action, array('doquery', 'optimizeall', 'repairall'))) {
+            $this->$do_action();
+        } elseif (count($tablename) == 0) {
+            exit(__('请选中表后再进行操作'));
+        } else {
+            foreach ($tablename as $table) {
+                $this->$do_action($table);
+                echo "<br />";
+            }
+        }
+    }
+
+    private function viewinfo($name)
+    {
+        $row = Db::query("SHOW CREATE TABLE `{$name}`");
+        $row = array_values($row[0]);
+        $info = $row[1];
+        echo "<xmp>{$info};</xmp>";
+    }
+
+    private function viewdata($name = '')
+    {
+        $sqlquery = "SELECT * FROM `{$name}`";
+        $this->doquery($sqlquery);
+    }
+
+    private function optimize($name = '')
+    {
+        if (Db::execute("OPTIMIZE TABLE `{$name}`")) {
+            echo __('Optimize table %s done', $name);
+        } else {
+            echo __('Optimize table %s fail', $name);
+        }
+    }
+
+    private function optimizeall($name = '')
+    {
+        $list = Db::query("SHOW TABLES");
+        foreach ($list as $key => $row) {
+            $name = reset($row);
+            if (Db::execute("OPTIMIZE TABLE {$name}")) {
+                echo __('Optimize table %s done', $name);
+            } else {
+                echo __('Optimize table %s fail', $name);
+            }
+            echo "<br />";
+        }
+    }
+
+    private function repair($name = '')
+    {
+        if (Db::execute("REPAIR TABLE `{$name}`")) {
+            echo __('Repair table %s done', $name);
+        } else {
+            echo __('Repair table %s fail', $name);
+        }
+    }
+
+    private function repairall($name = '')
+    {
+        $list = Db::query("SHOW TABLES");
+        foreach ($list as $key => $row) {
+            $name = reset($row);
+            if (Db::execute("REPAIR TABLE {$name}")) {
+                echo __('Repair table %s done', $name);
+            } else {
+                echo __('Repair table %s fail', $name);
+            }
+            echo "<br />";
+        }
+    }
+
+    private function doquery($sql = null)
+    {
+        $sqlquery = $sql ? $sql : $this->request->post('sqlquery');
+        if ($sqlquery == '') {
+            exit(__('SQL can not be empty'));
+        }
+        $sqlquery = str_replace('__PREFIX__', config('database.prefix'), $sqlquery);
+        $sqlquery = str_replace("\r", "", $sqlquery);
+        $sqls = preg_split("/;[ \t]{0,}\n/i", $sqlquery);
+        $maxreturn = 100;
+        $r = '';
+        foreach ($sqls as $key => $val) {
+            if (trim($val) == '') {
+                continue;
+            }
+            $val = rtrim($val, ';');
+            $r .= "SQL:<span style='color:green;'>{$val}</span> ";
+            if (preg_match("/^(select|explain)(.*)/i ", $val)) {
+                Debug::remark("begin");
+                $limit = stripos(strtolower($val), "limit") !== false ? true : false;
+                try {
+                    $count = Db::execute($val);
+                    if ($count > 0) {
+                        $resultlist = Db::query($val . (!$limit && $count > $maxreturn ? ' LIMIT ' . $maxreturn : ''));
+                    } else {
+                        $resultlist = [];
+                    }
+                } catch (\PDOException $e) {
+                    continue;
+                }
+                Debug::remark("end");
+                $time = Debug::getRangeTime('begin', 'end', 4);
+
+                $usedseconds = __('Query took %s seconds', $time) . "<br />";
+                if ($count <= 0) {
+                    $r .= __('Query returned an empty result');
+                } else {
+                    $r .= (__('Total:%s', $count) . (!$limit && $count > $maxreturn ? ',' . __('Max output:%s', $maxreturn) : ""));
+                }
+                $r = $r . ',' . $usedseconds;
+                $j = 0;
+                foreach ($resultlist as $m => $n) {
+                    $j++;
+                    if (!$limit && $j > $maxreturn) {
+                        break;
+                    }
+                    $r .= "<hr/>";
+                    $r .= "<font color='red'>" . __('Row:%s', $j) . "</font><br />";
+                    foreach ($n as $k => $v) {
+                        $r = $r . "<font color='blue'>" . htmlentities($k) . ":</font>" . htmlentities($v) . "<br/>\r\n";
+                    }
+                }
+            } else {
+                try {
+                    Debug::remark("begin");
+                    $count = Db::getPdo()->exec($val);
+                    Debug::remark("end");
+
+                } catch (\PDOException $e) {
+                    continue;
+                }
+                $time = Debug::getRangeTime('begin', 'end', 4);
+                $r .= __('Query affected %s rows and took %s seconds', $count, $time) . "<br />";
+            }
+        }
+        echo $r;
+    }
+}

+ 43 - 0
application/admin/lang/zh-cn/general/database.php

@@ -0,0 +1,43 @@
+<?php
+
+return [
+    'SQL Result'                                                             => '查询结果',
+    'Basic query'                                                            => '基础查询',
+    'View structure'                                                         => '查看表结构',
+    'View data'                                                              => '查看表数据',
+    'Backup and Restore'                                                     => '备份与还原',
+    'Backup now'                                                             => '立即备份',
+    'File'                                                                   => '文件',
+    'Size'                                                                   => '大小',
+    'Date'                                                                   => '备份日期',
+    'Restore'                                                                => '还原',
+    'Delete'                                                                 => '删除',
+    'Optimize'                                                               => '优化表',
+    'Repair'                                                                 => '修复表',
+    'Optimize all'                                                           => '优化全部表',
+    'Repair all'                                                             => '修复全部表',
+    'Backup successful'                                                      => '备份成功',
+    'Restore successful'                                                     => '还原成功',
+    'Delete successful'                                                      => '删除成功',
+    'Can not open zip file'                                                  => '无法打开备份文件',
+    'Can not unzip file'                                                     => '无法解压备份文件',
+    'Sql file not found'                                                     => '未找到SQL文件',
+    'Table:%s'                                                               => '总计:%s个表',
+    'Record:%s'                                                              => '记录:%s条',
+    'Data:%s'                                                                => '占用:%s',
+    'Index:%s'                                                               => '索引:%s',
+    'SQL Result:'                                                            => '查询结果:',
+    'SQL can not be empty'                                                   => 'SQL语句不能为空',
+    'Max output:%s'                                                          => '最大返回%s条',
+    'Total:%s'                                                               => '共有%s条记录! ',
+    'Row:%s'                                                                 => '记录:%s',
+    'Executes one or multiple queries which are concatenated by a semicolon' => '请输入SQL语句,支持批量查询,多条SQL以分号(;)分格',
+    'Query affected %s rows and took %s seconds'                             => '共影响%s条记录! 耗时:%s秒!',
+    'Query returned an empty result'                                         => '返回结果为空!',
+    'Query took %s seconds'                                                  => '耗时%s秒!',
+    'Optimize table %s done'                                                 => '优化表[%s]成功',
+    'Repair table %s done'                                                   => '修复表[%s]成功',
+    'Optimize table %s fail'                                                 => '优化表[%s]失败',
+    'Repair table %s fail'                                                   => '修复表[%s]失败'
+];
+

+ 101 - 0
application/admin/view/general/database/index.html

@@ -0,0 +1,101 @@
+<style type="text/css">
+    #searchfloat {position:absolute;top:40px;right:20px;background:#F7F0A0;padding:10px;}
+    #saved {position: relative;}
+    #saved_sql {position:absolute;bottom:0;height:300px;background:#F7F0A0;width:100%;overflow:auto;display:none;}
+    #saved_sql li {display:block;clear:both;width:100%;float:left;line-height:18px;padding:1px 0}
+    #saved_sql li a{float:left;text-decoration: none;display:block;padding:0 5px;}
+    #saved_sql li i{display:none;float:left;color:#06f;font-size: 14px;font-style: normal;margin-left:2px;line-height:18px;}
+    #saved_sql li:hover{background:#fff;}
+    #saved_sql li:hover i{display:block;cursor:pointer;}
+    #database #tablename {height:205px;width:100%;padding:5px;}
+    #database #tablename option{height:18px;}
+    #database #subaction {height:210px;width:100%;}
+    #database .select-striped > option:nth-of-type(odd) {background-color: #f9f9f9;}
+    #database .dropdown-menu ul {margin:-3px 0;}
+    #database .dropdown-menu ul li{margin:3px 0;}
+    #database .dropdown-menu.row .col-xs-6{padding:0 5px;}
+    #sqlquery {color:#444;}
+    #resultparent {padding:5px; height:calc(100vh - 350px);}
+</style>
+<style data-render="darktheme">
+    body.darktheme #database .select-striped > option:nth-of-type(odd) {
+        background-color: #262626;
+    }
+    body.darktheme #tablename::-webkit-scrollbar {
+        width: 9px;
+        height: 9px;
+    }
+    body.darktheme #tablename::-webkit-scrollbar-thumb {
+        background: #999;
+    }
+    body.darktheme #tablename::-webkit-scrollbar-track {
+        background: #333;
+    }
+    body.darktheme #sqlquery {
+        color: #ccc;
+    }
+</style>
+<div class="panel panel-default panel-intro">
+    {:build_heading()}
+
+    <div class="panel-body">
+        <div id="database" class="tab-content">
+            <div class="tab-pane fade active in" id="one">
+                <div class="widget-body no-padding">
+                    {if $auth->check('general/database/query')}
+                    <div class="row">
+                        <div class="col-xs-4">
+                            <h4>{:__('SQL Result')}:</h4>
+                        </div>
+                        <div class="col-xs-8 text-right">
+                            <form action="{:url('general.database/query')}" method="post" name="infoform" target="resultframe">
+                                <input type="hidden" name="do_action" id="topaction" />
+                                <div class="btn-group">
+                                    <button data-toggle="dropdown" class="btn btn-primary btn-embossed dropdown-toggle" type="button">{:__('Basic query')} <span class="caret"></span></button>
+                                    <div class="row dropdown-menu pull-right" style="width:300px;">
+                                        <div class="col-xs-6">
+                                            <select class="form-control select-striped" id="tablename" name="tablename[]" multiple="multiple">
+                                                {foreach $tables as $table}
+                                                <option value="{$table.name}" title="">{$table.name}<!--({$table.rows})--></option>
+                                                {/foreach}
+                                            </select>
+                                        </div>
+                                        <div class="col-xs-6">
+                                            <ul id="subaction" class="list-unstyled">
+                                                <li><input type="submit" name="submit1" value="{:__('View structure')}" rel="viewinfo" class="btn btn-primary btn-embossed btn-sm btn-block"/></li>
+                                                <li><input type="submit" name="submit2" value="{:__('View data')}" rel="viewdata" class="btn btn-primary btn-embossed btn-sm btn-block"/></li>
+                                                <li><input type="submit" name="submit3" value="{:__('Optimize')}" rel="optimize" class="btn btn-primary btn-embossed btn-sm btn-block" /></li>
+                                                <li><input type="submit" name="submit4" value="{:__('Repair')}" rel="repair" class="btn btn-primary btn-embossed btn-sm btn-block"/></li>
+                                                <li><input type="submit" name="submit5" value="{:__('Optimize all')}" rel="optimizeall" class="btn btn-primary btn-embossed btn-sm btn-block" /></li>
+                                                <li><input type="submit" name="submit6" value="{:__('Repair all')}" rel="repairall" class="btn btn-primary btn-embossed btn-sm btn-block" /></li>
+                                            </ul>
+                                        </div>
+                                        <div class="clear"></div>
+                                    </div>
+
+                                </div>
+                            </form>
+                        </div>
+
+                    </div>
+                    <div class="well" id="resultparent">
+                        <iframe name="resultframe" frameborder="0" id="resultframe" style="height:100%;" width="100%" height="100%"></iframe>
+                    </div>
+                    <form action="{:url('general.database/query')}" method="post" id="sqlexecute" name="form1" target="resultframe">
+                        <input type="hidden" name="do_action" value="doquery" />
+                        <div class="form-group">
+                            <textarea name="sqlquery" placeholder="{:__('Executes one or multiple queries which are concatenated by a semicolon')}" cols="60" rows="5" class="form-control" id="sqlquery"></textarea>
+                        </div>
+
+                        <button type="submit" class="btn btn-primary btn-embossed"><i class="fa fa-check"></i> {:__('Execute')}</button>
+                        <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+                    </form>
+                    {else /}
+                    <div id="backuplist"></div>
+                    {/if}
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 39 - 0
public/assets/js/backend/general/database.js

@@ -0,0 +1,39 @@
+define(['jquery', 'bootstrap', 'backend', 'template'], function ($, undefined, Backend, Template) {
+
+    var Controller = {
+        index: function () {
+
+            //禁止在操作select元素时关闭dropdown的关闭事件
+            $("#database").on('click', '.dropdown-menu input, .dropdown-menu label, .dropdown-menu select', function (e) {
+                e.stopPropagation();
+            });
+
+            //提交时检查是否有删除或清空操作
+            $("#database").on("submit", "#sqlexecute", function () {
+                var v = $("#sqlquery").val().toLowerCase();
+                if ((v.indexOf("delete ") >= 0 || v.indexOf("truncate ") >= 0) && !confirm(__('Are you sure you want to delete or turncate?'))) {
+                    return false;
+                }
+            });
+
+            //事件按钮操作
+            $("#database").on("click", "ul#subaction li input", function () {
+                $("#topaction").val($(this).attr("rel"));
+                return true;
+            });
+
+            //窗口变更的时候重设结果栏高度
+            $(window).on("resize", function () {
+                $("#database .well").height($(window).height() - $("#database #sqlexecute").height() - $("#ribbon").outerHeight() - $(".panel-heading").outerHeight() - 130);
+            });
+
+            //修复iOS下iframe无法滚动的BUG
+            if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
+                $("#resultparent").css({"-webkit-overflow-scrolling": "touch", "overflow": "auto"});
+            }
+
+            $(window).resize();
+        }
+    };
+    return Controller;
+});