浏览代码

插件:Mysql数据库管理

lizhen 1 天之前
父节点
当前提交
95e92085c2
共有 32 个文件被更改,包括 5793 次插入0 次删除
  1. 0 0
      addons/famysql/.addonrc
  2. 82 0
      addons/famysql/Famysql.php
  3. 31 0
      addons/famysql/config.php
  4. 15 0
      addons/famysql/controller/Index.php
  5. 1079 0
      addons/famysql/data/fields.php
  6. 126 0
      addons/famysql/data/menu.php
  7. 628 0
      addons/famysql/data/tables.ini
  8. 10 0
      addons/famysql/info.ini
  9. 204 0
      addons/famysql/library/Backup.php
  10. 719 0
      application/admin/controller/famysql/Field.php
  11. 365 0
      application/admin/controller/famysql/Index.php
  12. 910 0
      application/admin/controller/famysql/Table.php
  13. 22 0
      application/admin/lang/zh-cn/famysql/field.php
  14. 8 0
      application/admin/lang/zh-cn/famysql/index.php
  15. 50 0
      application/admin/lang/zh-cn/famysql/table.php
  16. 19 0
      application/admin/view/famysql/field/create.html
  17. 114 0
      application/admin/view/famysql/field/field_add.html
  18. 92 0
      application/admin/view/famysql/field/field_edit.html
  19. 29 0
      application/admin/view/famysql/field/fields.html
  20. 34 0
      application/admin/view/famysql/index/index_add.html
  21. 34 0
      application/admin/view/famysql/index/index_edit.html
  22. 25 0
      application/admin/view/famysql/index/indexs.html
  23. 39 0
      application/admin/view/famysql/table/backup.html
  24. 41 0
      application/admin/view/famysql/table/backuplist.html
  25. 35 0
      application/admin/view/famysql/table/index.html
  26. 67 0
      application/admin/view/famysql/table/table_add.html
  27. 35 0
      application/admin/view/famysql/table/table_batch_add.html
  28. 52 0
      application/admin/view/famysql/table/table_edit.html
  29. 1 0
      application/extra/addons.php
  30. 352 0
      public/assets/js/backend/famysql/field.js
  31. 99 0
      public/assets/js/backend/famysql/index.js
  32. 476 0
      public/assets/js/backend/famysql/table.js

文件差异内容过多而无法显示
+ 0 - 0
addons/famysql/.addonrc


+ 82 - 0
addons/famysql/Famysql.php

@@ -0,0 +1,82 @@
+<?php
+namespace addons\famysql;
+
+use app\common\library\Menu;
+use app\admin\model\AuthRule;
+use think\Addons;
+
+/**
+ * Famysql插件
+ */
+class Famysql extends Addons
+{
+
+    /**
+     * 插件安装方法
+     * @return bool
+     */
+    public function install()
+    {
+        $menu = [];
+        $config_file = ADDON_PATH . "famysql" . DS . 'data' . DS . "menu.php";
+        if (is_file($config_file)) {
+            $menu = include $config_file;
+        }
+        if ($menu) {
+            Menu::create($menu);
+        }
+        return true;
+    }
+
+    /**
+     * 插件卸载方法
+     * @return bool
+     */
+    public function uninstall()
+    {
+        AuthRule::destroy($this->getIds());
+        return true;
+    }
+
+    /**
+     * 插件启用方法
+     */
+    public function enable()
+    {
+        AuthRule::where('id', 'in', $this->getIds())->update(['status' => 'normal']);
+        return true;
+    }
+
+    /**
+     * 插件禁用方法
+     */
+    public function disable()
+    {
+        AuthRule::where('id', 'in', $this->getIds())->update(['status' => 'hidden']);
+        return true;
+    }
+
+    /**
+     * 插件升级方法
+     */
+    public function upgrade()
+    {
+        AuthRule::destroy($this->getIds());
+        $menu = [];
+        $config_file = ADDON_PATH . "famysql" . DS . 'data' . DS . "menu.php";
+        if (is_file($config_file)) {
+            $menu = include $config_file;
+        }
+        if ($menu) {
+            Menu::create($menu);
+            AuthRule::where('id', 'in', $this->getIds())->update(['status' => 'hidden']);
+        }
+        return true;
+    }
+
+    private function getIds()
+    {
+        $ids = AuthRule::where('name', 'like', "famysql%")->column('id');
+        return $ids;
+    }
+}

+ 31 - 0
addons/famysql/config.php

@@ -0,0 +1,31 @@
+<?php
+
+return [
+    [
+        'name' => 'is_admin',
+        'title' => '是否允许管理系统级数据表',
+        'type' => 'radio',
+        'content' => [
+            1 => '是',
+            0 => '否',
+        ],
+        'value' => '0',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '如果允许,请注意操作安全,操作错误有可能造成系统崩溃',
+        'ok' => '',
+        'extend' => '',
+    ],
+    [
+        'name' => '__tips__',
+        'title' => '温馨提示',
+        'type' => '',
+        'content' => [],
+        'value' => '为了确保系统运行稳定性,默认关闭系统级数据表管理',
+        'rule' => '',
+        'msg' => '',
+        'tip' => '',
+        'ok' => '',
+        'extend' => '',
+    ],
+];

+ 15 - 0
addons/famysql/controller/Index.php

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

+ 1079 - 0
addons/famysql/data/fields.php

@@ -0,0 +1,1079 @@
+<?php
+
+return [
+    'address' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '详细地址',
+    ],
+    'address_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '地址ID',
+    ],
+    'address_name' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 0,
+        'comment' => '地址名称',
+    ],
+    'admin_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '管理员ID',
+    ],
+    'admin_ids' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'comment' => '管理员ID(多选)',
+    ],
+    'after' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '变动后',
+    ],
+    'amount' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '商品结算金额',
+    ],
+    'area_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0,' . "\n",
+        'is_null' => 0,
+        'comment' => '详细地址',
+    ],
+    'avatar' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'is_null' => 0,
+        'comment' => '头像',
+    ],
+    'bank_name' => [
+        'type' => 'varchar',
+        'length' => 191,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '银行名',
+    ],
+    'before' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '变动前',
+    ],
+    'bio' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 1,
+        'comment' => '店铺简介',
+    ],
+    'brand_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 1,
+        'unsigned' => 1,
+        'comment' => '品牌ID',
+    ],
+    'buyer_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'is_null' => 0,
+        'comment' => '买家',
+    ],
+    'card_no' => [
+        'type' => 'varchar',
+        'length' => 191,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '卡号',
+    ],
+    'category_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '分类ID',
+    ],
+    'category_ids' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'comment' => '分类ID(多选)',
+    ],
+    'city' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '市',
+    ],
+    'code' => [
+        'type' => 'varchar',
+        'length' => 60,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '编码',
+    ],
+    'color' => [
+        'type' => 'varchar',
+        'length' => 10,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '#dbb29d',
+        'is_null' => 0,
+        'comment' => '用户背景色',
+    ],
+    'comment' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '评论',
+    ],
+    'comment_status' => [
+        'type' => 'tinyint',
+        'length' => 2,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '评论状态',
+    ],
+    'comment_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '评论ID',
+    ],
+    'confirmtime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '确认时间',
+    ],
+    'content' => [
+        'type' => 'text',
+        'is_null' => 1,
+        'comment' => '内容',
+    ],
+    'country' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '国家',
+    ],
+    'coupon_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '原始优惠券ID',
+    ],
+    'coupon_no' => [
+        'type' => 'varchar',
+        'length' => 16,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 0,
+        'comment' => '优惠券编码',
+    ],
+    'coupon_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '优惠券金额',
+    ],
+    'cover' => [
+        'type' => 'text',
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 1,
+        'comment' => '封面图',
+    ],
+    'createtime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '创建时间',
+    ],
+    'data' => [
+        'type' => 'text',
+        'is_null' => 0,
+        'comment' => '数据',
+    ],
+    'date' => [
+        'type' => 'varchar',
+        'length' => 30,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '签到日期',
+    ],
+    'dealtime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '成交时间(评论时间)',
+    ],
+    'default' => [
+        'type' => 'enum',
+        'length' => [
+            '0',
+            '1',
+        ],
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '默认:1=是,0=否',
+    ],
+    'deletetime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '删除时间',
+    ],
+    'description' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '描述',
+    ],
+    'discount' => [
+        'type' => 'decimal',
+        'length' => 2,
+        'default' => '0.0',
+        'is_null' => 1,
+        'unsigned' => 1,
+        'comment' => '折扣率',
+    ],
+    'discount_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '优惠价格',
+    ],
+    'edu' => [
+        'type' => 'enum',
+        'length' => ['0', '1', '2', '3', '4', '5', '6'],
+        'is_null' => 1,
+        'comment' => '学历:0=小学文化,1=初中,2=高中,3=大专,4=本科,5=硕士,6=博士',
+    ],
+    'enddate' => [
+        'type' => 'date',
+        'is_null' => 1,
+        'comment' => '结束日期',
+    ],
+    'endtime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '结束时间',
+    ],
+    'expire_day' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => 'X天过期',
+    ],
+    'expire_in' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '过期周期(s)',
+    ],
+    'expire_time' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '截至日期',
+    ],
+    'flag' => [
+        'type' => 'set',
+        'length' => [
+            'hot',
+            'new',
+            'recommend',
+        ],
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 1,
+        'comment' => '标志',
+    ],
+    'follow' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 1,
+        'comment' => '关注',
+    ],
+    'goods_amount' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '商品总价',
+    ],
+    'goods_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'is_null' => 0,
+        'comment' => '所属产品',
+    ],
+    'goods_ids' => [
+        'type' => 'varchar',
+        'length' => 1200,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '商品组',
+    ],
+    'goods_image' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '商品图片',
+    ],
+    'goods_original_amount' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '商品原价',
+    ],
+    'goods_original_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '商品原价',
+    ],
+    'goods_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '商品价格',
+    ],
+    'goods_sku_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '订单ID',
+    ],
+    'goods_sku_ids' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => 'NULL,' . "\n",
+        'is_null' => 1,
+        'comment' => '排序',
+    ],
+    'goods_sku_price_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '商品规格',
+    ],
+    'goods_sku_sn' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 0,
+        'comment' => '商品编码',
+    ],
+    'goods_sku_text' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '中文规格',
+    ],
+    'goods_title' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '商品名称',
+    ],
+    'group' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '分组',
+    ],
+    'height' => [
+        'type' => 'int',
+        'length' => 5,
+        'default' => '0',
+        'is_null' => 1,
+        'unsigned' => 1,
+        'comment' => '高度',
+    ],
+    'image' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 0,
+        'comment' => '图片',
+    ],
+    'images' => [
+        'type' => 'varchar',
+        'length' => 512,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '图片',
+    ],
+    'invalid' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 1,
+        'unsigned' => 1,
+        'comment' => '是否失效',
+    ],
+    'ip' => [
+        'type' => 'varchar',
+        'length' => 20,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '客户端IP',
+    ],
+    'is_default' => [
+        'type' => 'enum',
+        'length' => [
+            '0',
+            '1',
+        ],
+        'default' => '0',
+        'is_null' => 1,
+        'comment' => '默认',
+    ],
+    'is_check' => [
+        'type' => 'int',
+        'length' => 1,
+        'default' => '0',
+        'is_null' => 1,
+        'comment' => '监控状态',
+    ],
+    'is_nav' => [
+        'type' => 'tinyint',
+        'length' => 1,
+        'default' => '1',
+        'is_null' => 0,
+        'comment' => '是否导航显示',
+    ],
+    'keywords' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 0,
+        'comment' => '关键字',
+    ],
+    'lat' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '纬度',
+    ],
+    'likes' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '收藏',
+    ],
+    'logo' => [
+        'type' => 'varchar',
+        'length' => 500,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => null,
+        'is_null' => 1,
+        'comment' => 'Logo',
+    ],
+    'lng' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '经度',
+    ],
+    'memo' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '备注',
+    ],
+    'message' => [
+        'type' => 'text',
+        'is_null' => 1,
+        'comment' => '消息',
+    ],
+    'mobile' => [
+        'type' => 'varchar',
+        'length' => 20,
+        'is_null' => 0,
+        'comment' => '手机号',
+    ],
+    'money' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '金额',
+    ],
+    'name' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 0,
+        'comment' => '名称',
+    ],
+    'nickname' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '昵称',
+    ],
+    'notice' => [
+        'type' => 'text',
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 1,
+        'comment' => '通知内容',
+    ],
+    'num' => [
+        'type' => 'int',
+        'length' => 11,
+        'is_null' => 0,
+        'comment' => '数量',
+    ],
+    'order_goods_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '订单商品ID',
+    ],
+    'order_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '订单',
+    ],
+    'order_money' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '订单金额',
+    ],
+    'order_no' => [
+        'type' => 'varchar',
+        'length' => 18,
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 0,
+        'comment' => '订单号',
+    ],
+    'order_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '订单金额',
+    ],
+    'order_sn' => [
+        'type' => 'varchar',
+        'length' => 60,
+        'is_null' => 0,
+        'comment' => '订单号',
+    ],
+    'order_status' => [
+        'type' => 'tinyint',
+        'length' => 2,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '订单状态',
+    ],
+    'origin_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '原价',
+    ],
+    'out_trade_no' => [
+        'type' => 'varchar',
+        'length' => 32,
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 0,
+        'comment' => '交易号',
+    ],
+    'pay_fee' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '实际支付金额',
+    ],
+    'pay_id' => [
+        'type' => 'text',
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 0,
+        'comment' => '支付订单号',
+    ],
+    'pay_no' => [
+        'type' => 'varchar',
+        'length' => 32,
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 0,
+        'comment' => '交易号',
+    ],
+    'pay_price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '支付金额',
+    ],
+    'pay_state' => [
+        'type' => 'enum',
+        'length' => [
+            '0',
+            '1',
+            '2',
+        ],
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '支付状态',
+    ],
+    'pay_type' => [
+        'type' => 'enum',
+        'length' => [
+            'wechat',
+            'alipay',
+            'wallet',
+            'score',
+        ],
+        'is_null' => 1,
+        'comment' => '支付方式:wechat=微信支付,alipay=支付宝,wallet=钱包支付,score=积分支付',
+    ],
+    'payment_json' => [
+        'type' => 'varchar',
+        'length' => 2500,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '交易原始数据',
+    ],
+    'pay_time' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '支付时间',
+    ],
+    'pid' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 1,
+        'comment' => '上级',
+    ],
+    'platform' => [
+        'type' => 'set',
+        'length' => [
+            'H5',
+            'wxOfficialAccount',
+            'wxMiniProgram',
+            'App',
+            'preview',
+        ],
+        'is_null' => 1,
+        'comment' => '适用平台:H5=H5,wxOfficialAccount=微信公众号网页,wxMiniProgram=微信小程序,App=App,preview=预览',
+    ],
+    'price' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '价格',
+    ],
+    'province' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '省',
+    ],
+    'real_name' => [
+        'type' => 'varchar',
+        'length' => 191,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '真实姓名',
+    ],
+    'reason' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '售后原因',
+    ],
+    'remark' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '备注',
+    ],
+    'score' => [
+        'type' => 'float',
+        'length' => 3,
+        'default' => '0.0',
+        'is_null' => 1,
+        'unsigned' => 1,
+        'comment' => '积分',
+    ],
+    'score_amount' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '积分总数',
+    ],
+    'score_fee' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '积分支付数',
+    ],
+    'secret' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => null,
+        'is_null' => 1,
+        'comment' => 'Secret',
+    ],
+    'sex' => [
+        'type' => 'enum',
+        'length' => ['0', '1'],
+        'is_null' => 1,
+        'comment' => '性别:0=男,1=女',
+    ],
+    'shop_id' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '店铺ID',
+    ],
+    'shop_name' => [
+        'type' => 'varchar',
+        'length' => 32,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 0,
+        'comment' => '店铺名称',
+    ],
+    'sku_price_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '规格',
+    ],
+    'sn' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '货号',
+    ],
+    'spec' => [
+        'type' => 'enum',
+        'length' => [
+            'single',
+            'multi',
+        ],
+        'collate' => 'utf8mb4_general_ci',
+        'default' => 'single',
+        'is_null' => 0,
+        'comment' => '商品规格:single=单规格,multi=多规格',
+    ],
+    'startdate' => [
+        'type' => 'date',
+        'is_null' => 1,
+        'comment' => '开始日期',
+    ],
+    'starttime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '开始时间',
+    ],
+    'state' => [
+        'type' => 'enum',
+        'length' => [
+            '0',
+            '1',
+        ],
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '1',
+        'is_null' => 0,
+        'comment' => '状态:0=隐藏,1=显示',
+    ],
+    'status' => [
+        'type' => 'varchar',
+        'length' => 20,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '状态',
+    ],
+    'stock' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '库存',
+    ],
+    'store_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '门店',
+    ],
+    'store_ids' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'is_null' => 0,
+        'comment' => '包含门店',
+    ],
+    'subscribe' => [
+        'type' => 'enum',
+        'length' => [
+            '0',
+            '1',
+        ],
+        'is_null' => 1,
+        'comment' => '关注状态:0=取消关注,1=已关注',
+    ],
+    'subscribe_time' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '关注时间',
+    ],
+    'switch' => [
+        'type' => 'tinyint',
+        'length' => 1,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '开关',
+    ],
+    'thumbnail' => [
+        'type' => 'text',
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 1,
+        'comment' => '缩略图',
+    ],
+    'tip' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'default' => '',
+        'is_null' => 0,
+        'comment' => '变量描述',
+    ],
+    'title' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '活动名称',
+    ],
+    'total_amount' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '订单总金额',
+    ],
+    'total_fee' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '支付金额',
+    ],
+    'total_income' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'default' => '0.00',
+        'is_null' => 0,
+        'comment' => '总收益',
+    ],
+    'trade_no' => [
+        'type' => 'varchar',
+        'length' => 32,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '',
+        'is_null' => 1,
+        'comment' => '交易订单号',
+    ],
+    'transaction_id' => [
+        'type' => 'varchar',
+        'length' => 60,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '交易单号',
+    ],
+    'type' => [
+        'type' => 'varchar',
+        'length' => 20,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '类型',
+    ],
+    'updatetime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '更新时间',
+    ],
+    'url' => [
+        'type' => 'varchar',
+        'length' => 255,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '商品链接',
+    ],
+    'user_id' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '会员ID',
+    ],
+    'user_ids' => [
+        'type' => 'varchar',
+        'length' => 100,
+        'comment' => '会员ID(多选)',
+    ],
+    'user_nickname' => [
+        'type' => 'varchar',
+        'length' => 50,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '用户昵称',
+    ],
+    'user_no' => [
+        'type' => 'varchar',
+        'length' => 16,
+        'collate' => 'utf8mb4_general_ci',
+        'default' => '0',
+        'is_null' => 0,
+        'comment' => '账户号',
+    ],
+    'usetime' => [
+        'type' => 'bigint',
+        'length' => 16,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '使用时间',
+    ],
+    'uuid' => [
+        'type' => 'varchar',
+        'length' => 36,
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 0,
+        'comment' => 'app唯一编码',
+    ],
+    'value' => [
+        'type' => 'longtext',
+        'collate' => 'utf8mb4_general_ci',
+        'is_null' => 1,
+        'comment' => '配置:key=名称,value=值2',
+    ],
+    'views' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 0,
+        'unsigned' => 1,
+        'comment' => '点击',
+    ],
+    'wallet' => [
+        'type' => 'decimal',
+        'length' => '10,2',
+        'is_null' => 0,
+        'comment' => '变动金额',
+    ],
+    'wallet_type' => [
+        'type' => 'enum',
+        'length' => [
+            'money',
+            'score',
+        ],
+        'is_null' => 0,
+        'comment' => '日志类型:money=余额,score=积分',
+    ],
+    'weigh' => [
+        'type' => 'int',
+        'length' => 10,
+        'default' => '0',
+        'is_null' => 1,
+        'comment' => '权重',
+    ],
+    'weight' => [
+        'type' => 'int',
+        'length' => 11,
+        'default' => null,
+        'is_null' => 1,
+        'comment' => '重量(kg)',
+    ],
+    'width' => [
+        'type' => 'int',
+        'length' => 5,
+        'default' => '0',
+        'is_null' => 1,
+        'unsigned' => 1,
+        'comment' => '宽度',
+    ],
+];

+ 126 - 0
addons/famysql/data/menu.php

@@ -0,0 +1,126 @@
+<?php
+
+$menu = [
+    [
+        "name" => "famysql/table",
+        "title" => "数据库管理",
+        "icon" => "fa fa-database",
+        "remark" => "可在线进行数据库表优化或修复,查看表结构和数据等",
+        "ismenu" => 1,
+        "sublist" => [
+            [
+                "name" => "famysql/table/index",
+                "title" => "查看"
+            ],
+            [
+                "name" => "famysql/table/table_add",
+                "title" => "添加"
+            ],
+            [
+                "name" => "famysql/table/table_batch_add",
+                "title" => "快速建表"
+            ],
+            [
+                "name" => "famysql/table/table_edit",
+                "title" => "编辑"
+            ],
+            [
+                "name" => "famysql/table/table_del",
+                "title" => "删除"
+            ],
+            [
+                "name" => "famysql/table/truncate",
+                "title" => "截/断表"
+            ],
+            [
+                "name" => "famysql/table/optimize",
+                "title" => "优化表"
+            ],
+            [
+                "name" => "famysql/table/repair",
+                "title" => "修复表"
+            ],
+            [
+                "name" => "famysql/table/copy",
+                "title" => "复制表"
+            ],
+            [
+                "name" => "famysql/table/backuplist",
+                "title" => "备份列表"
+            ],
+            [
+                "name" => "famysql/table/download",
+                "title" => "备份下载"
+            ],
+            [
+                "name" => "famysql/table/restore",
+                "title" => "恢复"
+            ],
+            [
+                "name" => "famysql/table/backup",
+                "title" => "备份"
+            ],
+            [
+                "name" => "famysql/table/upload",
+                "title" => "上传文件"
+            ],
+            [
+                "name" => "famysql/index",
+                "title" => "索引管理",
+                "icon" => "fa fa-list",
+                "ismenu" => 0,
+                "sublist" => [
+                    [
+                        "name" => "famysql/index/indexs",
+                        "title" => "索引首页"
+                    ],
+                    [
+                        "name" => "famysql/index/index_add",
+                        "title" => "添加"
+                    ],
+                    [
+                        "name" => "famysql/index/index_edit",
+                        "title" => "编辑"
+                    ],
+                    [
+                        "name" => "famysql/index/index_del",
+                        "title" => "删除"
+                    ]
+                ]
+            ],
+            [
+                "name" => "famysql/field",
+                "title" => "字段管理",
+                "icon" => "fa fa-list",
+                "ismenu" => 0,
+                "sublist" => [
+                    [
+                        "name" => "famysql/field/fields",
+                        "title" => "字段首页"
+                    ],
+                    [
+                        "name" => "famysql/field/create",
+                        "title" => "快速建表"
+                    ],
+                    [
+                        "name" => "famysql/field/field_add",
+                        "title" => "添加字段"
+                    ],
+                    [
+                        "name" => "famysql/field/field_edit",
+                        "title" => "修改字段"
+                    ],
+                    [
+                        "name" => "famysql/field/field_del",
+                        "title" => "删除"
+                    ],
+                    [
+                        "name" => "famysql/field/field_drag",
+                        "title" => "字段排序"
+                    ]
+                ]
+            ]
+        ]
+    ]
+];
+return $menu;

+ 628 - 0
addons/famysql/data/tables.ini

@@ -0,0 +1,628 @@
+CREATE TABLE IF NOT EXISTS `__PREFIX__area` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(50) DEFAULT NULL COMMENT '名称',
+  `pid` int(11) DEFAULT '0' COMMENT '上级',
+  `level` int(11) DEFAULT '1',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='省市区数据';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__cart` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `user_id` int(10) NOT NULL DEFAULT '0' COMMENT '用户ID',
+  `shop_id` int(10) NOT NULL DEFAULT '0' COMMENT '店铺ID',
+  `goods_id` int(10) NOT NULL DEFAULT '0' COMMENT '商品ID',
+  `goods_num` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '购物车数量',
+  `sku_price_id` int(11) DEFAULT NULL COMMENT '规格',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='购物车表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__category` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID',
+  `type` enum('article','goods') COLLATE utf8mb4_general_ci NOT NULL COMMENT '类型',
+  `name` varchar(30) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '名称',
+  `name_spacer` varchar(0) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '存放目录结构',
+  `image` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '图片',
+  `flag` set('hot','new','recommend') COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '标志',
+  `isnav` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否导航显示',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`),
+  KEY `weigh` (`weigh`) USING BTREE,
+  KEY `parent_id` (`pid`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品、文章类目表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__config` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(30) NOT NULL DEFAULT '' COMMENT '变量名',
+  `group` varchar(30) NOT NULL DEFAULT '' COMMENT '分组',
+  `title` varchar(100) NOT NULL DEFAULT '' COMMENT '变量标题',
+  `tip` varchar(100) NOT NULL DEFAULT '' COMMENT '变量描述',
+  `type` varchar(30) NOT NULL DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file',
+  `value` longtext NOT NULL COMMENT '变量值',
+  `content` longtext CHARACTER SET utf8mb4 COMMENT '变量字典数据',
+  `rule` varchar(100) NOT NULL DEFAULT '' COMMENT '验证规则',
+  `extend` varchar(255) NOT NULL DEFAULT '' COMMENT '扩展属性',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE KEY `name` (`name`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='配置';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__faq` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) DEFAULT NULL COMMENT '标题',
+  `content` longtext COMMENT '内容',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='常见问题';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__feedback` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) NOT NULL COMMENT '反馈用户',
+  `type` varchar(30) DEFAULT NULL COMMENT '反馈类型',
+  `content` varchar(255) DEFAULT NULL COMMENT '反馈内容',
+  `images` varchar(512) DEFAULT NULL COMMENT '图片',
+  `phone` varchar(30) DEFAULT NULL COMMENT '联系电话',
+  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否处理:0=未处理,1=已处理',
+  `remark` varchar(191) DEFAULT NULL COMMENT '处理备注',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='意见反馈';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__goods` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `shop_id` int(10) unsigned DEFAULT '0' COMMENT '店铺ID',
+  `shop_category_id` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '店铺内类目',
+  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '商品类目',
+  `brand_id` int(10) unsigned DEFAULT '0' COMMENT '品牌ID',
+  `category_attribute` longtext COLLATE utf8mb4_general_ci COMMENT '类目属性',
+  `title` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '商品标题',
+  `image` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '商品主图',
+  `images` varchar(1500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '产品相册',
+  `description` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '商品描述',
+  `flag` set('hot','index','recommend') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标志(多选):hot=热门,index=首页,recommend=推荐',
+  `stock` enum('porder','payment') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'porder' COMMENT '库存计算方式:porder=下单减库存,payment=付款减库存',
+  `content` longtext COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品详情',
+  `freight_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '运费模板',
+  `grounding` tinyint(1) NOT NULL DEFAULT '0' COMMENT '上架状态',
+  `specs` enum('single','multi') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'single' COMMENT '商品规格:single=单规格,multi=多规格',
+  `distribution` enum('true','false') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'false' COMMENT '是否独立分销:true=开启,false=关闭',
+  `activity` enum('0','1') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '活动中:0=没在活动中,1=活动中',
+  `activity_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '活动ID',
+  `activity_type` enum('goods','groups','seckill','bargain') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'goods' COMMENT '活动类型:goods=商品,groups=拼团,seckill=秒杀,bargain=砍价',
+  `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '点击',
+  `price` decimal(10,2) DEFAULT '0.00' COMMENT '产品价格',
+  `sales` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '销量',
+  `payment` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '付款人数',
+  `comment` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '评论',
+  `praise` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '好评',
+  `moderate` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '中评',
+  `negative` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '差评',
+  `like` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '收藏',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__comment` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `user_id` int(10) NOT NULL DEFAULT '0' COMMENT '用员ID',
+  `shop_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '店铺ID',
+  `order_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '订单ID',
+  `goods_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '商品ID',
+  `order_type` enum('goods','groups','seckill') COLLATE utf8mb4_general_ci DEFAULT 'goods' COMMENT '订单类型:goods=普通订单,groups=拼团订单,seckill=秒杀订单',
+  `order_goods_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品ID',
+  `state` enum('0','1','2') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '评价:0=好评,1=中评,2=差评',
+  `content` text COLLATE utf8mb4_general_ci COMMENT '内容',
+  `tag` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '评论标签',
+  `suk` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '所购买商品SUK',
+  `images` varchar(1500) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图片组',
+  `score` float(3,1) unsigned DEFAULT '0.0' COMMENT '综合评分',
+  `score_describe` float(3,1) unsigned DEFAULT '0.0' COMMENT '描述相符',
+  `score_service` float(3,1) unsigned DEFAULT '0.0' COMMENT '服务相符',
+  `score_deliver` float(3,1) unsigned DEFAULT '0.0' COMMENT '发货相符',
+  `score_logistics` float(3,1) unsigned DEFAULT '0.0' COMMENT '物流相符',
+  `switch` tinyint(1) DEFAULT '0' COMMENT '匿名评论',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品评论';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__follow` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `user_id` int(10) unsigned DEFAULT '0' COMMENT '用户ID',
+  `goods_id` int(10) unsigned DEFAULT '0' COMMENT '商品ID',
+  `goods_type` enum('goods','groups','seckill') COLLATE utf8mb4_general_ci DEFAULT 'goods' COMMENT '商品类型:goods=普通商品,groups=拼团商品,seckill=秒杀商品',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='关注商品表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__goods_sku` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(50) DEFAULT NULL COMMENT '名称',
+  `pid` int(11) NOT NULL DEFAULT '0' COMMENT '所属规格',
+  `goods_id` int(11) NOT NULL COMMENT '产品',
+  `weigh` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品规格';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__goods_sku_price` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `goods_sku_ids` varchar(255) DEFAULT NULL,
+  `goods_id` int(11) NOT NULL COMMENT '所属产品',
+  `weigh` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
+  `image` varchar(255) DEFAULT NULL COMMENT '缩略图',
+  `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存',
+  `stock_warning` int(11) DEFAULT NULL COMMENT '库存预警',
+  `sales` int(11) NOT NULL DEFAULT '0' COMMENT '已售',
+  `sn` varchar(50) DEFAULT NULL COMMENT '货号',
+  `weight` int(11) DEFAULT NULL COMMENT '重量(kg)',
+  `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '价格',
+  `goods_sku_text` varchar(255) DEFAULT NULL COMMENT '中文规格',
+  `status` varchar(10) DEFAULT 'up' COMMENT '状态',
+  `createtime` bigint(16) DEFAULT NULL,
+  `updatetime` bigint(16) DEFAULT NULL,
+  `deletetime` bigint(16) DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品规格';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__link` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `type` enum('system','activity','user','product','page') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'system' COMMENT '页面类型:system=系统,activity=活动,user=用户中心,product=商品,page=自定义页面',
+  `title` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '页面标题',
+  `route` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '路径',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='页面链接';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__order` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `type` enum('goods','score') NOT NULL DEFAULT 'goods' COMMENT '订单类型:goods=商城订单,score=积分商城订单',
+  `order_sn` varchar(60) NOT NULL COMMENT '订单号',
+  `user_id` int(11) DEFAULT '0' COMMENT '用户',
+  `activity_type` varchar(255) DEFAULT NULL COMMENT '活动类型',
+  `goods_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '商品总价',
+  `dispatch_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '总运费',
+  `phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
+  `consignee` varchar(20) DEFAULT NULL COMMENT '收货人',
+  `province_name` varchar(20) DEFAULT NULL COMMENT '省',
+  `city_name` varchar(20) DEFAULT NULL COMMENT '市',
+  `area_name` varchar(20) DEFAULT NULL COMMENT '区',
+  `address` varchar(255) DEFAULT NULL COMMENT '详细地址',
+  `province_id` int(11) NOT NULL DEFAULT '0',
+  `city_id` int(11) NOT NULL DEFAULT '0',
+  `area_id` int(11) NOT NULL DEFAULT '0',
+  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '订单状态:-2=交易关闭,-1=已取消,0=未支付,1=已支付,2=已完成',
+  `invoice_status` enum('-1','0','1') NOT NULL DEFAULT '-1' COMMENT '发票开具状态:-1=不可开具,0=未申请,1=已申请',
+  `memo` varchar(255) DEFAULT NULL COMMENT '商户备注',
+  `remark` varchar(255) DEFAULT NULL COMMENT '用户备注',
+  `total_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '订单总金额',
+  `score_amount` int(11) NOT NULL DEFAULT '0' COMMENT '积分总数',
+  `total_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '支付金额',
+  `last_total_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '改价前金额',
+  `discount_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '折扣总金额',
+  `coupon_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '优惠券抵用金额',
+  `activity_discount_money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '活动优惠',
+  `dispatch_discount_money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '运费优惠',
+  `pay_fee` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '实际支付金额',
+  `score_fee` int(11) NOT NULL DEFAULT '0' COMMENT '积分支付数',
+  `goods_original_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '商品原价',
+  `coupons_id` int(11) NOT NULL DEFAULT '0' COMMENT '优惠券 id',
+  `transaction_id` varchar(60) DEFAULT NULL COMMENT '交易单号',
+  `payment_json` varchar(2500) DEFAULT NULL COMMENT '交易原始数据',
+  `pay_type` enum('wechat','alipay','wallet','score') DEFAULT NULL COMMENT '支付方式:wechat=微信支付,alipay=支付宝,wallet=钱包支付,score=积分支付',
+  `paytime` bigint(16) DEFAULT NULL COMMENT '支付时间',
+  `ext` varchar(2048) DEFAULT NULL COMMENT '附加字段',
+  `platform` enum('H5','App','wxOfficialAccount','wxMiniProgram') DEFAULT NULL COMMENT '平台:H5=H5,wxOfficialAccount=微信公众号,wxMiniProgram=微信小程序,App=App',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE KEY `order_sn` (`order_sn`) USING BTREE,
+  KEY `user_id` (`user_id`) USING BTREE,
+  KEY `status` (`status`) USING BTREE,
+  KEY `createtime` (`createtime`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单管理';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__order_aftersale` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `aftersale_sn` varchar(40) NOT NULL COMMENT '售后单号',
+  `user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户',
+  `type` varchar(20) NOT NULL COMMENT '类型:refund=退款,return=退货,other=其他',
+  `phone` varchar(20) DEFAULT NULL COMMENT '联系方式',
+  `activity_id` int(11) DEFAULT NULL COMMENT '活动',
+  `activity_type` varchar(255) DEFAULT NULL COMMENT '活动类型',
+  `order_id` int(11) NOT NULL DEFAULT '0' COMMENT '订单',
+  `order_item_id` int(11) NOT NULL DEFAULT '0' COMMENT '订单商品',
+  `goods_id` int(11) NOT NULL DEFAULT '0' COMMENT '商品',
+  `goods_sku_price_id` int(11) NOT NULL DEFAULT '0' COMMENT '规格 id',
+  `goods_sku_text` varchar(30) DEFAULT NULL COMMENT '规格名',
+  `goods_title` varchar(255) DEFAULT NULL COMMENT '商品名称',
+  `goods_image` varchar(255) DEFAULT NULL COMMENT '商品图片',
+  `goods_original_price` decimal(10,2) NOT NULL COMMENT '商品原价',
+  `discount_fee` decimal(10,2) DEFAULT NULL COMMENT '优惠费用',
+  `goods_price` decimal(10,2) NOT NULL COMMENT '商品价格',
+  `goods_num` int(11) NOT NULL DEFAULT '0' COMMENT '购买数量',
+  `dispatch_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '发货状态:0=未发货,1=已发货,2=已收货',
+  `dispatch_fee` decimal(10,2) DEFAULT NULL COMMENT '发货费用',
+  `aftersale_status` tinyint(2) NOT NULL COMMENT '售后状态:-1=拒绝,0=未处理,1=处理中,2=售后完成',
+  `refund_status` tinyint(1) DEFAULT NULL COMMENT '退款状态:-1=拒绝退款,0=未退款,1=同意',
+  `refund_fee` decimal(10,2) DEFAULT NULL COMMENT '退款金额',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '添加时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE KEY `aftersale_sn` (`aftersale_sn`) USING BTREE,
+  KEY `user_id` (`user_id`) USING BTREE,
+  KEY `order_id` (`order_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='售后单';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__order_item` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) DEFAULT NULL COMMENT '用户',
+  `order_id` int(11) NOT NULL DEFAULT '0' COMMENT '订单',
+  `goods_id` int(11) NOT NULL DEFAULT '0' COMMENT '商品',
+  `goods_type` varchar(30) NOT NULL DEFAULT 'normal' COMMENT '商品类型:normal=实体商品,virtual=虚拟商品',
+  `goods_sku_price_id` int(11) NOT NULL DEFAULT '0' COMMENT '规格 id',
+  `activity_id` int(11) NOT NULL DEFAULT '0' COMMENT '活动 id',
+  `activity_type` varchar(255) DEFAULT NULL COMMENT '活动类型',
+  `item_goods_sku_price_id` int(11) NOT NULL DEFAULT '0' COMMENT '活动规格|积分商城规格 id',
+  `goods_sku_text` varchar(60) DEFAULT NULL COMMENT '规格名',
+  `goods_title` varchar(255) DEFAULT NULL COMMENT '商品名称',
+  `goods_image` varchar(255) DEFAULT NULL COMMENT '商品图片',
+  `goods_original_price` decimal(10,2) NOT NULL COMMENT '商品原价',
+  `discount_fee` decimal(10,2) DEFAULT NULL COMMENT '优惠费用',
+  `goods_price` decimal(10,2) NOT NULL COMMENT '商品价格',
+  `goods_num` int(11) NOT NULL DEFAULT '0' COMMENT '购买数量',
+  `goods_weight` int(11) NOT NULL DEFAULT '0' COMMENT '商品重量',
+  `pay_price` decimal(10,2) NOT NULL COMMENT '支付金额(不含运费)',
+  `dispatch_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '发货状态:0=未发货,1=已发货,2=已收货',
+  `dispatch_fee` decimal(10,2) DEFAULT NULL COMMENT '发货费用',
+  `dispatch_type` varchar(20) DEFAULT NULL COMMENT '发货方式',
+  `dispatch_id` int(11) DEFAULT NULL COMMENT '发货模板',
+  `store_id` int(11) NOT NULL DEFAULT '0' COMMENT '门店',
+  `aftersale_status` tinyint(2) NOT NULL COMMENT '售后状态:-1=拒绝,0=未申请,1=申请售后,2=售后完成',
+  `comment_status` tinyint(2) NOT NULL COMMENT '评价状态:0=未评价,1=已评价',
+  `refund_status` tinyint(1) DEFAULT NULL COMMENT '退款状态:-1=拒绝退款,0=无,1=申请中,2=同意',
+  `refund_fee` decimal(10,2) DEFAULT NULL COMMENT '退款金额',
+  `refund_msg` varchar(255) DEFAULT NULL COMMENT '退款原因',
+  `express_name` varchar(60) DEFAULT NULL COMMENT '快递公司',
+  `express_code` varchar(60) DEFAULT NULL COMMENT '快递公司编号',
+  `express_no` varchar(60) DEFAULT NULL COMMENT '快递单号',
+  `ext` varchar(2048) DEFAULT NULL COMMENT '附加字段',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '添加时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `order_id` (`order_id`) USING BTREE,
+  KEY `store_id` (`store_id`) USING BTREE,
+  KEY `activity_id` (`activity_id`) USING BTREE,
+  KEY `goods_id` (`goods_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单商品明细';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__refund_log` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `order_sn` varchar(45) DEFAULT NULL COMMENT '商户订单号',
+  `refund_sn` varchar(45) DEFAULT NULL COMMENT '商户退款单号',
+  `order_item_id` varchar(45) DEFAULT NULL COMMENT '订单商品',
+  `pay_fee` decimal(10,2) DEFAULT NULL COMMENT '支付金额',
+  `refund_fee` decimal(10,2) DEFAULT NULL COMMENT '退款金额',
+  `pay_type` varchar(20) NOT NULL DEFAULT '' COMMENT '付款方式',
+  `status` tinyint(1) DEFAULT '0' COMMENT '退款状态:0=退款中,1=退款完成,-1=退款失败',
+  `payment_json` varchar(1024) DEFAULT NULL COMMENT '退款原始数据',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='退款日志';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__store` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `name` varchar(50) NOT NULL COMMENT '门店名称',
+  `images` varchar(2500) NOT NULL COMMENT '门店图片',
+  `realname` varchar(50) NOT NULL COMMENT '联系人',
+  `phone` varchar(20) NOT NULL COMMENT '联系电话',
+  `province_name` varchar(50) NOT NULL COMMENT '省',
+  `city_name` varchar(50) NOT NULL COMMENT '市',
+  `area_name` varchar(50) NOT NULL COMMENT '区',
+  `province_id` int(11) NOT NULL COMMENT '省ID',
+  `city_id` int(11) NOT NULL COMMENT '市ID',
+  `area_id` int(11) NOT NULL COMMENT '区ID',
+  `address` varchar(255) NOT NULL COMMENT '详细地址',
+  `latitude` decimal(10,6) DEFAULT NULL COMMENT '纬度',
+  `longitude` decimal(10,6) DEFAULT NULL COMMENT '经度',
+  `store` enum('0','1') NOT NULL DEFAULT '0' COMMENT '支持配送:0=否,1=是',
+  `selfetch` enum('0','1') NOT NULL DEFAULT '0' COMMENT '支持自提:0=否,1=是',
+  `service_type` varchar(20) NOT NULL COMMENT '服务范围',
+  `service_radius` int(11) NOT NULL DEFAULT '0' COMMENT '服务半径',
+  `service_province_ids` varchar(1200) DEFAULT NULL COMMENT '服务行政省',
+  `service_city_ids` varchar(1200) DEFAULT NULL COMMENT '服务行政市',
+  `service_area_ids` varchar(1200) DEFAULT NULL COMMENT '服务行政区',
+  `openhours` varchar(20) NOT NULL COMMENT '营业时间',
+  `openweeks` set('1','2','3','4','5','6','7') NOT NULL DEFAULT '1,2,3,4,5,6,7' COMMENT '营业天数',
+  `status` enum('0','1') NOT NULL DEFAULT '1' COMMENT '门店状态:0=禁用,1=启用',
+  `createtime` bigint(16) NOT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) NOT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='门店';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__user_address` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `is_default` enum('0','1') DEFAULT '0' COMMENT '默认',
+  `user_id` int(11) DEFAULT NULL COMMENT '用户',
+  `consignee` varchar(20) DEFAULT NULL COMMENT '收货人',
+  `phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
+  `province_name` varchar(20) DEFAULT NULL COMMENT '省',
+  `city_name` varchar(20) DEFAULT NULL COMMENT '市',
+  `area_name` varchar(20) DEFAULT NULL COMMENT '区',
+  `address` varchar(255) DEFAULT NULL COMMENT '详细地址',
+  `province_id` int(11) DEFAULT NULL,
+  `city_id` int(11) DEFAULT NULL,
+  `area_id` int(11) DEFAULT NULL,
+  `latitude` decimal(10,6) DEFAULT NULL COMMENT '纬度',
+  `longitude` decimal(10,6) DEFAULT NULL COMMENT '经度',
+  `createtime` bigint(16) DEFAULT NULL,
+  `updatetime` bigint(16) DEFAULT NULL,
+  `deletetime` bigint(16) DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户地址';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__user_favorite` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `user_id` int(11) DEFAULT NULL COMMENT '用户',
+  `goods_id` int(11) DEFAULT NULL COMMENT '商品',
+  `createtime` bigint(16) DEFAULT NULL,
+  `updatetime` bigint(16) DEFAULT NULL,
+  `deletetime` bigint(16) DEFAULT NULL,
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户收藏';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__article` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `admin_id` int(10) NOT NULL DEFAULT '0' COMMENT '管理员ID',
+  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类ID(单选)',
+  `flag` set('hot','index','recommend') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标志(多选):hot=热门,index=首页,recommend=推荐',
+  `title` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标题',
+  `content` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',
+  `image` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '图片',
+  `images` varchar(1500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '图片组',
+  `attachfile` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '附件',
+  `keywords` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '关键字',
+  `description` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
+  `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '点击',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='文章表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__brand` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `admin_id` int(10) NOT NULL DEFAULT '0' COMMENT '管理员ID',
+  `shop_id` int(10) NOT NULL DEFAULT '0' COMMENT '商家ID',
+  `category_id` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '分类ID',
+  `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '品牌名',
+  `image` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '图片',
+  `content` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容介绍',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `switch` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'normal' COMMENT '状态',
+  `state` enum('0','1') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '状态值:0=审核中,1=已审核',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='品牌表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__complaint` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `type` enum('0','1','2') COLLATE utf8mb4_general_ci NOT NULL COMMENT '举报类型:0=用户举报,1=商品举报,2=店铺举报',
+  `user_id` int(10) NOT NULL DEFAULT '0' COMMENT '举报人',
+  `complaint_user_id` int(10) unsigned DEFAULT '0' COMMENT '被举报会员ID',
+  `complaint_shop_id` int(10) unsigned DEFAULT '0' COMMENT '被举报店铺ID',
+  `complaint_goods_id` int(10) unsigned DEFAULT '0' COMMENT '被举报商品ID',
+  `content` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '内容',
+  `images` varchar(1500) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '图片组',
+  `reason` enum('0','1','2','3','4','5','6','7','8','9','10','11','12','13') COLLATE utf8mb4_general_ci NOT NULL COMMENT '举报理由:0=虚假交易,1=诈骗欺诈,2=收到空包裹,3=假冒盗版,4=泄露信息,5=违禁物品,6=未按成交价交易,7=未按约定时间发货,8=垃圾营销,9=涉黄信息,10=不实信息,11=人身攻击,12=违法信息,13=诈骗信息',
+  `receipt` text COLLATE utf8mb4_general_ci COMMENT '处理回执',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `state` enum('normal','hidden') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'normal' COMMENT '状态:normal=未受理,hidden=已受理',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='举报表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__coupons` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `shop_id` int(10) NOT NULL DEFAULT '0' COMMENT '管理员ID',
+  `type` enum('reduction','discount','shipping','vip') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'reduction' COMMENT '优惠券类型:reduction=满减券,discount=折扣券,shipping=包邮券,vip=会员赠券',
+  `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '名称',
+  `userlevel` int(10) unsigned DEFAULT '0' COMMENT '会员等级',
+  `usertype` enum('reduction','discount') COLLATE utf8mb4_general_ci DEFAULT 'reduction' COMMENT '赠券类型:reduction=满减券,discount=折扣券',
+  `price` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '面值',
+  `discount` decimal(2,1) unsigned DEFAULT '0.0' COMMENT '折扣率',
+  `limit` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '消费限制',
+  `rangetype` enum('all','goods','category') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'all' COMMENT '可用范围:all=全部商品,goods=指定商品,category=指定分类',
+  `range` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '范围',
+  `pretype` enum('appoint','fixed') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'appoint' COMMENT '时效类型:appoint=领后天数,fixed=固定时间',
+  `validity` int(10) unsigned DEFAULT '0' COMMENT '有效天数',
+  `startdate` date DEFAULT NULL COMMENT '开始日期',
+  `enddate` date DEFAULT NULL COMMENT '结束时间',
+  `drawlimit` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '领取限制',
+  `grant` varchar(10) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '-1' COMMENT '发放总数量',
+  `alreadygrant` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '已领取数量',
+  `surplus` int(10) unsigned DEFAULT '0' COMMENT '剩余数量',
+  `content` text COLLATE utf8mb4_general_ci COMMENT '使用说明',
+  `usenum` int(10) unsigned DEFAULT '0' COMMENT '已使用数量',
+  `invalid` int(10) unsigned DEFAULT '0' COMMENT '是否失效',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='卡券表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__groups` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `group_no` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '拼团号',
+  `user_id` int(10) NOT NULL DEFAULT '0' COMMENT '团长ID',
+  `shop_id` int(10) NOT NULL DEFAULT '0' COMMENT '所属商家',
+  `goods_id` int(10) NOT NULL DEFAULT '0' COMMENT '所属商品',
+  `group_type` enum('alone','group','ladder') COLLATE utf8mb4_general_ci NOT NULL COMMENT '拼团类型:alone=直购,group=拼团,ladder=阶梯拼团',
+  `is_ladder` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否阶梯拼团',
+  `ladder_id` int(10) NOT NULL DEFAULT '0' COMMENT '阶梯ID',
+  `people_num` int(10) NOT NULL DEFAULT '0' COMMENT '成团人数',
+  `join_num` int(10) NOT NULL DEFAULT '0' COMMENT '参团人数',
+  `state` enum('ready','start','success','fail','auto') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'ready' COMMENT '拼团状态:ready=准备中,start=拼团中,success=已成团,fail=拼团失败,auto=自动成团',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '开团时间',
+  `grouptime` bigint(16) DEFAULT NULL COMMENT '成团时间',
+  `validitytime` bigint(16) DEFAULT NULL COMMENT '有效时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='拼团表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__notice` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `user_id` int(10) DEFAULT '0' COMMENT '用户ID',
+  `shop_id` int(10) unsigned DEFAULT '0' COMMENT '店铺ID',
+  `image` varchar(200) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '图片',
+  `title` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '消息标题',
+  `come` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '来自哪里',
+  `content` text COLLATE utf8mb4_general_ci COMMENT '消息内容',
+  `type` enum('order','notice') COLLATE utf8mb4_general_ci NOT NULL COMMENT '分类',
+  `modules` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '类型:order=订单,refund=退款,groupsorder=拼团订单,groupsrefund=拼团退款,live=直播,goods=商品',
+  `modules_id` int(10) unsigned DEFAULT '0' COMMENT '所属ID',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='消息表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__pay` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `pay_no` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '交易号',
+  `trade_no` varchar(32) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '交易订单号',
+  `user_id` int(10) NOT NULL DEFAULT '0' COMMENT '用户ID',
+  `shop_id` int(10) NOT NULL DEFAULT '0' COMMENT '用户ID',
+  `order_id` int(10) NOT NULL DEFAULT '0' COMMENT '订单ID',
+  `order_no` varchar(18) COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单号',
+  `pay_type` enum('0','1','2') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '支付类型:0=余额支付,1=微信支付,2=支付宝支付',
+  `pay_state` enum('0','1','2') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '支付状态 (支付回调):0=未支付,1=已支付,2=已退款',
+  `type` enum('goods','groups') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'goods' COMMENT '订单类型:goods=商品订单,groups=拼团订单',
+  `number` int(10) NOT NULL DEFAULT '0' COMMENT '总数',
+  `price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '支付金额',
+  `order_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单金额',
+  `freight_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '快递金额',
+  `coupon_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券金额',
+  `discount_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠金额',
+  `refund_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '退款金额',
+  `actual_payment` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际支付',
+  `total_amount` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '总金额',
+  `notice` text COLLATE utf8mb4_general_ci COMMENT '通知内容',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单支付表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__record` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
+  `shop_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '店铺ID',
+  `goods_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '商品ID',
+  `goods_type` enum('goods','groups','seckill') COLLATE utf8mb4_general_ci DEFAULT 'goods' COMMENT '商品类型:goods=普通商品,groups=拼团商品,seckill=秒杀商品',
+  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '类目ID',
+  `category_pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '类目父ID',
+  `uuid` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'app唯一编码',
+  `ip` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '设备ip',
+  `views` int(64) NOT NULL DEFAULT '1' COMMENT '点击',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='日志记录';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__search` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `keywords` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '关键字',
+  `flag` set('hot','index','recommend') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '推荐位 :hot=系统热门,index=搜索条,recommend=推荐',
+  `views` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '搜索次数',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `switch` tinyint(1) NOT NULL DEFAULT '0' COMMENT '开关',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='搜索表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__shop` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+  `user_id` int(10) unsigned DEFAULT '0' COMMENT '会员ID',
+  `shopname` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '店铺名称',
+  `keywords` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '关键字',
+  `description` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
+  `service_ids` varchar(100) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '服务(多选)',
+  `avatar` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '店铺头像',
+  `state` enum('0','1','2') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '店铺类型:0=个人,1=企业,2=旗舰',
+  `level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '店铺等级',
+  `islive` tinyint(1) NOT NULL DEFAULT '0' COMMENT '直播权限',
+  `isself` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否自营',
+  `bio` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '店铺简介',
+  `city` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '省市',
+  `return` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '退货地址',
+  `like` int(10) NOT NULL DEFAULT '0' COMMENT '收藏/喜欢',
+  `score_describe` float(3,1) NOT NULL DEFAULT '0.0' COMMENT '宝贝描述',
+  `score_service` float(3,1) NOT NULL DEFAULT '0.0' COMMENT '卖家服务',
+  `score_deliver` float(3,1) NOT NULL DEFAULT '0.0' COMMENT '发货相符',
+  `score_logistics` float(3,1) NOT NULL DEFAULT '0.0' COMMENT '物流服务',
+  `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重',
+  `verify` enum('0','1','2','3','4') COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '审核:0=提交资质,1=提交店铺,2=提交审核,3=通过,4=未通过',
+  `createtime` bigint(16) DEFAULT NULL COMMENT '创店时间',
+  `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',
+  `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间',
+  `status` enum('normal','hidden') COLLATE utf8mb4_general_ci DEFAULT 'normal' COMMENT '状态',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `shopname` (`shopname`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='店铺表';
+
+CREATE TABLE IF NOT EXISTS `__PREFIX__version` (
+  `id` int(32) NOT NULL AUTO_INCREMENT COMMENT '客户端版本号',
+  `title` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '版本标题',
+  `versionName` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '版本名称',
+  `versionCode` int(10) NOT NULL DEFAULT '100' COMMENT '版本号',
+  `content` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '升级内容',
+  `androidLink` varchar(500) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '安卓升级文件',
+  `iosLink` varchar(500) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'IOS升级文件',
+  `type` enum('base','alpha','beta','rc','release') COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'beta' COMMENT '版本类型:base=结构版,alpha=内测版,beta=公测版,rc=终测版,release=正式版',
+  `enforce` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '强制更新',
+  `createtime` bigint(16) NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `updatetime` bigint(16) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='版本升级表';

+ 10 - 0
addons/famysql/info.ini

@@ -0,0 +1,10 @@
+name = famysql
+title = Mysql数据库管理
+intro = 一款适用于FastAdmin框架的MySQL数据库的管理插件。
+author = 聚盟
+website = https://www.hnjumeng.com
+version = 1.0.9
+state = 1
+url = /addons/famysql
+license = regular
+licenseto = 19079

+ 204 - 0
addons/famysql/library/Backup.php

@@ -0,0 +1,204 @@
+<?php
+
+namespace addons\famysql\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($addon = 'all', $type = 0, $backUpdir = 'download/')
+    {
+        $sql = $this->_init($type);
+        $zip = new ZipArchive();
+        $date = date('YmdHis');
+        if (!is_dir($backUpdir)) {
+            @mkdir($backUpdir, 0755);
+        }
+        $name = "backup-{$this->name}-{$addon}-{$type}-{$date}-" . Random::alnum(6);
+        $filename = $backUpdir . $name . ".zip";
+
+        if ($zip->open($filename, ZIPARCHIVE::CREATE) !== true) {
+            throw new Exception("Could not open <$filename>\n");
+        }
+        if ($type == 0) {
+            $sql = preg_replace('/AUTO_INCREMENT=\d+ /', '', $sql);
+        }
+        $zip->addFromString($name . ".sql", $sql);
+        $zip->close();
+    }
+
+    private function _init($type)
+    {
+        # COUNT
+        $ct = 0;
+        # CONTENT
+        $sqldump = '';
+        # COPYRIGHT & OPTIONS
+        $sqldump .= "-- MySQL dump\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";
+        $tables = array_diff($this->tables, $this->ignoreTables);
+        # LOOP: Get the tables
+        foreach ($tables as $table) {
+            # COUNT
+            $ct++;
+            /** ** ** ** ** **/
+            # DATABASE: Count the rows in each tables
+            $count_rows = $this->db->prepare("SELECT * FROM `" . $table . "`");
+            $count_rows->execute();
+            $c_rows = $count_rows->columnCount();
+            # DATABASE: Count the columns in each tables
+            $count_columns = $this->db->prepare("SELECT COUNT(*) FROM `" . $table . "`");
+            $count_columns->execute();
+            $c_columns = $count_columns->fetchColumn();
+            /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
+            if ($type != 1) {
+                # MYSQL DUMP: Remove tables if they exists
+                $sqldump .= "--\n";
+                $sqldump .= "-- Table structure for table `" . $table . "`\n";
+                $sqldump .= "--\n\n";
+                $sqldump .= "DROP TABLE IF EXISTS `" . $table . "`;\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 . "`") as $field) {
+                    $sqldump .= str_replace('CREATE TABLE', 'CREATE TABLE', $field['Create Table']);
+                }
+                # MYSQL DUMP: New rows
+                $sqldump .= ";\n\n";
+                /** ** ** ** ** **/
+            }
+
+            # CHECK: There are one or more columns
+            if ($c_columns != 0 && $type != 0) {
+                # MYSQL DUMP: List the data for each table
+                $sqldump .= "--\n";
+                $sqldump .= "-- Dumping data for table `" . $table . "`\n";
+                $sqldump .= "--\n\n";
+                $sqldump .= "LOCK TABLES `" . $table . "` WRITE;\n";
+                # MYSQL DUMP: Insert into each table
+                $sqldump .= "INSERT INTO `" . $table . "` (";
+                # ARRAY
+                $rows = [];
+                $numeric = [];
+                # LOOP: Get the tables
+                foreach ($this->db->query("DESCRIBE `" . $table . "`") 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 . "`") 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 . "`(";
+                        # ARRAY
+                        $rows = [];
+                        # LOOP: Get the tables
+                        foreach ($this->db->query("DESCRIBE `" . $table . "`") as $row) {
+                            $rows[] = "`" . $row[0] . "`";
+                        }
+                        $sqldump .= implode(', ', $rows);
+                        $sqldump .= ") VALUES\n";
+                    }
+                }
+                $sqldump .= "UNLOCK TABLES;\n\n";
+            }
+        }
+
+        $sqldump .= "\n\n\n";
+        return $sqldump;
+
+    }
+
+}

+ 719 - 0
application/admin/controller/famysql/Field.php

@@ -0,0 +1,719 @@
+<?php
+
+namespace app\admin\controller\famysql;
+
+use app\common\controller\Backend;
+use think\Db;
+use think\Config;
+
+/**
+ * 字段管理
+ */
+class Field extends Backend
+{
+    protected $dbName = '';
+
+    protected $noNeedRight = ['selectfields', 'getType', 'getSuffix'];
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        if (!config("app_debug")) {
+            $this->error("数据库管理插件只允许在开发环境下使用");
+        }
+        if (!$this->auth->isSuperAdmin()) {
+            $this->error(__('Access is allowed only to the super management group'));
+        }
+        $this->dbName = Config::get("database.database");
+        $this->view->assign("suffixList", $this->getSuffixList());
+    }
+
+    /**
+     * 字段首页
+     */
+    public function fields()
+    {
+        $name = $this->request->get('name');
+        $is_admin = (int) $this->request->get('is_admin');
+        $offset = $this->request->get("offset");
+        $limit = $this->request->get("limit");
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+
+        $ints = ["int", "tinyint", "smallint", "mediumint", "bigint", "float", "double", "decimal"];
+
+        if ($this->request->isAjax()) {
+            $tableFields = Db::table("information_schema.COLUMNS")->field("*")->where(['TABLE_SCHEMA' => $this->dbName, 'TABLE_NAME' => $name])->select();
+
+            $list = [];
+            foreach ($tableFields as $key => $tableField) {
+                $list[$key]['id'] = $tableField['ORDINAL_POSITION'];
+                $list[$key]['name'] = $tableField['COLUMN_NAME'];
+                $list[$key]['type'] = $tableField['DATA_TYPE'];
+                $list[$key]['length'] = $tableField['COLUMN_TYPE'];
+                $list[$key]['default'] = $tableField['COLUMN_DEFAULT'];
+                $list[$key]['primary_key'] = $tableField['COLUMN_KEY'] == 'PRI' ? 1 : 0;
+                $list[$key]['index'] = $tableField['COLUMN_KEY'] == 'MUL' ? 1 : 0;
+                $list[$key]['is_null'] = $tableField['IS_NULLABLE'] == 'YES' ? '否' : '是';
+                $list[$key]['unsigned'] = strpos($tableField['COLUMN_TYPE'], 'unsigned') !== false ? '是' : (in_array($tableField['DATA_TYPE'], $ints) ? '否' : '-');
+                $list[$key]['auto_increment'] = strpos($tableField['EXTRA'], 'auto_increment') !== false ? 1 : 0;
+                $list[$key]['comment'] = $tableField['COLUMN_COMMENT'];
+                $list[$key]['is_admin'] = $is_admin;
+            }
+            $result = array("total" => count($list), "rows" => array_slice($list, $offset, $limit));
+            return json($result);
+        }
+        $this->view->assign("name", $name);
+        $this->view->assign("is_admin", $is_admin);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 快速建表
+     */
+    public function create()
+    {
+        $name = $this->request->get('name');
+        $is_admin = (int) $this->request->get('is_admin');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = "ALTER TABLE `{$name}`";
+                $column_name = explode(',', $params['column_name']);
+                foreach ($column_name as $column) {
+                    $sql .= $this->getCommonFields($column);
+                }
+                $sql = rtrim($sql, ',');
+
+                Db::startTrans();
+                try {
+                    $result = Db::execute($sql);
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $this->view->assign("name", $name);
+        $this->view->assign("is_admin", $is_admin);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 添加字段
+     */
+    public function field_add()
+    {
+        if ($this->request->isPost()) {
+            $name = $this->request->param('name');
+            $params = $this->request->post("row/a");
+            $column_name = $params['suffix'] == '无' ? $params['name'] : $params['name'] . $params['suffix'];
+            if ($params) {
+                $result = false;
+                $sql = "ALTER TABLE `{$name}` ADD COLUMN `{$column_name}` ";
+                Db::startTrans();
+                try {
+                    if (in_array($params['type'], ['enum', 'set'])) {
+                        $length_arr = json_decode($params['length'], true);
+                        $default_arr = [];
+                        foreach ($length_arr as $value) {
+                            $default_arr[] = $value['vo'];
+                        }
+                        $params['length'] = $default_arr;
+                    }
+                    $sql .= $this->getFieldSql($column_name, $params);
+
+                    $result = Db::execute($sql);
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were updated'));
+                }
+            }
+        }
+
+
+        return $this->view->fetch();
+    }
+
+    /**
+     * 修改字段
+     */
+    public function field_edit()
+    {
+        $table = $this->request->param("table");
+        if ($table == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'table'));
+        }
+        $field = $this->request->param("field");
+        if ($field == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'field'));
+        }
+
+        $properties = Db::query("SHOW FULL COLUMNS FROM `{$table}` WHERE Field =  '{$field}'");
+
+        $type_arr = explode(" ", $properties[0]["Type"]);
+        $type = strstr($type_arr[0], "(", true) !== false ? strstr($type_arr[0], "(", true) : $type_arr[0];
+        $length = preg_match('/\((.*?)\)/', $type_arr[0], $matches) ? $matches[1] : 0;
+
+        $row['name'] = $properties[0]["Field"];
+        $row['type'] = $type;
+        $row['collate'] = $properties[0]["Collation"];
+        if (in_array($type, ["enum", "set"])) {
+            $length_arr = explode(",", $length);
+            $length_res = [];
+            foreach ($length_arr as $key => $value) {
+                preg_match("/\'(.*?)\'/", $value, $matches);
+                $length_res[$key]['vo'] = $matches[1];
+            }
+            $length = json_encode($length_res);
+        }
+        $row['length'] = $length;
+        $row['default'] = $properties[0]["Default"];
+        $row['is_null'] = $properties[0]["Null"];
+        $row['unsigned'] = in_array("unsigned", $type_arr) ? 1 : 0;
+        $row['zerofill'] = in_array("zerofill", $type_arr) ? 1 : 0;
+        $row['comment'] = $properties[0]["Comment"];
+
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = "ALTER TABLE `{$table}` MODIFY COLUMN `{$field}` ";
+                Db::startTrans();
+                try {
+                    if ($params['name'] !== $row['name']) {
+                        $sql = "ALTER TABLE `{$table}` CHANGE `{$row['name']}` `{$params['name']}`";
+                    }
+
+                    if (in_array($params['type'], ['enum', 'set'])) {
+                        $length_arr = json_decode($params['length'], true);
+                        $default_arr = [];
+                        foreach ($length_arr as $value) {
+                            $default_arr[] = $value['vo'];
+                        }
+                        $params['length'] = $default_arr;
+                    }
+                    $sql .= $this->getFieldSql($params['name'], $params);
+
+                    // halt($sql);
+
+                    $result = Db::execute($sql);
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were updated'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $ints = ["int", "tinyint", "smallint", "mediumint", "bigint", "float", "double", "decimal"];
+
+        $no_length = ['date', 'datetime', 'time', 'year', "mediumtext", "longtext", "text"];
+
+        $this->view->assign("row", $row);
+        $this->view->assign("is_int", in_array($row['type'], $ints));
+        $this->view->assign("is_enum", in_array($row['type'], ['enum', 'set']));
+        $this->view->assign("is_length", in_array($row['type'], $no_length));
+        return $this->view->fetch();
+    }
+
+    /**
+     * 删除
+     */
+    public function field_del()
+    {
+        $table = $this->request->param("table");
+        if ($table == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'table'));
+        }
+        $field = $this->request->param("field");
+        if ($field == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'field'));
+        }
+
+        $result = false;
+        Db::startTrans();
+        try {
+            $sql = "ALTER TABLE `{$table}` DROP COLUMN `{$field}`;";
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $this->success();
+        } else {
+            $this->error(__('No rows were deleted'));
+        }
+    }
+
+    /**
+     * 字段排序
+     */
+    public function field_drag()
+    {
+        $name = $this->request->get('name');
+
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+
+        $fields = Db::getTableFields($name);
+
+        //排序的数组
+        $ids = $this->request->post("ids");
+        //拖动的记录ID
+        $changeid = (int) $this->request->post("changeid");
+
+        $ids = explode(',', $ids);
+
+        $position = array_search($changeid, $ids);
+        switch ($position) {
+            case 0:
+                if ($ids[array_search($changeid, $ids) + 1] > 1) {
+                    $changeField = $fields[$changeid - 1];
+                    $afterField = $fields[$ids[1] - 2];
+                    $properties = $this->getProperties($name, $changeField);
+                    $sql = "ALTER TABLE `{$name}` MODIFY COLUMN  `{$changeField}` {$properties} AFTER `{$afterField}`";
+                } else {
+                    $afterField = $fields[$changeid - 1];
+                    $properties = $this->getProperties($name, $afterField);
+                    $sql = "ALTER TABLE `{$name}` MODIFY COLUMN  `{$afterField}` {$properties} FIRST";
+                }
+
+                break;
+            default:
+                $changeField = $fields[$changeid - 1];
+                $afterField = $fields[($ids[array_search($changeid, $ids) - 1] - 1)];
+                $properties = $this->getProperties($name, $changeField);
+                $sql = "ALTER TABLE `{$name}` MODIFY COLUMN  `{$changeField}` {$properties} AFTER `{$afterField}`";
+
+        }
+        $result = false;
+        Db::startTrans();
+        try {
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $this->success();
+        } else {
+            $this->error(__('No rows were updated'));
+        }
+    }
+
+    /**
+     * 查看
+     * @internal
+     */
+    public function index()
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 添加
+     * @internal
+     */
+    public function add()
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 编辑
+     * @param string $ids
+     * @internal
+     */
+    public function edit($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 删除
+     * @param string $ids
+     * @internal
+     */
+    public function del($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 批量更新
+     * @internal
+     * @param string $ids
+     * @return void
+     */
+    public function multi($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 字段选择
+     * @internal
+     */
+    public function selectfields()
+    {
+        //当前页
+        $page = $this->request->request("pageNumber");
+        //分页大小
+        $pagesize = $this->request->request("pageSize");
+
+        $q_word = (array) $this->request->request("q_word/a");
+
+        $word = $q_word[0];
+
+        $custom = (array) $this->request->request("custom/a");
+        if ($custom && is_array($custom)) {
+            $table = $custom['table'];
+        }
+
+        $fields = $this->getFields($table, ['id']);
+
+        $commonFields = $this->getCommonFields();
+
+        $fieldLists = [];
+        foreach ($commonFields as $commonField) {
+            if (!in_array($commonField['column_name'], $fields)) {
+                $fieldLists[] = $commonField;
+            }
+        }
+        if (!empty($word)) {
+            $res_arr = [];
+            foreach ($fieldLists as $fieldList) {
+                $res_arr[] = $fieldList['column_name'] . '-' . $fieldList['comment'];
+            }
+            $res_arr = array_filter($res_arr, function ($v) use ($word) {
+                return stripos($v, $word) !== false;
+            });
+            $res_arrs = array_values($res_arr);
+
+            $fieldLists_arr = [];
+            foreach ($res_arrs as $res) {
+                $fieldLists_arr[] = [
+                    'column_name' => explode('-', $res)[0],
+                    'comment' => explode('-', $res)[1]
+                ];
+            }
+            $fieldLists = $fieldLists_arr;
+        }
+
+        $result = array("total" => count($fieldLists), "list" => array_slice($fieldLists, ($page - 1) * $pagesize, $pagesize));
+
+        return json($result);
+
+    }
+
+    /**
+     * 字段类型
+     * @internal
+     */
+    public function getType()
+    {
+        //当前页
+        $page = $this->request->request("pageNumber");
+        //分页大小
+        $pagesize = $this->request->request("pageSize");
+
+        $q_word = (array) $this->request->request("q_word/a");
+
+        $word = $q_word ? $q_word[0] : '';
+
+        $custom = (array) $this->request->request("custom/a");
+        $keyValue = $this->request->request('keyValue');
+        if (!$keyValue) {
+            $suffix = [];
+            $type = [];
+            if ($custom && is_array($custom)) {
+                $suffix = $custom['suffix'];
+                $suffixList = $this->getSuffixList($suffix);
+                $type = !is_array($suffixList['type']) ? [$suffixList['type']] : $suffixList['type'];
+            }
+
+            $typeList = $this->getTypeList($type);
+            $lists = [];
+            foreach ($typeList as $v) {
+                $lists[] = ['type' => $v];
+            }
+            if (!empty($word)) {
+                $res_arr = [];
+                foreach ($lists as $list) {
+                    $res_arr[] = $list['type'];
+                }
+                $res_arr = array_filter($res_arr, function ($v) use ($word) {
+                    return stripos($v, $word) !== false;
+                });
+                $res_arrs = array_values($res_arr);
+
+                $lists_arr = [];
+                foreach ($res_arrs as $res) {
+                    $lists_arr[] = [
+                        'type' => $res,
+                    ];
+                }
+                $lists = $lists_arr;
+            }
+        } else {
+            $lists[] = ['type' => $keyValue];
+        }
+
+        $result = array("total" => count($lists), "rows" => array_slice($lists, ($page - 1) * $pagesize, $pagesize));
+        return json($result);
+    }
+
+    /**
+     * 字段后缀
+     * @internal
+     */
+    public function getSuffix()
+    {
+        $name = $this->request->request("name");
+        $suffix = $this->getSuffixList($name);
+        return json($suffix);
+    }
+
+    /**
+     * 读取后缀规则
+     * @return array
+     */
+    protected function getSuffixList($suffix = '')
+    {
+        $suffixList = [];
+        $suffixList['time'] = ["type" => ["bigint", "datetime"], "length" => 16, "default" => NULL, "comment" => '时间', "remark" => '识别为日期时间型数据,自动创建选择时间的组件'];
+        $suffixList['image'] = ["type" => ["varchar"], "length" => 255, "default" => '', "comment" => '缩略图', "remark" => '识别为图片文件,自动生成可上传图片的组件,单图'];
+        $suffixList['images'] = ["type" => ["varchar"], "length" => 1500, "default" => '', "comment" => '组图', "remark" => '识别为图片文件,自动生成可上传图片的组件,多图'];
+        $suffixList['file'] = ["type" => ["varchar"], "length" => 100, "default" => '', "is_null" => 1, "comment" => '附件', "remark" => '识别为普通文件,自动生成可上传文件的组件,单文件'];
+        $suffixList['files'] = ["type" => ["varchar"], "length" => 1000, "default" => '', "is_null" => 1, "comment" => '附件', "remark" => '识别为普通文件,自动生成可上传文件的组件,多文件'];
+        $suffixList['avatar'] = ["type" => ["varchar"], "length" => 255, "default" => '', "is_null" => 1, "comment" => '头像', "remark" => '识别为头像,自动生成可上传图片的组件,单图'];
+        $suffixList['avatars'] = ["type" => ["varchar"], "length" => 1500, "default" => '', "is_null" => 1, "comment" => '头像', "remark" => '识别为头像,自动生成可上传图片的组件,多图'];
+
+        $suffixList['seconds'] = ["type" => ["int"], "length" => 10, "default" => NULL, "is_null" => 1, "comment" => '时长/分钟'];
+
+        $suffixList['price'] = ["type" => ["decimal"], "length" => '10,2', "default" => '0.00', "is_null" => 1, 'unsigned' => 1, "comment" => '价格'];
+
+        $suffixList['content'] = ["type" => ["text", "mediumtext", "longtext"], "is_null" => 1, "comment" => '内容', "remark" => '识别为内容,自动生成富文本编辑器(需安装富文本插件)'];
+
+        $suffixList['_id'] = ["type" => ["int"], "length" => 10, "default" => 0, "is_null" => 1, "unsigned" => 1, "zerofill" => 0, "comment" => 'ID', "remark" => '识别为关联字段,自动生成可自动完成的文本框,单选'];
+        $suffixList['_ids'] = ["type" => ["varchar"], "length" => 100, "default" => '', "comment" => 'ID集合', "remark" => '识别为关联字段,自动生成可自动完成的文本框,多选'];
+
+        $suffixList['list'] = ["type" => ["enum", "set"], "is_null" => 1, "remark" => ['识别为列表字段,自动生成单选下拉列表', '识别为列表字段,自动生成多选下拉列表']];
+        $suffixList['data'] = ["type" => ["enum", "set"], "is_null" => 1, "remark" => ['识别为选项字段,自动生成单选框', '识别为选项字段,自动生成复选框']];
+
+        if (version_compare(config('fastadmin.version'), '1.3.0', '<')) {
+            $suffixList['json'] = ["type" => ["varchar"], "length" => 255, "default" => '', "is_null" => 1, "comment" => '管理员ID', "remark" => '识别为键值组件,自动生成键值录入组件,仅支持1.2.0+'];
+            $suffixList['switch'] = ["type" => ["tinyint"], "length" => 1, "default" => 0, "is_null" => 1, "comment" => '开关', "remark" => '识别为开关字段,自动生成开关组件,默认值1为开,0为关,仅支持FastAdmin 1.2.0+'];
+        } else {
+            $suffixList['range'] = ["type" => ["varchar"], "length" => 100, "default" => '', "is_null" => 1, "comment" => '区间', "remark" => '识别为时间区间组件,自动生成时间区间组件,仅支持FastAdmin 1.3.0+'];
+            $suffixList['tag'] = ["type" => ["varchar"], "length" => 255, "default" => '', "is_null" => 1, "comment" => '标签', "remark" => '识别为Tagsinput,自动生成标签输入组件,仅支持FastAdmin 1.3.0+'];
+            $suffixList['tags'] = ["type" => ["varchar"], "length" => 255, "default" => '', "is_null" => 1, "comment" => '标签组', "remark" => '识别为Tagsinput,自动生成标签输入组件,仅支持FastAdmin 1.3.0+'];
+        }
+        return empty($suffix) ? array_keys($suffixList) : $suffixList[$suffix];
+    }
+
+    /**
+     * 读取类型规则
+     * @return array
+     */
+    protected function getTypeList($types = [])
+    {
+        $typeList = [];
+        $sql = "SELECT DISTINCT DATA_TYPE FROM information_schema.COLUMNS";
+        $result = Db::query($sql);
+        foreach ($result as $key => $value) {
+            $typeList[$value['DATA_TYPE']] = $value['DATA_TYPE'];
+            if (!empty($types) && !in_array($value['DATA_TYPE'], $types)) {
+                unset($typeList[$value['DATA_TYPE']]);
+            }
+        }
+
+        return $typeList;
+    }
+
+    protected function getCommonFields($fields = '')
+    {
+        $fieldList = include ADDON_PATH . 'famysql' . DS . 'data' . DS . 'fields.php';
+
+        $fields = $fields == '' ? [] : explode(',', $fields);
+        if (!empty($fields)) {
+            $sql = "";
+            foreach ($fieldList as $field => $fieldInfo) {
+                if (in_array($field, $fields)) {
+                    $sql .= " ADD COLUMN `{$field}`" . $this->getFieldSql($field, $fieldInfo);
+                    $sql .= ",";
+                }
+            }
+            return $sql;
+        } else {
+            $fields = array_keys($fieldList);
+            $result = [];
+            foreach ($fields as $key => $field) {
+                $result[$key] = [
+                    "column_name" => $field,
+                    "comment" => isset($fieldList[$field]['comment']) ? $fieldList[$field]['comment'] : ucwords($field)
+                ];
+            }
+            return $result;
+        }
+    }
+
+    /**
+     * 获取表字段属性
+     */
+    protected function getProperties($table, $field)
+    {
+        $all = Db::query("SHOW FULL COLUMNS FROM `{$table}` WHERE Field =  '{$field}'");
+
+        $str = '';
+        $str .= "{$all[0]['Type']}";
+
+        if ($all[0]['Collation'] != NULL) {
+            $charset = substr($all[0]['Collation'], 0, strpos($all[0]['Collation'], '_'));
+            $str .= " CHARACTER SET {$charset} COLLATE {$all[0]['Collation']}";
+        }
+
+        if ($all[0]['Null'] == 'NO')
+            $str .= '  NOT NULL';
+
+        if ($all[0]['Default'] === '')
+            $str .= " DEFAULT ''";
+        if ($all[0]['Default'] != NULL && $all[0]['Default'] != '')
+            $str .= " DEFAULT '{$all[0]['Default']}'";
+
+        if ($all[0]['Extra'] == 'auto_increment')
+            $str .= ' AUTO_INCREMENT';
+        $str .= " Comment '{$all[0]['Comment']}'";
+
+        return $str;
+    }
+
+    protected function getFieldSql($field, $fieldInfo)
+    {
+        $sql = "";
+        if (isset($fieldInfo['type'])) {
+            $sql .= " {$fieldInfo['type']}";
+        }
+        if (!in_array($fieldInfo['type'], ["enum", "set"]) && isset($fieldInfo['length'])) {
+            $sql .= "(" . $fieldInfo['length'] . ")";
+        } elseif (in_array($fieldInfo['type'], ["enum", "set"])) {
+            $length = "";
+            foreach ($fieldInfo['length'] as $value) {
+                $length .= "'{$value}',";
+            }
+            $length = rtrim($length, ",");
+            $sql .= "(" . $length . ")";
+        }
+        if (isset($fieldInfo['unsigned']) && $fieldInfo['unsigned'] == 1) {
+            $sql .= " UNSIGNED";
+        }
+        if (isset($fieldInfo['zerofill']) && $fieldInfo['zerofill'] == 1) {
+            $sql .= " ZEROFILL";
+        }
+        if (isset($fieldInfo['is_null']) && $fieldInfo['is_null'] == 0) {
+            $sql .= " NOT NULL";
+        }
+        if (isset($fieldInfo['default'])) {
+            if (in_array($fieldInfo['type'], ["int", "tinyint", "smallint", "mediumint", "bigint"])) {
+                if ($fieldInfo['default'] == "") {
+                    $sql .= "";
+                } elseif ($fieldInfo['default'] == 0) {
+                    $sql .= " DEFAULT 0";
+                } else {
+                    $sql .= empty($fieldInfo['default']) ? "" : " DEFAULT {$fieldInfo['default']}";
+                }
+            } elseif (in_array($fieldInfo['type'], ["float", "double", "decimal"])) {
+                if ($fieldInfo['default'] == "") {
+                    $sql .= "";
+                } elseif ($fieldInfo['default'] == 0) {
+                    $sql .= " DEFAULT '0.00'";
+                } else {
+                    $sql .= empty($fieldInfo['default']) ? "" : " DEFAULT '{$fieldInfo['default']}'";
+                }
+            } elseif (in_array($fieldInfo['type'], ["text", "longtext", "mediumtext"])) {
+                $sql .= empty($fieldInfo['default']) ? "" : " DEFAULT '{$fieldInfo['default']}'";
+            } elseif (in_array($fieldInfo['type'], ["enum", "set"])) {
+                $sql .= (empty($fieldInfo['default']) && $fieldInfo['default'] !== '0') ? "" : " DEFAULT '{$fieldInfo['default']}'";
+            } else {
+                if ($fieldInfo['default'] === '0') {
+                    $sql .= " DEFAULT '0'";
+                } elseif (empty($fieldInfo['default'])) {
+                } else {
+                    $sql .= " DEFAULT '{$fieldInfo['default']}'";
+                }
+            }
+
+        }
+        $comment = isset($fieldInfo['comment']) ? $fieldInfo['comment'] : ucwords($field);
+        $sql .= " COMMENT '{$comment}'";
+
+        return $sql;
+    }
+
+    protected function getFields($table, $excludeFields = [])
+    {
+        $fields = Db::getFields($table);
+        $result = [];
+        foreach ($fields as $field => $fieldInfo) {
+            if (!in_array($field, $excludeFields)) {
+                $result[] = $field;
+            }
+        }
+        return $result;
+    }
+}

+ 365 - 0
application/admin/controller/famysql/Index.php

@@ -0,0 +1,365 @@
+<?php
+
+namespace app\admin\controller\famysql;
+
+use app\common\controller\Backend;
+use think\Db;
+
+/**
+ * 索引管理
+ */
+class Index extends Backend
+{
+    protected $noNeedRight = ['selectpage'];
+
+    /**
+     * 读取索引类型规则
+     * @return array
+     */
+    protected $typeList = ['INDEX' => 'INDEX(普通)', 'UNIQUE' => 'UNIQUE(唯一)', 'FULLTEXT' => 'FULLTEXT(全文)'];
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        if (!config("app_debug")) {
+            $this->error("数据库管理插件只允许在开发环境下使用");
+        }
+        if (!$this->auth->isSuperAdmin()) {
+            $this->error(__('Access is allowed only to the super management group'));
+        }
+        $this->view->assign("indexList", $this->typeList);
+    }
+
+    /**
+     * 索引首页
+     */
+    public function indexs()
+    {
+        $name = $this->request->get('name');
+        $is_admin = (int) $this->request->get('is_admin');
+        $offset = $this->request->get("offset");
+        $limit = $this->request->get("limit");
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+
+        if ($this->request->isAjax()) {
+            $indexs = Db::query("SHOW INDEX FROM {$name}");
+
+            $lists = [];
+            $Key_names = [];
+            foreach ($indexs as $index) {
+                array_push($Key_names, $index['Key_name']);
+                $Key_names = array_unique($Key_names);
+            }
+
+            foreach ($Key_names as $key => $Key_name) {
+                $lists[$key] = $this->get_indexs($name, $Key_name, $is_admin);
+            }
+
+            $result = array("total" => count($lists), "rows" => array_slice($lists, $offset, $limit));
+            return json($result);
+        }
+        $this->view->assign("name", $name);
+        $this->view->assign("is_admin", $is_admin);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 添加
+     */
+    public function index_add()
+    {
+        $table = $this->request->get('table');
+        if ($table == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'table'));
+        }
+
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = "CREATE";
+                Db::startTrans();
+                try {
+                    if ($params['non_unique'] !== 'INDEX') {
+                        $sql .= " {$params['non_unique']}";
+                    }
+                    $sql .= " INDEX `{$params['name']}` ON `{$table}`";
+                    $column_names = explode(',', $params['column_name']);
+                    $sql .= " (";
+                    foreach ($column_names as $column_name) {
+                        $sql .= "`{$column_name}`,";
+                    }
+                    $sql = rtrim($sql, ',');
+                    $sql .= ")";
+                    $result = Db::execute($sql);
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+
+        $this->view->assign("table", $table);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 编辑
+     */
+    public function index_edit()
+    {
+        $table = $this->request->get('table');
+        if ($table == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'table'));
+        }
+        $name = $this->request->get('name');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+        $row = $this->get_indexs($table, $name, 0);
+
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = "ALTER TABLE `{$table}` DROP INDEX `{$row['name']}`, ADD";
+                Db::startTrans();
+                try {
+                    if ($params['non_unique'] !== 'INDEX') {
+                        $sql .= " {$params['non_unique']}";
+                    }
+                    $sql .= " INDEX `{$params['name']}`";
+                    $column_names = explode(',', $params['column_name']);
+                    $sql .= "(";
+                    foreach ($column_names as $column_name) {
+                        $sql .= "`{$column_name}`,";
+                    }
+                    $sql = rtrim($sql, ',');
+                    $sql .= ")";
+                    $result = Db::execute($sql);
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+
+        $this->view->assign("row", $row);
+        $this->view->assign("table", $table);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 删除
+     */
+    public function index_del()
+    {
+        $table = $this->request->param("table");
+        if ($table == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'table'));
+        }
+        $name = $this->request->param("name");
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+
+        $result = false;
+        try {
+            $sql = "ALTER TABLE `{$table}` DROP INDEX `{$name}`;";
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $this->success();
+        } else {
+            $this->error(__('No rows were deleted'));
+        }
+    }
+
+    /**
+     * 查看
+     * @internal
+     */
+    public function index()
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 添加
+     * @internal
+     */
+    public function add()
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 编辑
+     * @param string $ids
+     * @internal
+     */
+    public function edit($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 删除
+     * @param string $ids
+     * @internal
+     */
+    public function del($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 批量更新
+     * @internal
+     * @param string $ids
+     * @return void
+     */
+    public function multi($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 字段列表
+     * @internal
+     */
+    public function selectpage($type = '')
+    {
+        //当前页
+        $page = $this->request->request("pageNumber");
+        //分页大小
+        $pagesize = $this->request->request("pageSize");
+
+        $q_word = (array) $this->request->request("q_word/a");
+
+        $word = $q_word ? $q_word[0] : '';
+
+        $custom = (array) $this->request->request("custom/a");
+        $keyValue = $this->request->request('keyValue');
+        if (!$keyValue) {
+            if ($custom && is_array($custom)) {
+                $table = $custom['table'];
+            }
+
+            $fields = Db::getFields($table);
+            $lists = [];
+            foreach ($fields as $field => $fieldInfo) {
+                if (!in_array($field, ['id'])) {
+                    $lists[] = $field;
+                }
+            }
+
+            foreach ($lists as $k => $v) {
+                $lists[$k] = ["column_name" => $v];
+            }
+
+            if (!empty($word)) {
+                $res_arr = [];
+                foreach ($lists as $list) {
+                    $res_arr[] = $list['column_name'];
+                }
+                $res_arr = array_filter($res_arr, function ($v) use ($word) {
+                    return stripos($v, $word) !== false;
+                });
+                $res_arrs = array_values($res_arr);
+
+                $lists_arr = [];
+                foreach ($res_arrs as $res) {
+                    $lists_arr[] = [
+                        'column_name' => $res,
+                    ];
+                }
+                $lists = $lists_arr;
+            }
+
+        } else {
+            $values = explode(',', $keyValue);
+            foreach ($values as $key => $value) {
+                $lists[$key] = ['column_name' => $value];
+            }
+
+        }
+
+        $result = array("total" => count($lists), "list" => array_slice($lists, ($page - 1) * $pagesize, $pagesize));
+
+        return json($result);
+
+    }
+
+    private function get_indexs($tableName, $keyName, $is_admin)
+    {
+        $indexs = Db::query("SHOW INDEX FROM {$tableName} WHERE Key_name = '{$keyName}'");
+
+        $lists = [];
+        foreach ($indexs as $key => $index) {
+            if ($index['Key_name'] == 'PRIMARY') {
+                $unique = 'PRIMARY';
+            } elseif (!$index['Non_unique']) {
+                $unique = 'UNIQUE';
+            } elseif ($index['Index_type'] == 'FULLTEXT') {
+                $unique = 'FULLTEXT';
+            } else {
+                $unique = 'INDEX';
+            }
+            $lists[$key]['name'] = $index['Key_name'];
+            $lists[$key]['column_name'] = $index['Column_name'];
+            $lists[$key]['non_unique'] = $unique;
+        }
+
+        $result['column_name'] = '';
+
+        foreach ($lists as $i => $list) {
+            $result['name'] = $index['Key_name'];
+            if (($i + 1) == count($lists)) {
+                $result['column_name'] .= $list['column_name'];
+            } else {
+                $result['column_name'] .= $list['column_name'] . ',';
+            }
+            $result['non_unique'] = $unique;
+            $result['is_admin'] = $is_admin;
+        }
+
+        return $result;
+    }
+}

+ 910 - 0
application/admin/controller/famysql/Table.php

@@ -0,0 +1,910 @@
+<?php
+
+namespace app\admin\controller\famysql;
+
+use app\common\controller\Backend;
+use addons\famysql\library\Backup;
+use think\Db;
+use think\Config;
+use think\Exception;
+use think\exception\PDOException;
+use ZipArchive;
+
+
+/**
+ * 数据库管理
+ *
+ * @icon fa fa-database
+ * @remark 可在线进行数据库表优化或修复,查看表结构和数据等
+ */
+class Table extends Backend
+{
+    protected $dbName = '';
+
+    protected $prefix = '';
+
+    protected $noNeedRight = ['selectnames', 'getCollation', 'get_table_list', 'check'];
+
+    /**
+     * 读取字符集
+     * @return array
+     */
+    protected $charsetList = ['utf8mb4', 'utf8', 'latin1', 'utf16'];
+
+    /**
+     * 读取排序规则
+     * @return array
+     */
+    protected $collationList = [
+        'utf8mb4' => ['utf8mb4_general_ci', 'utf8mb4_unicode_ci'],
+        'utf8' => ['utf8_general_ci', 'utf8_unicode_ci'],
+        'latin1' => ['latin1_general_ci'],
+        'utf16' => ['utf16_general_ci', 'utf16_unicode_ci'],
+    ];
+
+    public function _initialize()
+    {
+        parent::_initialize();
+        if (!config("app_debug")) {
+            $this->error("数据库管理插件只允许在开发环境下使用");
+        }
+        if (!$this->auth->isSuperAdmin()) {
+            $this->error(__('Access is allowed only to the super management group'));
+        }
+        $this->dbName = Config::get("database.database");
+        $this->prefix = Config::get('database.prefix');
+        $this->view->assign("charsetList", $this->charsetList);
+        $this->view->assign("groups", $this->getGroups(true));
+        $this->view->assign("groupsList", $this->getGroups());
+    }
+
+    /**
+     * 查看
+     */
+    public function index()
+    {
+        $group = $this->request->get("group");
+        $offset = $this->request->get("offset");
+        $limit = $this->request->get("limit");
+        $config = get_addon_config('famysql');
+        if ($this->request->isAjax()) {
+            $group = $group ?? 'system';
+            $tables = $this->getTables($group);
+
+            $list = [];
+            if (count($tables) > 0) {
+                $tableInfos = [];
+                foreach ($tables as $k => $v) {
+                    $tableInfos[] = Db::table("information_schema.TABLES")->field("*")->where(['TABLE_SCHEMA' => $this->dbName, 'TABLE_NAME' => $v])->find();
+                }
+
+                $i = 1;
+                foreach ($tableInfos as $key => $tableInfo) {
+                    $list[$key]['id'] = $i++;
+                    $list[$key]['group'] = $group;
+                    $list[$key]['is_admin'] = ($group == 'system' && !$config['is_admin']) ? 0 : 1;
+                    $list[$key]['is_has'] = $this->prefix !== '' ? 1 : 0;
+                    $list[$key]['name'] = $tableInfo['TABLE_NAME'];
+                    $list[$key]['engine'] = $tableInfo['ENGINE'];
+                    $list[$key]['rows'] = Db::table($tableInfo['TABLE_NAME'])->count();
+                    $list[$key]['field_nums'] = count(Db::getFields($tableInfo['TABLE_NAME']));
+                    $list[$key]['charset'] = substr($tableInfo['TABLE_COLLATION'], 0, strpos($tableInfo['TABLE_COLLATION'], '_'));
+                    $list[$key]['collation'] = $tableInfo['TABLE_COLLATION'];
+                    $list[$key]['comment'] = $tableInfo['TABLE_COMMENT'];
+                    $list[$key]['createtime'] = $tableInfo['CREATE_TIME'];
+                    $list[$key]['updatetime'] = $tableInfo['UPDATE_TIME'];
+                }
+            }
+
+            $result = array("total" => count($list), "rows" => array_slice($list, $offset, $limit));
+            return json($result);
+        }
+        $this->view->assign("group", $group);
+        $this->assignconfig("group", $group);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 添加
+     */
+    public function table_add()
+    {
+        $group = $this->request->get("group");
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = [];
+                $name = $this->prefix . $params['addon'] . '_' . $params['name'];
+                Db::startTrans();
+                try {
+                    $sql = "SHOW TABLES LIKE '{$name}'";
+                    $result = Db::query($sql);
+                    if ($result) {
+                        $this->error("表 {$name} 已存在于数据库 {$this->dbName} 中");
+                    } else {
+                        //在此执行创建表的操作
+                        $sql = "CREATE TABLE IF NOT EXISTS `{$name}` (
+                                    `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
+                                    PRIMARY KEY (`id`)
+                                ) ENGINE={$params['engine']} DEFAULT CHARSET={$params['charset']} COLLATE={$params['collation']} COMMENT='" . $params['comment'] . "';";
+
+                        $result = Db::execute($sql);
+                    }
+
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                    $this->success();
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+        }
+        $this->view->assign("group", $group);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 快速建表
+     */
+    public function table_batch_add()
+    {
+        $group = $this->request->get("group");
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = [];
+                $prefix = $this->prefix . $params['addon'] . '_';
+                Db::startTrans();
+                try {
+                    $templates = $this->template();
+                    $names = explode(',', $params['name']);
+                    foreach ($templates as $template) {
+                        if (in_array($template['table_name'], $names)) {
+                            $sql[] = str_replace("__PREFIX__", $prefix, $template['sql']) . ";";
+                        }
+                    }
+                    $result = Db::batchQuery($sql);
+                    if (!$result) {
+                        $this->error();
+                    }
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                    $this->success();
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+        $this->view->assign("group", $group);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 备份列表
+     */
+    public function backuplist()
+    {
+        $group = $this->request->get("group");
+        $offset = $this->request->get("offset");
+        $limit = $this->request->get("limit");
+        if ($this->request->isAjax()) {
+            $filter = $this->request->request("filter", '', 'trim');
+            $filter = (array) json_decode($filter, true);
+            $addon = !isset($filter['addon']) ? 'all' : $filter['addon'];
+            $type = !isset($filter['type']) ? 'all' : $filter['type'];
+            $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
+
+            $backuplist = [];
+            $files = [];
+            foreach (glob($backupDir . "*.*") as $key => $filename) {
+                $basename = basename($filename);
+                $file_arr = stripos($basename, '-') !== FALSE ? explode('-', $basename) : $basename;
+                $_addon = (is_array($file_arr) && $file_arr[0] == 'backup') ? $file_arr[2] : 'all';
+                $_type = (is_array($file_arr) && $file_arr[0] == 'backup') ? $file_arr[3] : 'all';
+                $time = filemtime($filename);
+
+                if (!in_array($basename, $files)) {
+                    $backuplist[$time] =
+                        [
+                            'file' => $basename,
+                            'addon' => $_addon,
+                            'addon_name' => $_addon !== 'all' ? get_addon_info($_addon)['title'] : '全部',
+                            'type' => $_type,
+                            'date' => date("Y-m-d H:i:s", $time),
+                            'size' => format_bytes(filesize($filename))
+                        ];
+                    array_push($files, $basename);
+                    if ($addon !== 'all' && $addon !== $_addon) {
+                        unset($backuplist[$time]);
+                    } elseif ($type !== 'all' && $type !== $_type) {
+                        unset($backuplist[$time]);
+                    }
+                }
+            }
+            krsort($backuplist);
+
+            $result = array("total" => count($backuplist), "rows" => array_slice($backuplist, $offset, $limit));
+            return json($result);
+        }
+        $this->view->assign("group", $group);
+        $this->assignconfig("group", $group);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 备份下载
+     */
+    public function download()
+    {
+        $file = $this->request->request('file');
+        $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
+        if (!preg_match("/^backup\-([a-z0-9\-_\.]+)\.zip$/i", $file)) {
+            $this->error(__("Invalid parameters"));
+        }
+        $file = $backupDir . $file;
+        if (!is_file($file)) {
+            $this->error(__('File not found'));
+        } else {
+            header('Content-Type:text/html;charset=utf-8');
+            header('Content-disposition:attachment; filename=' . basename($file));
+            $result = readfile($file);
+            header('Content-length:' . filesize($file));
+            $this->success(__('Download completed'));
+        }
+    }
+
+    /**
+     * 恢复
+     */
+    public function restore($ids = '')
+    {
+        $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
+        if ($this->request->isPost()) {
+            $action = $this->request->request('action');
+            $file = $this->request->request('file');
+            if (!preg_match("/\.(zip|sql?)$/", $file)) {
+                $this->error(__("Invalid parameters"));
+            }
+            $file = $backupDir . $file;
+            $ext = pathinfo($file, PATHINFO_EXTENSION);
+            if ($action == 'restore') {
+                if (!class_exists('ZipArchive')) {
+                    $this->error(__("Zip tips 1"));
+                }
+                try {
+                    if ($ext == 'zip') {
+                        $dir = RUNTIME_PATH . 'database' . DS;
+                        if (!is_dir($dir)) {
+                            @mkdir($dir, 0755);
+                        }
+
+                        $zip = new ZipArchive;
+                        if ($zip->open($file) !== true) {
+                            throw new Exception(__('Can not open zip file'));
+                        }
+                        if (!$zip->extractTo($dir)) {
+                            $zip->close();
+                            throw new Exception(__('Can not unzip file'));
+                        }
+                        $zip->close();
+                        $filename = basename($file);
+                        $sqlFile = $dir . str_replace('.zip', '.sql', $filename);
+                    } else {
+                        $sqlFile = $file;
+                    }
+
+                    if (!is_file($sqlFile)) {
+                        throw new Exception(__('Sql file not found'));
+                    }
+                    $filesize = filesize($sqlFile);
+                    $list = Db::query('SELECT @@global.max_allowed_packet');
+                    if (isset($list[0]['@@global.max_allowed_packet']) && $filesize >= $list[0]['@@global.max_allowed_packet']) {
+                        Db::execute('SET @@global.max_allowed_packet = ' . ($filesize + 1024));
+                        //throw new Exception('备份文件超过配置max_allowed_packet大小,请修改Mysql服务器配置');
+                    }
+                    $sql = file_get_contents($sqlFile);
+
+                    Db::clear();
+                    //必须重连一次
+                    Db::connect([], true)->query("select 1");
+                    Db::getPdo()->exec($sql);
+                } catch (Exception $e) {
+                    $this->error($e->getMessage());
+                } catch (PDOException $e) {
+                    $this->error($e->getMessage());
+                }
+                $this->success(__('Restore successful'));
+            } elseif ($action == 'delete') {
+                unlink($file);
+                $this->success(__('Delete successful'));
+            }
+        }
+    }
+
+    /**
+     * 备份
+     */
+    public function backup()
+    {
+        $group = $this->request->get("group");
+        $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
+        if ($this->request->isPost()) {
+            $params = $this->request->post('row/a');
+            $tableList = [];
+            $list = \think\Db::query("SHOW TABLES");
+            foreach ($list as $key => $row) {
+                if ($params['addon'] == 'all') {
+                    $tableList[] = reset($row);
+                } else {
+                    $tmp = explode('_', reset($row));
+                    if ($this->prefix !== '' && $tmp[1] == $params['addon']) {
+                        $tableList[] = reset($row);
+                    } elseif ($this->prefix == '' && $tmp[0] == $params['addon']) {
+                        $tableList[] = reset($row);
+                    }
+                }
+            }
+            if (!class_exists('ZipArchive')) {
+                $this->error(__("Zip tips 2"));
+            }
+            $database = config('database');
+            try {
+                $backup = new Backup($database['hostname'], $database['username'], $database['database'], $database['password'], $database['hostport']);
+                $backup->setTable($tableList)->setIgnoreTable($params['ignore_tables'])->backup($params['addon'], $params['type'], $backupDir);
+            } catch (Exception $e) {
+                $this->error($e->getMessage());
+            }
+            $this->success(__('Backup successful'));
+        }
+        $this->view->assign("group", $group);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 上传文件
+     */
+    public function upload()
+    {
+        $group = $this->request->get("group");
+        //默认普通上传文件
+        $file = $this->request->file('file');
+        $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
+        if ($file) {
+            try {
+                $info = $file->rule('uniqid')->move($backupDir, $file->getInfo()['name']);
+                if ($info) {
+                    $this->success(__('Uploaded successful'));
+                }
+            } catch (Exception $e) {
+                $this->error($file->getError());
+            }
+        }
+    }
+
+    /**
+     * 字段选择
+     * @internal
+     */
+    public function selectnames()
+    {
+        //当前页
+        $page = $this->request->request("pageNumber");
+        //分页大小
+        $pagesize = $this->request->request("pageSize");
+
+        $q_word = (array) $this->request->request("q_word/a");
+
+        $word = $q_word[0];
+
+        $custom = (array) $this->request->request("custom/a");
+        if ($custom && is_array($custom)) {
+            $addon = $custom['addon'];
+        }
+
+        $tables = $this->template();
+
+        if (!empty($word)) {
+            $res_arr = [];
+            foreach ($tables as $table) {
+                if (!in_array($this->prefix . $addon . '_' . $table['table_name'], $this->getTables($addon))) {
+                    $res_arr[] = $table['table_name'] . '-' . $table['comment'];
+                }
+            }
+            $res_arr = array_filter($res_arr, function ($v) use ($word) {
+                return stripos($v, $word) !== false;
+            });
+            $res_arrs = array_values($res_arr);
+
+            $tableLists_arr = [];
+            foreach ($res_arrs as $res) {
+                $tableLists_arr[] = [
+                    'table_name' => explode('-', $res)[0],
+                    'comment' => explode('-', $res)[1]
+                ];
+            }
+            $tables = $tableLists_arr;
+        } else {
+            $res_arr = [];
+            foreach ($tables as $table) {
+                if (!in_array($this->prefix . $addon . '_' . $table['table_name'], $this->getTables($addon))) {
+                    $res_arr[] = $table['table_name'] . '-' . $table['comment'];
+                }
+            }
+            $res_arrs = array_values($res_arr);
+
+            $tableLists_arr = [];
+            foreach ($res_arrs as $res) {
+                $tableLists_arr[] = [
+                    'table_name' => explode('-', $res)[0],
+                    'comment' => explode('-', $res)[1]
+                ];
+            }
+            $tables = $tableLists_arr;
+        }
+
+        $result = array("total" => count($tables), "list" => array_slice($tables, ($page - 1) * $pagesize, $pagesize));
+
+        return json($result);
+    }
+
+    /**
+     * 编辑
+     */
+    public function table_edit()
+    {
+        $name = $this->request->get('name');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+        $tableInfo = Db::table("information_schema.TABLES")->field("*")->where(['TABLE_SCHEMA' => $this->dbName, 'TABLE_NAME' => $name])->find();
+        $row['name'] = $tableInfo['TABLE_NAME'];
+        $row['engine'] = $tableInfo['ENGINE'];
+        $row['charset'] = substr($tableInfo['TABLE_COLLATION'], 0, strpos($tableInfo['TABLE_COLLATION'], '_'));
+        $row['collation'] = $tableInfo['TABLE_COLLATION'];
+        $row['comment'] = $tableInfo['TABLE_COMMENT'];
+
+        if ($this->request->isPost()) {
+            $params = $this->request->post("row/a");
+            if ($params) {
+                $result = false;
+                $sql = [];
+                Db::startTrans();
+                try {
+                    if ($params['comment'] != $row['comment'])
+                        $sql[] = "ALTER TABLE  `{$name}` COMMENT='{$params['comment']}'";
+                    if ($params['engine'] != $row['engine'])
+                        $sql[] = "ALTER TABLE  `{$name}` ENGINE='{$params['engine']}'";
+                    if ($params['charset'] != $row['charset'])
+                        $sql[] = "ALTER TABLE `{$name}` CONVERT TO CHARACTER SET '{$params['charset']}' COLLATE '{$params['collation']}'";
+                    if ($params['collation'] != $row['collation'])
+                        $sql[] = "ALTER TABLE `{$name}` CONVERT TO CHARACTER SET '{$params['charset']}' COLLATE '{$params['collation']}'";
+                    if ($params['name'] != $row['name'])
+                        $sql[] = "ALTER TABLE  `{$name}`  RENAME TO  `{$params['name']}`;";
+
+                    $result = Db::batchQuery($sql);
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success();
+                } else {
+                    $this->error(__('No rows were inserted'));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', ''));
+        }
+
+        $this->view->assign("row", $row);
+        return $this->view->fetch();
+    }
+
+    /**
+     * 删除
+     */
+    public function table_del()
+    {
+        $name = $this->request->get('name');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', 'name'));
+        }
+        $result = false;
+        Db::startTrans();
+        try {
+            $sql = "DROP TABLE IF EXISTS `{$name}`;";
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $group = $this->prefix !== '' ? explode('_', $name)[1] : explode('_', $name)[0];
+            $tables = $this->getTables($group);
+            $this->success('删除成功', null, count($tables));
+        } else {
+            $this->error(__('No rows were deleted'));
+        }
+    }
+
+    /**
+     * 添加
+     * @internal
+     */
+    public function add()
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 编辑
+     * @param string $ids
+     * @internal
+     */
+    public function edit($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 删除
+     * @param string $ids
+     * @internal
+     */
+    public function del($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 批量更新
+     * @internal
+     * @param string $ids
+     * @return void
+     */
+    public function multi($ids = null)
+    {
+        $this->error('禁止访问');
+    }
+
+    /**
+     * 截/断表
+     */
+    public function truncate()
+    {
+        $name = $this->request->get('name');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', $name));
+        }
+        $result = false;
+        Db::startTrans();
+        try {
+            $sql = "TRUNCATE TABLE `{$name}`;";
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $this->success(__('Truncate table %s done', $name));
+        } else {
+            $this->error(__('Truncate table %s fail', $name));
+        }
+    }
+
+    /**
+     * 优化表
+     */
+    public function optimize()
+    {
+        $name = $this->request->get('name');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', $name));
+        }
+        $result = false;
+        Db::startTrans();
+        try {
+            $sql = "OPTIMIZE TABLE `{$name}`;";
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $this->success(__('Optimize table %s done', $name));
+        } else {
+            $this->error(__('Optimize table %s fail', $name));
+        }
+    }
+
+    /**
+     * 修复表
+     */
+    public function repair()
+    {
+        $name = $this->request->get('name');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', $name));
+        }
+        $result = false;
+        Db::startTrans();
+        try {
+            $sql = "REPAIR TABLE `{$name}`;";
+            $result = Db::execute($sql);
+            if (Db::getPdo()->inTransaction() == true) {
+                Db::commit();
+            }
+        } catch (\think\exception\PDOException $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        } catch (\think\Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+        if ($result !== false) {
+            $this->success(__('Repair table %s done', $name));
+        } else {
+            $this->error(__('Repair table %s fail', $name));
+        }
+    }
+
+    /**
+     * 复制表格/结构/数据
+     */
+    public function copy()
+    {
+        $name = $this->request->get('name');
+        $type = $this->request->get('type');
+        if ($name == NULL) {
+            $this->error(__('Parameter %s can not be empty', $name));
+        }
+        if ($this->request->isPost()) {
+            $table = $this->request->post("table");
+            if ($table) {
+                $result = false;
+                $sql = [];
+                if ($this->prefix !== '' && strpos($table, $this->prefix) !== 0) {
+                    $table = $this->prefix . $table;
+                }
+                Db::startTrans();
+                try {
+                    $_sql = "SHOW TABLES LIKE '{$table}'";
+                    $result = Db::query($_sql);
+                    if ($result) {
+                        $this->error("表 {$table} 已存在于数据库 {$this->dbName} 中");
+                    } else {
+                        //在此执行复制表的操作
+                        if ($type == 1) {
+                            $sql[] = "CREATE TABLE `{$table}` LIKE `{$name}`;";
+                        } else {
+                            $sql[] = "CREATE TABLE `{$table}` LIKE `{$name}`;";
+                            $sql[] = "INSERT INTO `{$table}` SELECT * FROM `{$name}`;";
+                        }
+                        $result = Db::batchQuery($sql);
+                    }
+
+                    if (Db::getPdo()->inTransaction() == true) {
+                        Db::commit();
+                    }
+                    $this->success(__('Copy table %s done', $name));
+                } catch (\think\exception\PDOException $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                } catch (\think\Exception $e) {
+                    Db::rollback();
+                    $this->error($e->getMessage());
+                }
+                if ($result !== false) {
+                    $this->success(__('Copy table %s done', $name));
+                } else {
+                    $this->error(__('Copy table %s fail', $name));
+                }
+            }
+            $this->error(__('Parameter %s can not be empty', $table));
+        }
+    }
+
+    /**
+     * 字符集
+     * @internal
+     */
+    public function getCollation()
+    {
+        $custom = (array) $this->request->request("custom/a");
+        $keyValue = $this->request->request('keyValue');
+        if ($custom && is_array($custom)) {
+            $charset = $custom['charset'];
+        }
+
+        if (!$keyValue) {
+            $list = $this->collationList[$charset];
+            foreach ($list as $k => $v) {
+                $list[$k] = ['collation' => $v];
+            }
+        } else {
+            $list[] = ['collation' => $keyValue];
+        }
+
+
+        $result = array("total" => count($list), "list" => $list);
+        return json($result);
+    }
+
+    /**
+     * 获取数据表
+     * @internal
+     */
+    public function get_table_list()
+    {
+        //当前页
+        $page = $this->request->request("pageNumber");
+        //分页大小
+        $pagesize = $this->request->request("pageSize");
+        $custom = (array) $this->request->request("custom/a");
+        $addon = 'all';
+        if ($custom && is_array($custom)) {
+            $addon = $custom['addon'];
+        }
+
+        $tableList = [];
+        $list = \think\Db::query("SHOW TABLES");
+        foreach ($list as $key => $row) {
+            if ($addon == 'all') {
+                $tableList[$key] = ['table_name' => reset($row)];
+            } else {
+                $tmp = explode('_', reset($row));
+                if ($this->prefix !== '' && $tmp[1] == $addon) {
+                    $tableList[] = ['table_name' => reset($row)];
+                } elseif ($this->prefix == '' && $tmp[0] == $addon) {
+                    $tableList[] = ['table_name' => reset($row)];
+                }
+            }
+        }
+
+        array_values($tableList);
+
+        $result = array("total" => count($tableList), "rows" => array_slice($tableList, ($page - 1) * $pagesize, $pagesize));
+        return json($result);
+    }
+
+    /**
+     * 获取数据库表
+     */
+    protected function getTables($group = 'all')
+    {
+        $tables = Db::getTables();
+
+        //数据表分组
+        $addons = get_addon_list();
+        $result = [];
+        $result['system'] = [];
+        foreach ($tables as $index => $table) {
+            foreach ($addons as $key => $value) {
+                $tmp = explode('_', $table);
+                if ($this->prefix !== '' && $tmp[1] == $key) {
+                    if ($value['state'] == 1) {
+                        $result[$key][] = $table;
+                    }
+                    unset($tables[$index]);
+                } elseif ($this->prefix == '' && $tmp[0] == $key) {
+                    if ($value['state'] == 1) {
+                        $result[$key][] = $table;
+                    }
+                    unset($tables[$index]);
+                }
+            }
+        }
+        $result['system'] = array_values($tables);
+        return $group === 'all' ? $result : (isset($result[$group]) ? $result[$group] : []);
+    }
+
+    /**
+     * 获取数据库分组
+     */
+    protected function getGroups($is_has = false)
+    {
+        $keyNames = array_keys($this->getTables());
+        //数据表分组
+        $addons = get_addon_list();
+        $groups = [];
+        foreach ($addons as $key => $value) {
+            if ($value['state'] == 1 && !in_array($value['name'], ['famysql', 'fadeveloper'])) {
+                $groups[$key] = $value['title'];
+                if ($is_has && !in_array($key, $keyNames)) {
+                    unset($groups[$key]);
+                }
+            }
+        }
+        return $groups;
+    }
+
+    private function template()
+    {
+        $sqlFile = ADDON_PATH . 'famysql' . DS . 'data' . DS . 'tables.ini';
+
+        $file_handle = fopen($sqlFile, "r");
+        $file_content = fread($file_handle, filesize($sqlFile));
+        $sqls = explode(';', $file_content);
+        array_pop($sqls);
+
+        $result = [];
+        foreach ($sqls as $key => $sql) {
+            preg_match('/CREATE TABLE IF NOT EXISTS `([^`]+)`/i', $sql, $matches);
+            preg_match("/COMMENT='([^`]+)'/i", $sql, $cmatches);
+            $result[$key]['table_name'] = $matches ? str_replace("__PREFIX__", '', $matches[1]) : '';
+            $result[$key]['comment'] = $cmatches ? $cmatches[1] : '';
+            $result[$key]['sql'] = ltrim($sql);
+        }
+
+        fclose($file_handle);
+
+        return $result;
+    }
+
+    /**
+     * 检查插件依赖
+     * @internal
+     * @return void
+     */
+    public function check()
+    {
+        $table_name = $this->request->request('table_name');
+        $addonname = $this->request->request('addon_name');
+        $addon_name = 'fadeveloper';
+        $info = get_addon_info($addon_name);
+        $addonArr = [
+            'fadeveloper' => 'FastAdmin插件开发工具'
+        ];
+        if (!$info || !$info['state']) {
+            $this->error('请检查对应插件' . (isset($addonArr[$addon_name]) ? "《{$addonArr[$addon_name]}》" : "") . '是否安装且启动', 'addon/index');
+        }
+
+        $this->redirect('fadeveloper/command/crud?addon_name=' . $addonname . '&table_name=' . $table_name);
+    }
+}

+ 22 - 0
application/admin/lang/zh-cn/famysql/field.php

@@ -0,0 +1,22 @@
+<?php
+
+return [
+    'Name'                        => '字段名称',
+    'Suffix'                      => '字段后缀',
+    'Type'                        => '字段类型',
+    'Length'                      => '字段长度',
+    'Set vlaue'                   => '枚举/设置值',
+    'Default'                     => '默认值',
+    'Is_null'                     => '非空?',
+    'Unsigned'                    => 'unsigned',
+    'Zerofill'                    => 'zerofill?',
+    'Comment'                     => '注释',
+    'Remark'                      => '备注',
+    'Comment placeholder'         => '注释',
+    'Remark placeholder'          => '备注',
+    'Default placeholder'         => '字段默认值,错误的默认值会保存失败',
+    'Length placeholder'          => '字段长度,浮点类型可用“10,2”方式表示',
+    'Name placeholder'            => '字段名称,不可重复',
+    'Batch generate'              => '批量生成',
+    'Table Operate'               => '表操作',
+];                

+ 8 - 0
application/admin/lang/zh-cn/famysql/index.php

@@ -0,0 +1,8 @@
+<?php
+
+return [
+    'Name'                  => '索引名',
+    'Column_name'           => '列名',
+    'Non_unique'            => '索引类型',
+    'Table Operate'         => '表操作',
+];                

+ 50 - 0
application/admin/lang/zh-cn/famysql/table.php

@@ -0,0 +1,50 @@
+<?php
+
+return [
+    'Copy 1'                         => '复制表结构',
+    'Copy 2'                         => '复制表结构及数据',
+    'Truncate'                       => '清空数据',
+    'Optimize'                       => '优化表',
+    'Repair'                         => '修复表',
+    'Table Operate'                  => '表操作',
+    'Optimize table %s done'         => '优化表[%s]成功',
+    'Repair table %s done'           => '修复表[%s]成功',
+    'Truncate table %s done'         => '清空表[%s]成功',
+    'Copy table %s done'             => '复制表[%s]成功',
+    'Optimize table %s fail'         => '优化表[%s]失败',
+    'Repair table %s fail'           => '修复表[%s]失败',
+    'Truncate table %s fail'         => '清空表[%s]失败',
+    'Copy table %s fail'             => '复制表[%s]失败',
+    'Backup and Restore'             => '备份与还原',
+    'Backup now'                     => '立即备份',
+    'File'                           => '文件',
+    'Size'                           => '大小',
+    'Date'                           => '备份日期',
+    'Delete successful'              => '删除成功',
+    'Backup successful'              => '备份成功',
+    'Restore successful'             => '还原成功',
+    'Can not open zip file'          => '无法打开备份文件',
+    'Can not unzip file'             => '无法解压备份文件',
+    'Invalid parameters'             => '无效参数',
+    'File not found'                 => '找不到文件',
+    'Download completed'             => '下载完成',
+    'Zip tips 1'                     => '服务器缺少php-zip组件,无法进行还原操作',
+    'Sql file not found'             => '找不到sql文件',
+    'Zip tips 2'                     => '服务器缺少php-zip组件,无法进行备份操作',
+    'Uploaded successful'            => '上传成功',
+    'Batch_add'                      => '快速建表',
+    'Addon'                          => '插件',
+    'Name'                           => '表名称',
+    'Engine'                         => '引擎',
+    'Charset'                        => '字符集',
+    'Collation'                      => '排序规则',
+    'Comment'                        => '注释',
+    'Comment placeholder'            => '注释',
+    'Name placeholder'               => '表名称(不包含表前缀及插件名,例article,结果为:表前缀+插件名+article)',
+    'Copy'                           => '复制表',
+    'More Table Operate'             => '更多表操作',
+    'Index manager'                  => '索引管理',
+    'Field manager'                  => '字段管理',
+    'Download file'                  => '下载文件',
+    'Rows'                           => '行数',
+];                

+ 19 - 0
application/admin/view/famysql/field/create.html

@@ -0,0 +1,19 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-column_name" data-rule="required" data-source="famysql/field/selectfields" data-format-item="#titletpl" data-params='{"custom[table]":"{$name|htmlentities}"}' data-primary-key="column_name" data-field="column_name" data-multiple="true" data-pagination="true" data-page-size="10" class="form-control selectpage" name="row[column_name]" type="text" />			
+        </div>
+    </div>    
+    <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>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>
+<script type="text/html" id="titletpl">
+    <%=column_name%> - <%=comment%>
+</script>

+ 114 - 0
application/admin/view/famysql/field/field_add.html

@@ -0,0 +1,114 @@
+<form id="field_add-form" class="field_add-form form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+	<div class="panel-heading"><div class="panel-lead text-red"><b>温馨提示:</b>增加字段后,需要重新生成CRUD,不然新增加的字段不会生效!</div></div>
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-name" data-rule="required" class="form-control" placeholder="{:__('Name placeholder')}" name="row[name]" type="text" />
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Suffix')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<!--给select一个固定的高度-->
+			<select id="field-suffix" class="form-control  form-selection" name="row[suffix]" style="height:31px;">
+				<option>无</option>
+				{foreach name="suffixList" item="vo"}
+                <option value="{$vo}">{$vo}</option>
+                {/foreach}
+			</select>
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-type" data-rule="required" class="form-control selectpage"
+				data-source="famysql/field/getType" data-field="type" data-primary-key="type" name="row[type]" type="text" value="">			
+		</div>
+	</div>
+
+	<div class="form-group form-input-basic hidden">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Length')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<table class="table fieldlist" data-template="basictpl" data-name="row[length]">
+				<tr>
+					<td>{:__('Set vlaue')}</td>
+				</tr>
+				<tr>
+					<td colspan="1"><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></td>
+				</tr>
+			</table>
+
+			<!--请注意实际开发中textarea应该添加个hidden进行隐藏-->
+			<textarea name="row[length]" class="form-control hidden" disabled cols="30" rows="5">[]</textarea>
+			<script id="basictpl" type="text/html">
+				<tr class="form-inline">
+					<td><input type="text" name="<%=name%>[<%=index%>][vo]" class="form-control" size="15" value="<%=row.vo%>" placeholder="{:__('Set vlaue')}"/></td>
+					<td>
+						<!--下面的两个按钮务必保留-->
+						<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>
+		</div>
+	</div>
+
+	<div class="form-group form-input-length">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Length')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-length" data-rule="required" class="form-control" placeholder="{:__('Length placeholder')}" name="row[length]" type="text" />
+		</div>
+	</div>
+
+	<div class="form-group form-input-default">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Default')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-default" data-rule="" class="form-control" placeholder="{:__('Default placeholder')}" name="row[default]" type="text" />
+		</div>
+	</div>
+
+	<div class="form-group form-input-is_null">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Is_null')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[is_null]', ['1'=>'否','0'=>'是'])}
+		</div>
+	</div>
+
+	<div class="form-group form-input-unsigned hidden">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Unsigned')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[unsigned]', ['0'=>'否','1'=>'是'])}
+		</div>
+	</div>
+
+	<div class="form-group form-input-zerofill hidden">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Zerofill')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[zerofill]', ['0'=>'否','1'=>'是'])}
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Comment')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-comment"  class="form-control" placeholder="{:__('Comment placeholder')}" name="row[comment]" type="text" />
+		</div>
+	</div>
+
+	<div class="form-group form-input-remark hidden">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-remark"  class="form-control" readonly disabled placeholder="{:__('Remark placeholder')}" name="row[remark]" type="text" />
+		</div>
+	</div>
+
+	<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-success btn-embossed ">{:__('OK')}</button>
+			<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+		</div>
+	</div>
+</form>

+ 92 - 0
application/admin/view/famysql/field/field_edit.html

@@ -0,0 +1,92 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+	<div class="panel-heading"><div class="panel-lead text-red"><b>温馨提示:</b>更新字段名称后,需要重新生成CRUD,不然更新的字段不会生效!</div></div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" placeholder="{:__('Name placeholder')}" name="row[name]" type="text" value="{$row.name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-type" data-rule="required" class="form-control selectpage"
+				data-source="famysql/field/getType" data-field="type" data-primary-key="type" name="row[type]" type="text" value="{$row.type|htmlentities}">
+        </div>
+    </div>   
+
+	<div class="form-group form-input-basic{if !$is_enum} hidden{/if}">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Length')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<table class="table fieldlist" data-template="basictpl" data-name="row[length]">
+				<tr>
+					<td>{:__('Set vlaue')}</td>
+				</tr>
+				<tr>
+					<td colspan="1"><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></td>
+				</tr>
+			</table>
+
+			<!--请注意实际开发中textarea应该添加个hidden进行隐藏-->
+			<textarea name="row[length]" {if !$is_enum}disabled{/if} class="form-control hidden" cols="30" rows="5">{if $is_enum}{$row.length|htmlentities}{/if}</textarea>
+			<script id="basictpl" type="text/html">
+				<tr class="form-inline">
+					<td><input type="text" name="<%=name%>[<%=index%>][vo]" class="form-control" size="15" value="<%=row.vo%>" placeholder="{:__('Set vlaue')}"/></td>
+					<td>
+						<!--下面的两个按钮务必保留-->
+						<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>
+		</div>
+	</div>
+
+	<div class="form-group form-input-length{if $is_enum || $is_length} hidden{/if}">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Length')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-length" class="form-control"{if $is_enum || $is_length} disabled{/if} placeholder="{:__('Length placeholder')}" name="row[length]" type="text" value="{$row.length|htmlentities}" />
+		</div>
+	</div>
+
+	<div class="form-group form-input-default{if $is_length} hidden{/if}">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Default')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-default" data-rule=""{if $is_length} disabled{/if} class="form-control" placeholder="{:__('Default placeholder')}" name="row[default]" type="text" value="{$row.default|htmlentities}" />
+		</div>
+	</div>
+
+	<div class="form-group form-input-is_null">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Is_null')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[is_null]', ['1'=>'否','0'=>'是'], $row['is_null'] == 'NO' ? 0 : 1)}
+		</div>
+	</div>
+
+	<div class="form-group form-input-unsigned{if !$is_int} hidden{/if}">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Unsigned')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[unsigned]', ['0'=>'否','1'=>'是'], $row['unsigned'])}
+		</div>
+	</div>
+
+	<div class="form-group form-input-zerofill{if !$is_int} hidden{/if}">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Zerofill')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[zerofill]', ['0'=>'否','1'=>'是'], $row['zerofill'])}
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Comment')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-comment"  class="form-control" placeholder="{:__('Comment placeholder')}" name="row[comment]" type="text" value="{$row.comment|htmlentities}" />
+		</div>
+	</div>    
+    <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>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 29 - 0
application/admin/view/famysql/field/fields.html

@@ -0,0 +1,29 @@
+<div class="panel panel-default panel-intro">
+	<input id="assign-data-name" type="hidden" value="{$name|htmlentities}">
+	<input id="assign-data-is_admin" type="hidden" value="{$is_admin|htmlentities}">
+	{:build_heading()}
+	<div class="panel-body">
+		<div id="myTabContent" class="tab-content">
+			<div class="panel-heading"><div class="panel-lead text-red"><b>温馨提示:</b>删除字段后,需要重新生成CRUD,否则会提示找不到字段!</div></div>
+			<div class="tab-pane fade active in" id="one">
+				<div class="widget-body no-padding">
+					<div id="toolbar" class="toolbar">
+						{if $is_admin}
+						{:build_toolbar('refresh,add')}
+						{if $auth->check('famysql/field/create')}
+                        <a class="btn btn-warning btn-dialog" href="{:url('famysql.field/create',['name'=>$name,'is_admin'=>$is_admin])}" title="{:__('Batch generate')}"><i class="fa fa-plus"></i> {:__('Batch generate')}</a>
+                        {/if}
+						{else}
+						{:build_toolbar('refresh')}
+						{/if}												
+					</div>
+					<table id="table" class="table table-striped table-bordered table-hover"
+						   data-operate-field_edit="{:$auth->check('famysql/field/field_edit')}"
+						   data-operate-field_del="{:$auth->check('famysql/field/field_del')}"
+						   width="100%">
+					</table>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>

+ 34 - 0
application/admin/view/famysql/index/index_add.html

@@ -0,0 +1,34 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" />
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Column_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-column_name" data-rule="required" data-source="famysql/index/selectpage" data-params='{"custom[table]":"{$table|htmlentities}"}' data-primary-key="column_name" data-field="column_name" data-pagination="true" data-page-size="10" data-multiple="true" class="form-control selectpage" name="row[column_name]" type="text" />			
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Non_unique')}:</label>
+        <div class="col-xs-12 col-sm-8">
+			<!--给select一个固定的高度-->
+			<select id="field-type" class="form-control form-selection" name="row[non_unique]" style="height:31px;">
+				<option value="">----请选择----</option>
+				{foreach name="indexList" item="vo"}
+                <option value="{$key}" {in name="key" value=""}selected{/in}>{$vo}</option>
+                {/foreach}
+			</select>
+        </div>
+    </div>    
+    <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>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 34 - 0
application/admin/view/famysql/index/index_edit.html

@@ -0,0 +1,34 @@
+<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}">
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Column_name')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-column_name" data-rule="required" data-source="famysql/index/selectpage" data-params='{"custom[table]":"{$table|htmlentities}"}' data-primary-key="column_name" data-field="column_name" data-pagination="true" data-page-size="10" data-multiple="true" class="form-control selectpage" name="row[column_name]" type="text" value="{$row.column_name|htmlentities}">			
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Non_unique')}:</label>
+        <div class="col-xs-12 col-sm-8">
+			<!--给select一个固定的高度-->
+			<select id="field-type" class="form-control form-selection" name="row[non_unique]" style="height:31px;">
+				<option value="">----请选择----</option>
+				{foreach name="indexList" item="vo"}
+                <option value="{$key}" {in name="key" value="$row.non_unique"}selected{/in}>{$vo}</option>
+                {/foreach}
+			</select>
+        </div>
+    </div>    
+    <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>
+            <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+        </div>
+    </div>
+</form>

+ 25 - 0
application/admin/view/famysql/index/indexs.html

@@ -0,0 +1,25 @@
+<div class="panel panel-default panel-intro">
+	<input id="assign-data-name" type="hidden" value="{$name|htmlentities}">
+	<input id="assign-data-is_admin" type="hidden" value="{$is_admin|htmlentities}">
+	{: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">
+						{if $is_admin}
+						{:build_toolbar('refresh,add')}
+						{else}
+						{:build_toolbar('refresh')}
+						{/if}
+					</div>
+					<table id="table" class="table table-striped table-bordered table-hover"
+						   data-operate-index_edit="{:$auth->check('famysql/index/index_edit')}"
+						   data-operate-index_del="{:$auth->check('famysql/index/index_del')}"
+						   width="100%">
+					</table>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>

+ 39 - 0
application/admin/view/famysql/table/backup.html

@@ -0,0 +1,39 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+    {if $group == null}
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('插件')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <select id="c-addon" data-rule="required" class="form-control selectpicker" name="row[addon]">
+                {foreach name="groups" item="vo"}
+                <option value="{$key}" {in name="key" value="" }selected{/in}>{$vo}</option>
+                {/foreach}
+            </select>
+        </div>
+    </div>
+    {else}
+	<input id="c-addon" data-rule="required" class="form-control form-control" placeholder="插件" name="row[addon]"
+				type="hidden" value="{$group|htmlentities}">
+    {/if}
+    
+    <div class="form-group">
+        <label for="c-type" class="control-label col-xs-12 col-sm-2">{:__('类型')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            {:build_radios('row[type]', ['0'=>__('结构唯一'), '1'=>__('仅有数据'), '2'=>__('结构和数据')])}
+        </div>
+    </div>
+
+    <div class="form-group">
+        <label for="c-ignore_tables" class="control-label col-xs-12 col-sm-2">{:__('忽略的表')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input type="text" id="c-ignore_tables" name="row[ignore_tables]" class="form-control selectpage" data-source="famysql/table/get_table_list" data-field="table_name" data-primary-key="table_name" data-pagination="true" data-multiple="true" placeholder="请选择忽略的表" />
+        </div>
+    </div>
+
+    <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-success btn-embossed disabled">{:__('OK')}</button>
+			<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+		</div>
+	</div>
+</form>

+ 41 - 0
application/admin/view/famysql/table/backuplist.html

@@ -0,0 +1,41 @@
+<div class="panel panel-default panel-intro">
+    {if $group == null}
+    <div class="panel-heading">
+        {:build_heading(null,FALSE)}        
+        <ul class="nav nav-tabs nav-addon">
+            <li class="active"><a href="javascript:;" data-id="">{:__('All')}</a></li>
+        </ul>        
+    </div>
+    {/if}
+
+    <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')}" data-force-refresh="false"><i class="fa fa-refresh"></i> </a>
+                        {if $auth->check('famysql/table/backup')}
+                        <a href="{:url('famysql.table/backup',['group'=>$group])}" class="btn btn-success btn-backup btn-dialog" data-area='["80%","80%"]' title="{:__('Backup and Restore')}"><i class="fa fa-compress"></i> {:__('Backup now')}</a>
+                        {/if}
+
+                        {if $auth->check('famysql/table/upload')}
+                        <form style="width: 110px; display: inline-block;" class="form-inline" role="form"><button type="button" id="faupload-local" class="btn btn-primary faupload" data-input-id="c-local" data-url="{:url('famysql.table/upload',['group'=>$group])}" data-mimetype="zip,sql"><i class="fa fa-upload"></i> {:__("从本地上传")}</button></form>
+                        {/if}                                                
+                                               
+                        <div class="btn-group">
+                            <a href="#" class="btn btn-info btn-switch active btn-mini-xs" data-type="all">{:__('全部')}</a>
+                            <a href="#" class="btn btn-info btn-switch btn-mini-xs" data-type="0">{:__('结构唯一')}</a>
+                            <a href="#" class="btn btn-info btn-switch btn-mini-xs" data-type="1">{:__('仅有数据')}</a>
+                            <a href="#" class="btn btn-info btn-switch btn-mini-xs" data-type="2">{:__('结构和数据')}</a>
+                        </div>
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover" width="100%">
+
+                    </table>
+
+                </div>
+            </div>
+
+        </div>
+    </div>
+</div>

+ 35 - 0
application/admin/view/famysql/table/index.html

@@ -0,0 +1,35 @@
+<div class="panel panel-default panel-intro">
+    <div class="panel-heading">
+        {:build_heading(null,FALSE)}
+        {if $group == null}
+        <ul class="nav nav-tabs" data-field="group">
+            <li class="active"><a href="#system" data-value="system" data-toggle="tab">{:__('系统')}</a></li>
+            {foreach name="groups" item="vo"}
+            <li><a href="#{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
+            {/foreach}
+        </ul>
+        {/if}
+
+    </div>
+    <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,add')}
+                        {if $auth->check('famysql/table/table_batch_add')}
+                        <a class="btn btn-warning btn-dialog" href="{:url('famysql.table/table_batch_add',['group'=>$group])}" title="{:__('Batch_add')}"><i class="fa fa-list-alt"></i> {:__('Batch_add')}</a>
+                        {/if}
+                        {if $auth->check('famysql/table/backuplist')}
+                        <a class="btn btn-info btn-dialog" href="{:url('famysql.table/backuplist',['group'=>$group])}" data-area='["80%","80%"]' title="{:__('Backup and Restore')}"><i class="fa fa-compress"></i> {:__('Backup and Restore')}</a>
+                        {/if}                        
+                    </div>
+                    <table id="table" class="table table-striped table-bordered table-hover"
+                        data-operate-edit="{:$auth->check('famysql/table/table_edit')}"
+                        data-operate-del="{:$auth->check('famysql/table/table_del')}" width="100%">
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 67 - 0
application/admin/view/famysql/table/table_add.html

@@ -0,0 +1,67 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+	{if $group == null}
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Addon')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<select id="c-addon" data-rule="required" class="form-control selectpicker" name="row[addon]">
+				{foreach name="groupsList" item="vo"}
+				<option value="{$key}" {in name="key" value="" }selected{/in}>{$vo}</option>
+				{/foreach}
+			</select>
+		</div>
+	</div>
+	{else}
+	<input id="c-addon" data-rule="required" class="form-control" placeholder="{:__('Addon')}" name="row[addon]"
+				type="hidden" value="{$group}">
+    {/if}
+	
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-name" data-rule="required" class="form-control" placeholder="{:__('Name placeholder')}" name="row[name]"
+				type="text" value="">
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Engine')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[engine]', ['InnoDB'=>__('InnoDB'), 'MyISAM'=>__('MyISAM')])}
+		</div>
+	</div>
+	
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Charset')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<select id="c-charset" data-rule="required" class="form-control selectpicker" name="row[charset]">
+				{foreach name="charsetList" item="vo"}
+				<option value="{$vo}" {in name="vo" value="" }selected{/in}>{$vo}</option>
+				{/foreach}
+			</select>
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Collation')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-collation" data-rule="required" class="form-control selectpage"
+				data-source="famysql/table/getCollation" data-field="collation" data-primary-key="collation" name="row[collation]" type="text" value="utf8mb4_general_ci">
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Comment')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-comment" data-rule="required" class="form-control" placeholder="{:__('Comment placeholder')}"
+				name="row[comment]" type="text" value="">
+		</div>
+	</div>
+
+	<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-success btn-embossed disabled">{:__('OK')}</button>
+			<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+		</div>
+	</div>
+</form>

+ 35 - 0
application/admin/view/famysql/table/table_batch_add.html

@@ -0,0 +1,35 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+	{if $group == null}
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Addon')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<select id="c-addon" data-rule="required" class="form-control selectpicker" name="row[addon]">
+				{foreach name="groupsList" item="vo"}
+				<option value="{$key}" {in name="key" value="" }selected{/in}>{$vo}</option>
+				{/foreach}
+			</select>
+		</div>
+	</div>
+	{else}
+	<input id="c-addon" data-rule="required" class="form-control form-control" placeholder="{:__('Addon')}" name="row[addon]"
+				type="hidden" value="{$group}">
+    {/if}
+	
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-name" data-rule="required" data-source="famysql/table/selectnames" data-format-item="#titletpl" data-primary-key="table_name" data-field="table_name" data-multiple="true" data-pagination="true" data-page-size="10" class="form-control selectpage" name="row[name]" type="text" />
+		</div>
+	</div>
+
+	<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-success btn-embossed disabled">{:__('OK')}</button>
+			<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+		</div>
+	</div>
+</form>
+<script type="text/html" id="titletpl">
+    <%=table_name%> - <%=comment%>
+</script>

+ 52 - 0
application/admin/view/famysql/table/table_edit.html

@@ -0,0 +1,52 @@
+<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-name" data-rule="required" class="form-control" placeholder="{:__('Name placeholder')}" name="row[name]" type="text" value="{$row.name|htmlentities}">
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Engine')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			{:build_radios('row[engine]', ['InnoDB'=>__('InnoDB'), 'MyISAM'=>__('MyISAM')], $row['engine'])}
+		</div>
+	</div>
+	
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Charset')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<select id="c-charset" data-rule="required" class="form-control selectpicker" name="row[charset]">
+				{foreach name="charsetList" item="vo"}
+				<option value="{$vo}" {in name="vo" value="$row.charset" }selected{/in}>{$vo}</option>
+				{/foreach}
+			</select>
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Collation')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-collation" data-rule="required" class="form-control selectpage"
+				data-source="famysql/table/getCollation" data-field="collation" data-primary-key="collation" name="row[collation]" type="text" value="{$row.collation|htmlentities}">
+		</div>
+	</div>
+
+	<div class="form-group">
+		<label class="control-label col-xs-12 col-sm-2">{:__('Comment')}:</label>
+		<div class="col-xs-12 col-sm-8">
+			<input id="c-comment" data-rule="required" class="form-control" name="row[comment]" type="text" value="{$row.comment|htmlentities}">
+		</div>
+	</div>
+	<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-success btn-embossed disabled">{:__('OK')}</button>
+			<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
+		</div>
+	</div>
+</form>
+
+
+

+ 1 - 0
application/extra/addons.php

@@ -14,6 +14,7 @@ return [
         ],
         'upgrade' => [
             'exam',
+            'famysql',
         ],
         'user_sidenav_after' => [
             'poster',

+ 352 - 0
public/assets/js/backend/famysql/field.js

@@ -0,0 +1,352 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        fields: function () {
+            // 初始化表格参数配置
+            Table.config.dragsortfield = 'name';
+
+            var name = $("#assign-data-name").val();
+            var is_admin = $("#assign-data-is_admin").val();
+            Table.api.init({
+                extend: {
+                    index_url: 'famysql/field/fields?name=' + name + '&is_admin=' + is_admin,
+                    add_url: 'famysql/field/field_add?name=' + name,
+                    dragsort_url: 'famysql/field/field_drag?name=' + name,
+                },
+                showExport: false,//导出按钮导出整个表的所有行
+                showToggle: false,//切换卡片视图和表格视图
+                showColumns: false,//切换显示隐藏列
+                search: false,//关闭快速搜索
+                commonSearch: false,//关闭通用搜索
+            });
+
+            var table = $("#table");
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        { field: 'id', title: __('Id') },
+                        { field: 'name', title: __('Name') },
+                        { field: 'type', title: __('Type') },
+                        { field: 'length', title: __('Length') },
+                        { field: 'default', title: __('Default') },
+                        { field: 'is_null', title: __('Is_null') },
+                        { field: 'unsigned', title: __('Unsigned') },
+                        { field: 'comment', title: __('Comment') },
+                        {
+                            field: 'operate',
+                            title: __('Table Operate'),
+                            buttons: [
+                                {
+                                    name: 'dragsort',
+                                    icon: 'fa fa-arrows',
+                                    title: __('Drag to sort'),
+                                    extend: 'data-toggle="tooltip"',
+                                    classname: 'btn btn-xs btn-primary btn-dragsort',
+                                },
+                                {
+                                    name: 'field_edit',
+                                    icon: 'fa fa-pencil',
+                                    title: __('Edit'),
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/field/field_edit?table=" + name + "&field=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-success btn-dialog'
+                                },
+                                {
+                                    name: 'field_del',
+                                    icon: 'fa fa-trash',
+                                    title: __('Del'),
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/field/field_del?table=" + name + "&field=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-danger btn-ajax',
+                                    confirm: function (row) {
+                                        return '是否确定删除该“' + row.name + '”字段,不可恢复?';
+                                    },
+                                    success: function (data, ret) {
+                                        $(".btn-refresh").trigger("click"); //刷新数据
+                                    },
+                                    error: function (data, ret) {
+                                        console.log(data, ret);
+                                        Layer.alert(ret.msg);
+                                        return false;
+                                    }
+                                }
+                            ],
+                            table: table,
+                            events: Table.api.events.operate,
+                            formatter: function (value, row, index) {
+                                var that = $.extend({}, this);
+                                if (!row.is_admin || row.name == 'id') {
+                                    return '-';
+                                }
+                                return Table.api.formatter.operate.call(that, value, row, index);
+                            }
+                        }
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        field_add: function () {
+            Controller.api.bindevent();
+            var ints = ["int", "tinyint", "smallint", "mediumint", "bigint", "float", "double", "decimal"]
+            $(document).on("change", "#field-suffix", function () {
+                var suffix = $(this).val();
+                if (suffix !== "无") {
+                    $.ajax({
+                        type: "POST",
+                        url: "famysql/field/getSuffix",
+                        data: { name: suffix },
+                        async: false
+                    }).done(function (data) {
+                        $("#c-type").val((data.type)[0]);
+                        var type = $("#c-type").val();
+                        var unsigned_show = $(".form-input-unsigned").hasClass("hidden");
+                        var length_show = $(".form-input-length").hasClass("hidden");
+                        var basic_show = $(".form-input-basic").hasClass("hidden");
+                        var default_show = $(".form-input-default").hasClass("hidden");
+                        if (ints.indexOf(type) !== -1) {
+                            if (unsigned_show) {
+                                $(".form-input-unsigned").removeClass("hidden");
+                                $(".form-input-zerofill").removeClass("hidden");
+                                $(".form-input-unsigned input").attr("disabled", false);
+                                $(".form-input-zerofill input").attr("disabled", false);
+                            }
+                            if (!length_show) {
+                                $(".form-input-length").removeClass("hidden");
+                                $(".form-input-length input").attr("disabled", false);
+                            }
+                            if (!basic_show) {
+                                $(".form-input-basic").addClass("hidden");
+                                $(".form-input-basic textarea").attr("disabled", "disabled");
+                            }
+                            if (default_show) {
+                                $(".form-input-default").removeClass("hidden");
+                                $(".form-input-default input").attr("disabled", false);
+                            }
+                        } else if (type == 'enum' || type == 'set') {
+                            if (!unsigned_show) {
+                                $(".form-input-unsigned").addClass("hidden");
+                                $(".form-input-zerofill").addClass("hidden");
+                                $(".form-input-unsigned input").attr("disabled", "disabled");
+                                $(".form-input-zerofill input").attr("disabled", "disabled");
+                            }
+                            if (!length_show) {
+                                $(".form-input-length").addClass("hidden");
+                                $(".form-input-length input").attr("disabled", "disabled");
+                            }
+                            if (basic_show) {
+                                $(".form-input-basic").removeClass("hidden");
+                                $(".form-input-basic textarea").attr("disabled", false);
+                            }
+                            if (default_show) {
+                                $(".form-input-default").removeClass("hidden");
+                                $(".form-input-default input").attr("disabled", false);
+                            }
+                        } else if (type == 'text' || type == 'longtext' || type == 'mediumtext') {
+                            if (!unsigned_show) {
+                                $(".form-input-unsigned").addClass("hidden");
+                                $(".form-input-zerofill").addClass("hidden");
+                                $(".form-input-unsigned input").attr("disabled", "disabled");
+                                $(".form-input-zerofill input").attr("disabled", "disabled");
+                            }
+                            if (!length_show) {
+                                $(".form-input-length").addClass("hidden");
+                                $(".form-input-length input").attr("disabled", "disabled");
+                            }
+                            if (!basic_show) {
+                                $(".form-input-basic").addClass("hidden");
+                                $(".form-input-basic textarea").attr("disabled", "disabled");
+                            }
+                            if (!default_show) {
+                                $(".form-input-default").addClass("hidden");
+                                $(".form-input-default input").attr("disabled", "disabled");
+                            }
+                        } else if (type == 'date' || type == 'datetime' || type == 'time' || type == 'year') {
+                            if (!unsigned_show) {
+                                $(".form-input-unsigned").addClass("hidden");
+                                $(".form-input-zerofill").addClass("hidden");
+                                $(".form-input-unsigned input").attr("disabled", "disabled");
+                                $(".form-input-zerofill input").attr("disabled", "disabled");
+                            }
+                            if (!length_show) {
+                                $(".form-input-length").addClass("hidden");
+                                $(".form-input-length input").attr("disabled", "disabled");
+                            }
+                            if (!basic_show) {
+                                $(".form-input-basic").addClass("hidden");
+                                $(".form-input-basic textarea").attr("disabled", "disabled");
+                            }
+                            if (!default_show) {
+                                $(".form-input-default").addClass("hidden");
+                                $(".form-input-default input").attr("disabled", "disabled");
+                            }
+                        } else {
+                            if (!unsigned_show) {
+                                $(".form-input-unsigned").addClass("hidden");
+                                $(".form-input-zerofill").addClass("hidden");
+                                $(".form-input-unsigned input").attr("disabled", "disabled");
+                                $(".form-input-zerofill input").attr("disabled", "disabled");
+                            }
+                            if (length_show) {
+                                $(".form-input-length").removeClass("hidden");
+                                $(".form-input-length input").attr("disabled", false);
+                            } else if (!length_show && !data.length) {
+                                $(".form-input-length").addClass("hidden");
+                                $(".form-input-length input").attr("disabled", "disabled");
+                            }
+                            if (!basic_show) {
+                                $(".form-input-basic").addClass("hidden");
+                                $(".form-input-basic textarea").attr("disabled", "disabled");
+                            }
+                            if (default_show) {
+                                $(".form-input-default").removeClass("hidden");
+                                $(".form-input-default input").attr("disabled", false);
+                            }
+                        }
+                        $("#c-length").val(data.length);
+                        $("#c-comment").val(data.comment);
+                        $("#c-remark").val(data.remark);
+                    })
+                    $(".form-input-remark").removeClass("hidden");
+                } else {
+                    $("#field_add-form").trigger("reset");
+                    $('#c-type').val("varchar");
+                    $(".form-input-remark").addClass("hidden");
+                    $(".form-input-remark input").attr("disabled", "disabled");
+                    $(".form-input-length").removeClass("hidden");
+                    $(".form-input-basic").addClass("hidden");
+                }
+
+                $("#c-type").selectPageRefresh();
+            });
+        },
+        field_edit: function () {
+            Controller.api.bindevent();
+        },
+        create: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                var ints = ["int", "tinyint", "smallint", "mediumint", "bigint", "float", "double", "decimal"]
+                $(document).on("change", "#c-type", function () {
+                    var type = $(this).val();
+                    var unsigned_show = $(".form-input-unsigned").hasClass("hidden");
+                    var length_show = $(".form-input-length").hasClass("hidden");
+                    var basic_show = $(".form-input-basic").hasClass("hidden");
+                    var default_show = $(".form-input-default").hasClass("hidden");
+                    if (ints.indexOf(type) !== -1) {
+                        if (unsigned_show) {
+                            $(".form-input-unsigned").removeClass("hidden");
+                            $(".form-input-zerofill").removeClass("hidden");
+                            $(".form-input-unsigned input").attr("disabled", false);
+                            $(".form-input-zerofill input").attr("disabled", false);
+                        }
+                        if (!length_show) {
+                            $(".form-input-length").removeClass("hidden");
+                            $(".form-input-length input").attr("disabled", false);
+                        }
+                        if (!basic_show) {
+                            $(".form-input-basic").addClass("hidden");
+                            $(".form-input-basic textarea").attr("disabled", "disabled");
+                        }
+                        if (default_show) {
+                            $(".form-input-default").removeClass("hidden");
+                            $(".form-input-default input").attr("disabled", false);
+                        }
+                    } else if (type == 'enum' || type == 'set') {
+                        if (!unsigned_show) {
+                            $(".form-input-unsigned").addClass("hidden");
+                            $(".form-input-zerofill").addClass("hidden");
+                            $(".form-input-unsigned input").attr("disabled", "disabled");
+                            $(".form-input-zerofill input").attr("disabled", "disabled");
+                        }
+                        if (!length_show) {
+                            $(".form-input-length").addClass("hidden");
+                            $(".form-input-length input").attr("disabled", "disabled");
+                        }
+                        if (basic_show) {
+                            $(".form-input-basic").removeClass("hidden");
+                            $(".form-input-basic textarea").attr("disabled", false);
+                        }
+                        if (default_show) {
+                            $(".form-input-default").removeClass("hidden");
+                            $(".form-input-default input").attr("disabled", false);
+                        }
+                    } else if (type == 'text' || type == 'longtext' || type == 'mediumtext') {
+                        if (!unsigned_show) {
+                            $(".form-input-unsigned").addClass("hidden");
+                            $(".form-input-zerofill").addClass("hidden");
+                            $(".form-input-unsigned input").attr("disabled", "disabled");
+                            $(".form-input-zerofill input").attr("disabled", "disabled");
+                        }
+                        if (!length_show) {
+                            $(".form-input-length").addClass("hidden");
+                            $(".form-input-length input").attr("disabled", "disabled");
+                        }
+                        if (!basic_show) {
+                            $(".form-input-basic").addClass("hidden");
+                            $(".form-input-basic textarea").attr("disabled", "disabled");
+                        }
+                        if (default_show) {
+                            $(".form-input-default").removeClass("hidden");
+                            $(".form-input-default input").attr("disabled", false);
+                        }
+                    } else if (type == 'date' || type == 'datetime' || type == 'time' || type == 'year') {
+                        if (!unsigned_show) {
+                            $(".form-input-unsigned").addClass("hidden");
+                            $(".form-input-zerofill").addClass("hidden");
+                            $(".form-input-unsigned input").attr("disabled", "disabled");
+                            $(".form-input-zerofill input").attr("disabled", "disabled");
+                        }
+                        if (!length_show) {
+                            $(".form-input-length").addClass("hidden");
+                            $(".form-input-length input").attr("disabled", "disabled");
+                        }
+                        if (!basic_show) {
+                            $(".form-input-basic").addClass("hidden");
+                            $(".form-input-basic textarea").attr("disabled", "disabled");
+                        }
+                        if (!default_show) {
+                            $(".form-input-default").addClass("hidden");
+                            $(".form-input-default input").attr("disabled", "disabled");
+                        }
+                    } else {
+                        if (!unsigned_show) {
+                            $(".form-input-unsigned").addClass("hidden");
+                            $(".form-input-zerofill").addClass("hidden");
+                            $(".form-input-unsigned input").attr("disabled", "disabled");
+                            $(".form-input-zerofill input").attr("disabled", "disabled");
+                        }
+                        if (length_show) {
+                            $(".form-input-length").removeClass("hidden");
+                            $(".form-input-length input").attr("disabled", false);
+                        }
+                        if (!basic_show) {
+                            $(".form-input-basic").addClass("hidden");
+                            $(".form-input-basic textarea").attr("disabled", "disabled");
+                        }
+                        if (default_show) {
+                            $(".form-input-default").removeClass("hidden");
+                            $(".form-input-default input").attr("disabled", false);
+                        }
+                    }
+                    $("#c-length").val("");
+                    $("#c-default").val("");
+                });
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 99 - 0
public/assets/js/backend/famysql/index.js

@@ -0,0 +1,99 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
+
+    var Controller = {
+        indexs: function () {
+            // 初始化表格参数配置
+
+            var name = $("#assign-data-name").val();
+            var is_admin = $("#assign-data-is_admin").val();
+            Table.api.init({
+                extend: {
+                    index_url: 'famysql/index/indexs?name=' + name + '&is_admin=' + is_admin,
+                    add_url: 'famysql/index/index_add?table=' + name,
+                },
+                showExport: false,//导出按钮导出整个表的所有行
+                showToggle: false,//切换卡片视图和表格视图
+                showColumns: false,//切换显示隐藏列
+                search: false,//关闭快速搜索
+                commonSearch: false,//关闭通用搜索
+                // pagination: false,
+            });
+
+            var table = $("#table");
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        { field: 'name', title: __('Name') },
+                        { field: 'column_name', title: __('Column_name') },
+                        { field: 'non_unique', title: __('Non_unique') },
+                        {
+                            field: 'operate',
+                            title: __('Table Operate'),
+                            buttons: [
+                                {
+                                    name: 'index_edit',
+                                    icon: 'fa fa-pencil',
+                                    title: __('Edit'),
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/index/index_edit?table=" + name + "&name=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-success btn-dialog'
+                                },
+                                {
+                                    name: 'index_del',
+                                    icon: 'fa fa-trash',
+                                    title: __('Del'),
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/index/index_del?table=" + name + "&name=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-danger btn-ajax',
+                                    confirm: function (row) {
+                                        return '是否确定删除该“' + row.name + '”索引,不可恢复?';
+                                    },
+                                    success: function (data, ret) {
+                                        $(".btn-refresh").trigger("click"); //刷新数据
+                                    },
+                                    error: function (data, ret) {
+                                        console.log(data, ret);
+                                        Layer.alert(ret.msg);
+                                        return false;
+                                    }
+                                }
+                            ],
+                            table: table,
+                            events: Table.api.events.operate,
+                            formatter: function (value, row, index) {
+                                var that = $.extend({}, this);
+                                if (!row.is_admin || row.non_unique === 'PRIMARY') {
+                                    return '-';
+                                }
+                                return Table.api.formatter.operate.call(that, value, row, index);
+                            }
+                        }
+                    ]
+                ]
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        index_add: function () {
+            Controller.api.bindevent();
+        },
+        index_edit: function () {
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

+ 476 - 0
public/assets/js/backend/famysql/table.js

@@ -0,0 +1,476 @@
+define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'selectpage'], function ($, undefined, Backend, Table, Form, selectPage) {
+
+    var Controller = {
+        index: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'famysql/table/index',
+                    add_url: Config.group ? 'famysql/table/table_add?group=' + Config.group : 'famysql/table/table_add',
+                },
+                showExport: false,//导出按钮导出整个表的所有行
+                showToggle: false,//切换卡片视图和表格视图
+                showColumns: false,//切换显示隐藏列
+                search: false,//关闭快速搜索
+                commonSearch: false,//关闭通用搜索
+            });
+            var table = $("#table");
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                pk: 'id',
+                sortName: 'id',
+                columns: [
+                    [
+                        { field: 'id', title: __('Id') },
+                        { field: 'name', title: __('Name') },
+                        { field: 'engine', title: __('Engine'), width: '80px' },
+                        { field: 'charset', title: __('Charset'), width: '80px' },
+                        { field: 'collation', title: __('Collation'), width: '150px' },
+                        { field: 'comment', title: __('Comment') },
+                        { field: 'rows', title: __('Rows') },
+                        { field: 'createtime', title: __('Create time') },
+                        { field: 'updatetime', title: __('Update time') },
+                        {
+                            field: 'operate', title: __('Table Operate'), width: '400px', table: table, operate: false,
+                            events: {
+                                'click .btn-copy-1': function (e, value, row) {
+                                    Layer.prompt({
+                                        title: "请输入你需要新复制的数据表名",
+                                        success: function (layero) {
+                                            var name = row.name;
+                                            var name_arr = name.split("_");
+                                            if (row.is_has) {
+                                                name_arr.shift()
+                                            }
+                                            const str = name_arr.join('_');
+                                            $("input", layero).prop("placeholder", "例如:test,请不要加前缀").val(str);
+                                        }
+                                    }, function (value) {
+                                        Fast.api.ajax({
+                                            url: "famysql/table/copy?name=" + row.name + "&type=1",
+                                            data: { table: value },
+                                        }, function (data, ret) {
+                                            Layer.closeAll();
+                                            parent.location.reload();
+                                            return false;
+                                        });
+                                    });
+                                },
+                                'click .btn-copy-2': function (e, value, row) {
+                                    Layer.prompt({
+                                        title: "请输入你需要新复制的数据表名",
+                                        success: function (layero) {
+                                            var name = row.name;
+                                            var name_arr = name.split("_");
+                                            if (row.is_has) {
+                                                name_arr.shift()
+                                            }
+                                            const str = name_arr.join('_');
+                                            $("input", layero).prop("placeholder", "例如:test,请不要加前缀").val(str);
+                                        }
+                                    }, function (value) {
+                                        Fast.api.ajax({
+                                            url: "famysql/table/copy?name=" + row.name + "&type=2",
+                                            data: { table: value },
+                                        }, function (data, ret) {
+                                            Layer.closeAll();
+                                            parent.location.reload();
+                                            return false;
+                                        });
+                                    });
+                                }
+                            },
+                            buttons: [
+                                {
+                                    name: 'copy',
+                                    text: __('Copy 1'),
+                                    title: __('Copy 1'),
+                                    dropdown: __('Copy'),
+                                    classname: 'btn btn-xs btn-warning btn-copy-1',
+                                    icon: 'fa fa-copy',
+                                },
+                                {
+                                    name: 'copy-2',
+                                    text: __('Copy 2'),
+                                    title: function (row) {
+                                        return __('Copy 2') + "(" + row.rows + ")";
+                                    },
+                                    dropdown: __('Copy'),
+                                    classname: 'btn btn-xs btn-warning btn-copy-2',
+                                    icon: 'fa fa-copy',
+                                },
+                                {
+                                    name: 'truncate',
+                                    text: function (row) {
+                                        return __('Truncate') + "(" + row.rows + ")";
+                                    },
+                                    title: function (row) {
+                                        return __('Truncate') + "(" + row.rows + ")";
+                                    },
+                                    dropdown: __('More Table Operate'),
+                                    classname: 'btn btn-xs btn-danger btn-truncate',
+                                    icon: 'fa fa-minus-circle',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/truncate?name=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-danger btn-ajax',
+                                    confirm: function (row) {
+                                        return '是否确定清空该“' + row.name + '”数据表?';
+                                    },
+                                    success: function (data, ret) {
+                                        $(".btn-refresh").trigger("click"); //刷新数据
+                                    },
+                                    visible: function (row) {
+                                        return row.is_admin !== 0;
+                                    },
+                                    error: function (data, ret) {
+                                        Layer.alert(ret.msg);
+                                        return false;
+                                    }
+                                },
+                                {
+                                    name: 'optimize',
+                                    text: __('Optimize'),
+                                    title: __('Optimize'),
+                                    dropdown: __('More Table Operate'),
+                                    classname: 'btn btn-xs btn-danger btn-optimize',
+                                    icon: 'fa fa-exclamation-triangle',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/optimize?name=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-danger btn-ajax',
+                                    confirm: function (row) {
+                                        return '是否确定优化该“' + row.name + '”数据表?';
+                                    },
+                                    success: function (data, ret) {
+                                        $(".btn-refresh").trigger("click"); //刷新数据
+                                    },
+                                    visible: function (row) {
+                                        return row.is_admin !== 0;
+                                    },
+                                    error: function (data, ret) {
+                                        Layer.alert(ret.msg);
+                                        return false;
+                                    }
+                                },
+                                {
+                                    name: 'repair',
+                                    text: __('Repair'),
+                                    title: __('Repair'),
+                                    dropdown: __('More Table Operate'),
+                                    classname: 'btn btn-xs btn-danger btn-repair',
+                                    icon: 'fa fa-check-circle-o',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/repair?name=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-danger btn-ajax',
+                                    confirm: function (row) {
+                                        return '是否确定修复该“' + row.name + '”数据表?';
+                                    },
+                                    success: function (data, ret) {
+                                        $(".btn-refresh").trigger("click"); //刷新数据
+                                    },
+                                    visible: function (row) {
+                                        return row.is_admin !== 0;
+                                    },
+                                    error: function (data, ret) {
+                                        Layer.alert(ret.msg);
+                                        return false;
+                                    }
+                                },
+                                {
+                                    name: 'editone',
+                                    icon: 'fa fa-pencil',
+                                    text: __('Edit'),
+                                    title: __('Edit'),
+                                    dropdown: __('More Table Operate'),
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/table_edit?name=" + row.name);
+                                    },
+                                    visible: function (row) {
+                                        return row.is_admin !== 0;
+                                    },
+                                    classname: 'btn btn-xs btn-success btn-dialog'
+                                },
+                                {
+                                    name: 'delone',
+                                    icon: 'fa fa-trash',
+                                    text: __('Del'),
+                                    title: __('Del'),
+                                    dropdown: __('More Table Operate'),
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/table_del?name=" + row.name);
+                                    },
+                                    classname: 'btn btn-xs btn-danger btn-ajax',
+                                    confirm: function (row) {
+                                        return '是否确定删除该“' + row.name + '”数据表,不可恢复?';
+                                    },
+                                    success: function (data, ret) {
+                                        if (ret.data == 0) {
+                                            parent.location.reload();
+                                        } else {
+                                            $(".btn-refresh").trigger("click"); //刷新数据
+                                        }
+                                    },
+                                    visible: function (row) {
+                                        return row.is_admin !== 0;
+                                    },
+                                    error: function (data, ret) {
+                                        Layer.alert(ret.msg);
+                                        return false;
+                                    }
+                                },
+                                {
+                                    name: 'crud',
+                                    text: 'CRUD',
+                                    title: function (row) {
+                                        return "(表" + row.name + ")" + __('CRUD');
+                                    },
+                                    extend: 'data-area=\'["90%", "90%"]\'',
+                                    dropdown: __('More Table Operate'),
+                                    classname: 'btn btn-warning btn-xs btn-primary btn-dialog ',
+                                    visible: function (row) {
+                                        return row.group !== 'system';
+                                    },
+                                    url: function (row) {
+                                        return Fast.api.fixurl('famysql/table/check?addon_name=' + row.group + '&table_name=' + row.name);
+                                    },
+                                    icon: 'fa fa-terminal',
+                                },
+                                {
+                                    name: 'indexs',
+                                    title: __('Index manager'),
+                                    text: __('Index manager'),
+                                    extend: 'data-area=\'["90%", "90%"]\'',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/index/indexs?name=" + row.name + "&is_admin=" + row.is_admin);
+                                    },
+                                    icon: 'fa fa-list-ol',
+                                    classname: 'btn btn-xs btn-danger btn-dialog'
+                                },
+                                {
+                                    name: 'fields',
+                                    title: function (row) {
+                                        return "(" + row.name + ")" + __('Field manager');
+                                    },
+                                    text: function (row) {
+                                        return __('Field manager') + "(" + row.field_nums + ")";
+                                    },
+                                    extend: 'data-area=\'["90%", "90%"]\'',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/field/fields?name=" + row.name + "&is_admin=" + row.is_admin);
+                                    },
+                                    icon: 'fa fa-table',
+                                    classname: 'btn btn-success btn-xs btn-execute btn-dialog'
+                                },
+                            ],
+                            formatter: Table.api.formatter.operate
+                        },
+                    ]
+                ],
+                //启用固定列
+                fixedColumns: true,
+                //固定右侧列数
+                fixedRightNumber: 1,
+                queryParams: function (params) {
+                    if (Config.group) {
+                        params.group = Config.group;
+                    }
+                    return params;
+                },
+            });
+
+            // 绑定TAB事件
+            $('.panel-heading a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+                var value = $(this).data("value");
+                var options = table.bootstrapTable('getOptions');
+                options.queryParams = function (params) {
+                    params.group = value;
+                    return params;
+                };
+                return false;
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+        },
+        backuplist: function () {
+            // 初始化表格参数配置
+            Table.api.init({
+                extend: {
+                    index_url: 'famysql/table/backuplist'
+                }
+            });
+
+            var table = $("#table");
+
+            table.on('load-success.bs.table', function (e, json) {
+                if (json && typeof json.rows != 'undefined' && $(".nav-addon li").size() == 1) {
+                    var addons = [];
+                    $.each(json.rows, function (i, j) {
+                        if (addons.indexOf(j.addon) == -1 && j.addon != 'all') {
+                            $(".nav-addon").append("<li><a href='javascript:;' data-value='" + j.addon + "'>" + j.addon_name + "</a></li>");
+                            addons.push(j.addon);
+                        }
+                    });
+                }
+            });
+
+            // 初始化表格
+            table.bootstrapTable({
+                url: $.fn.bootstrapTable.defaults.extend.index_url,
+                columns: [
+                    [
+                        {
+                            field: 'id', title: __('ID'), operate: false, formatter: function (value, row, index) {
+                                return index + 1;
+                            }
+                        },
+                        { field: 'addon', title: __('Addon'), visible: false },
+                        { field: 'type', title: __('File'), visible: false },
+                        {
+                            field: 'file', title: __('File'), operate: false, formatter: function (value, row, index) {
+                                var url = Fast.api.fixurl("famysql/table/download?file=" + row.file);
+                                return '<a href="' + url + '" data-toggle="tooltip" title="' + __('Download file') + '" target="_blank">' + row.file + '</a>';
+                            }
+                        },
+                        { field: 'size', title: __('Size'), operate: false },
+                        { field: 'date', title: __('Date'), operate: false },
+                        {
+                            field: 'operate', title: __('Operate'), table: table, operate: false,
+                            buttons: [
+                                {
+                                    name: 'restore',
+                                    text: __('恢复'),
+                                    icon: 'fa fa-reply',
+                                    classname: 'btn btn-primary btn-restore btn-xs btn-ajax ',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/restore?action=restore&file=" + row.file);
+                                    }
+                                },
+                                {
+                                    name: 'delone',
+                                    text: __('Del'),
+                                    icon: 'fa fa-times',
+                                    classname: 'btn btn-danger btn-delete btn-xs btn-ajax',
+                                    extend: 'data-toggle="tooltip"',
+                                    url: function (row) {
+                                        return Fast.api.fixurl("famysql/table/restore?action=delete&file=" + row.file);
+                                    },
+                                    confirm: function (row) {
+                                        return '是否确定删除该“' + row.file + '”备份文件,不可恢复?';
+                                    },
+                                    refresh: true
+                                },
+                            ],
+                            formatter: Table.api.formatter.buttons
+                        }
+                    ]
+                ],
+                commonSearch: true,
+                search: false,
+                templateView: false,
+                clickToSelect: false,
+                showColumns: false,
+                showToggle: false,
+                showExport: false,
+                showSearch: false,
+                searchFormVisible: false,
+                queryParams: function (params) {
+                    if (Config.group) {
+                        //这里可以追加搜索条件
+                        var filter = JSON.parse(params.filter);
+                        var op = JSON.parse(params.op);
+                        filter.addon = Config.group;
+                        op.addon = "=";
+                        params.filter = JSON.stringify(filter);
+                        params.op = JSON.stringify(op);
+                    }
+
+                    return params;
+                },
+            });
+
+            // 为表格绑定事件
+            Table.api.bindevent(table);
+
+            // 切换
+            $(document).on("click", ".btn-switch", function () {
+                $(".btn-switch").removeClass("active");
+                $(this).addClass("active");
+                $("form.form-commonsearch input[name='type']").val($(this).data("type"));
+                table.bootstrapTable('refresh', { url: $.fn.bootstrapTable.defaults.extend.index_url, pageNumber: 1 });
+                return false;
+            });
+            $(document).on("click", ".nav-addon li a", function () {
+                $(".nav-addon li").removeClass("active");
+                $(this).parent().addClass("active");
+                $("form.form-commonsearch input[name='addon']").val($(this).data("value"));
+                table.bootstrapTable('refresh', { url: $.fn.bootstrapTable.defaults.extend.index_url, pageNumber: 1 });
+                return false;
+            });
+            //上传完成后刷新
+            $(".faupload").data("upload-complete", function (files) {
+                if (files[0].ret.code) {
+                    Toastr.success(files[0].ret.msg);
+                } else {
+                    Toastr.error(files[0].ret.msg);
+                }
+                $(".btn-refresh").trigger("click"); //刷新数据
+            });
+            Controller.api.bindevent();
+        },
+        backup: function () {
+            $(document).on("change", "#c-addon", function () {
+                $("#c-ignore_tables").selectPageRefresh();
+            });
+            $("#c-ignore_tables").data("params", function (obj) {
+                //obj为SelectPage对象
+                return { custom: { addon: $("#c-addon").val() } };
+            });
+            Controller.api.bindevent();
+        },
+        table_add: function () {
+            $(document).on("change", "#c-charset", function () {
+                $("#c-collation").selectPageRefresh();
+            });
+            Controller.api.bindevent();
+        },
+        table_batch_add: function () {
+            $("#c-name").data("params", function (obj) {
+                //obj为SelectPage对象
+                return { custom: { addon: $("#c-addon").val() } };
+            });
+            Controller.api.bindevent();
+        },
+        table_edit: function () {
+            $(document).on("change", "#c-charset", function () {
+                $("#c-collation").selectPageRefresh();
+            });
+            Controller.api.bindevent();
+        },
+        api: {
+            bindevent: function () {
+                $("#c-collation").data("params", function (obj) {
+                    //obj为SelectPage对象
+                    return { custom: { charset: $("#c-charset").val() } };
+                });
+
+                $("#c-type").data("params", function (obj) {
+                    //obj为SelectPage对象
+                    if ($("#field-suffix").val() !== "无") {
+                        return { custom: { suffix: $("#field-suffix").val() } };
+                    }
+
+                });
+
+                Form.api.bindevent($("form[role=form]"));
+            }
+        }
+    };
+    return Controller;
+});

部分文件因为文件数量过多而无法显示