瀏覽代碼

新增自定义命令行

huwhois 2 年之前
父節點
當前提交
e47a028a06

+ 2 - 2
app/common/command/MakeController.php

@@ -2,7 +2,7 @@
 
 declare (strict_types = 1);
 
-namespace app\admin\command;
+namespace app\common\command;
 
 // 引入框架内置类
 use think\console\Input;
@@ -19,7 +19,7 @@ class MakeController extends Command
 
     protected function configure()
     {
-        $this->setName('MakeModel')
+        $this->setName('makecontroller')
             ->addArgument('name', Argument::OPTIONAL, "console name")
             ->setDescription('Create a new model class');
     }

+ 2 - 2
app/common/command/MakeModel.php

@@ -2,7 +2,7 @@
 
 declare (strict_types = 1);
 
-namespace app\admin\command;
+namespace app\common\command;
 
 // 引入框架内置类
 use think\console\Input;
@@ -19,7 +19,7 @@ class MakeModel extends Command
 
     protected function configure()
     {
-        $this->setName('MakeModel')
+        $this->setName('makemodel')
             ->addArgument('name', Argument::OPTIONAL, "console name")
             ->setDescription('Create a new model class');
     }

+ 22 - 0
app/common/model/ArticleBrowerHistory.php

@@ -0,0 +1,22 @@
+<?php
+declare (strict_types = 1);
+
+namespace app\common\model;
+
+use think\Model;
+
+/**
+ * @mixin \think\Model
+ */
+class ArticleBrowerHistory extends Model
+{
+    
+    protected $schema = [
+        "id" => "int",  // id
+        "ip" => "varchar",  // ip地址
+        "aid" => "int",  // 文章id
+        "create_time" => "int",  // 创建时间
+    ];
+
+    protected $autoWriteTimestamp = false;
+}

+ 82 - 0
app/common/utils/ControllerUtils.php

@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+namespace app\common\utils;
+
+// 引入框架内置类
+use think\facade\Db;
+use think\facade\Config;
+
+/**
+ * 模型 utils
+ *
+ * @version      0.0.1
+ * @author      by huwhois
+ * @time        2022/08/10
+ */
+class ControllerUtils
+{
+    protected $namespace;
+    protected $class;
+
+    protected function getPathName(string $name): string
+    {
+        $name = substr($name, 4);
+
+        return app()->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'controller.stub';
+    }
+
+    /**
+     * 构建Class文件
+     * @return string
+     */
+    protected function buildClass()
+    {
+        $stub = file_get_contents($this->getStub());
+
+        return str_replace(['{%namespace%}', '{%className%}'], [
+            $this->namespace,
+            $this->class
+        ], $stub);
+    }
+
+    /**
+     * 生成 Model 文件
+     * @param  string $name 类目,包含命名空间
+     * @param string $connections 数据库配置, 默认 mysql
+     */
+    public function makeModel(string $name, string $connections = 'mysql')
+    {
+        $pathname = $this->getPathName($name);
+
+        if (is_file($pathname)) {
+            throw new \Exception("Model :' . $name . ' already exists!</error>", 1);
+        }
+
+        if (!is_dir(dirname($pathname))) {
+            mkdir(dirname($pathname), 0755, true);
+        }
+
+        file_put_contents($pathname, $this->codeModel($name, $connections));
+    }
+
+    /**
+     * 生成 Model 不生成文件
+     * @param  string $name 类目,包含命名空间
+     * @return string
+     */
+    public function codeModel(string $name)
+    {
+        $this->namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+
+        $this->class = str_replace($this->namespace . '\\', '', $name);
+
+        return $this->buildClass();
+    }
+}

+ 155 - 0
app/common/utils/ModelUtils.php

@@ -0,0 +1,155 @@
+<?php
+
+declare(strict_types=1);
+
+namespace app\common\utils;
+
+// 引入框架内置类
+use think\facade\Db;
+use think\facade\Config;
+
+/**
+ * 模型 utils
+ *
+ * @version      0.0.1
+ * @author      by huwhois
+ * @time        2022/08/10
+ */
+class ModelUtils
+{
+    protected $namespace;
+    protected $class;
+    protected $tableName;
+    protected $databaseName;
+
+    protected function getPathName(string $name): string
+    {
+        $name = substr($name, 4);
+
+        return app()->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
+    }
+
+    protected function getStub(): string
+    {
+        return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'model.stub';
+    }
+
+    /**
+     * 获取表的主键
+     * @return string
+     */
+    protected function getPrimarykey()
+    {
+        $sql = "SELECT COLUMN_NAME AS pk FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_NAME = '" . $this->tableName . "' AND table_schema = '.$this->databaseName.' AND CONSTRAINT_NAME = 'PRIMARY'";
+
+        $res = Db::query($sql);
+
+        $pk = empty($res) ? '' : $res[0]['pk'];
+
+        return $pk;
+    }
+
+    /**
+     * 获取表的所有字段信息
+     * @return array
+     */
+    protected function getColumns(string $tableName = '', string $database = '')
+    {
+        $sql = "SELECT column_name,column_comment,data_type FROM information_schema.COLUMNS WHERE TABLE_NAME = '" . $this->tableName . "' AND table_schema = '" . $this->databaseName . "'";
+
+        // echo $sql . "\n";
+
+        $res = Db::query($sql);
+
+        $columns = empty($res) ? [] : $res;
+
+        return $columns;
+    }
+
+    /**
+     * 构建Class文件
+     * @return string
+     */
+    protected function buildClass()
+    {
+        $stub = file_get_contents($this->getStub());
+
+        $primarykey = "";
+
+        $pk = $this->getPrimarykey();
+
+        if ($pk != "" && $pk != "id") {
+            $primarykey = 'protected $pk = "' . $pk . '";';
+        }
+
+        $schemas = '';
+        $autoWriteTimestamp = '';
+        $create_time = false;
+        $update_time = false;
+
+        $columns = $this->getColumns();
+
+        foreach ($columns as $column) {
+            $schemas .= "\n" . '        "' . $column['column_name'] . '" => "' . $column['data_type'] . '",  // ' . $column['column_comment'];
+
+            if ($column['column_name'] == 'create_time') {
+                $create_time = true;
+            }
+
+            if ($column['column_name'] == 'update_time') {
+                $update_time = true;
+            }
+        }
+
+        if (!$create_time || !$update_time) {
+            $autoWriteTimestamp = 'protected $autoWriteTimestamp = false;';
+        }
+
+        return str_replace(['{%namespace%}', '{%className%}', '{%primarykey%}', '{%schemas%}', '{%autoWriteTimestamp%}'], [
+            $this->namespace,
+            $this->class,
+            $primarykey,
+            $schemas,
+            $autoWriteTimestamp
+        ], $stub);
+    }
+
+    /**
+     * 生成 Model 文件
+     * @param  string $name 类目,包含命名空间
+     * @param string $connections 数据库配置, 默认 mysql
+     */
+    public function makeModel(string $name, string $connections = 'mysql')
+    {
+        $pathname = $this->getPathName($name);
+
+        if (is_file($pathname)) {
+            throw new \Exception("Model :' . $name . ' already exists!</error>", 1);
+        }
+
+        if (!is_dir(dirname($pathname))) {
+            mkdir(dirname($pathname), 0755, true);
+        }
+
+        file_put_contents($pathname, $this->codeModel($name, $connections));
+    }
+
+    /**
+     * 生成 Model 不生成文件
+     * @param  string $name 类目,包含命名空间
+     * @param string $connections 数据库配置, 默认 mysql
+     * @return string
+     */
+    public function codeModel(string $name, string $connections = 'mysql')
+    {
+        $this->namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+
+        $this->class = str_replace($this->namespace . '\\', '', $name);
+
+        $this->tableName = Config::get('database.connections.'.$connections.'.prefix') . strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $this->class));
+
+        $this->databaseName = Config::get('database.connections.'.$connections.'.database');
+
+        return $this->buildClass();
+    }
+}

+ 64 - 0
app/common/utils/stubs/controller.stub

@@ -0,0 +1,64 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Request;
+
+class {%className%}
+{
+    /**
+     * 显示资源列表
+     *
+     * @return \think\Response
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * 保存新建的资源
+     *
+     * @param  \think\Request  $request
+     * @return \think\Response
+     */
+    public function save(Request $request)
+    {
+        //
+    }
+
+    /**
+     * 显示指定的资源
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function read($id)
+    {
+        //
+    }
+
+    /**
+     * 保存更新的资源
+     *
+     * @param  \think\Request  $request
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function update(Request $request, $id)
+    {
+        //
+    }
+
+    /**
+     * 删除指定资源
+     *
+     * @param  int  $id
+     * @return \think\Response
+     */
+    public function delete($id)
+    {
+        //
+    }
+}

+ 110 - 0
app/common/utils/stubs/form.vue.stub

@@ -0,0 +1,110 @@
+<template>
+  <el-dialog
+    :title="!dataForm.id ? '新增' : '修改'"
+    :close-on-click-modal="false"
+    :visible.sync="visible">
+    <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
+#foreach($column in $columns)
+#if($column.columnName != $pk.columnName)
+    <el-form-item label="${column.comments}" prop="${column.attrname}">
+      <el-input v-model="dataForm.${column.attrname}" placeholder="${column.comments}"></el-input>
+    </el-form-item>
+#end
+#end
+    </el-form>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">取消</el-button>
+      <el-button type="primary" @click="dataFormSubmit()">确定</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        visible: false,
+        dataForm: {
+#foreach($column in $columns)
+#if($column.columnName == $pk.columnName)
+          ${column.attrname}: 0,
+#else
+          ${column.attrname}: ''#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+        },
+        dataRule: {
+#foreach($column in $columns)
+#if($column.columnName != $pk.columnName)
+          ${column.attrname}: [
+            { required: true, message: '${column.comments}不能为空', trigger: 'blur' }
+          ]#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+        }
+      }
+    },
+    methods: {
+      init (id) {
+        this.dataForm.${pk.attrname} = id || 0
+        this.visible = true
+        this.$nextTick(() => {
+          this.$refs['dataForm'].resetFields()
+          if (this.dataForm.${pk.attrname}) {
+            this.$http({
+              url: #[[this.$http.adornUrl]]#(`/${moduleName}/${pathName}/info/#[[$]]#{this.dataForm.${pk.attrname}}`),
+              method: 'get',
+              #[[params: this.$http.adornParams()]]#
+            }).then(({data}) => {
+              if (data && data.code === 0) {
+#foreach($column in $columns)
+#if($column.columnName != $pk.columnName)
+                this.dataForm.${column.attrname} = data.${classname}.${column.attrname}
+#end
+#end
+              }
+            })
+          }
+        })
+      },
+      // 表单提交
+      dataFormSubmit () {
+        #[[this.$refs['dataForm'].validate((valid) => {]]#
+          if (valid) {
+            this.$http({
+              url: #[[this.$http.adornUrl]]#(`/${moduleName}/${pathName}/${!this.dataForm.${pk.attrname} ? 'save' : 'update'}`),
+              method: 'post',
+              #[[data: this.$http.adornData({]]#
+#foreach($column in $columns)
+#if($column.columnName == $pk.columnName)
+                '${column.attrname}': this.dataForm.${column.attrname} || undefined,
+#else
+                '${column.attrname}': this.dataForm.${column.attrname}#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+              })
+            }).then(({data}) => {
+              if (data && data.code === 0) {
+                #[[this.$message({]]#
+                  message: '操作成功',
+                  type: 'success',
+                  duration: 1500,
+                  onClose: () => {
+                    this.visible = false
+                    #[[this.$emit('refreshDataList')]]#
+                  }
+                })
+              } else {
+                #[[this.$message.error(data.msg)]]#
+              }
+            })
+          }
+        })
+      }
+    }
+  }
+</script>
+

+ 152 - 0
app/common/utils/stubs/index.vue.stub

@@ -0,0 +1,152 @@
+<template>
+  <div class="app-container">
+    <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
+      <el-form-item>
+        <el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="getDataList()">查询</el-button>
+        <el-button v-if="isAuth('{%moduleName%}:{%className%}:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
+        <el-button v-if="isAuth('{%moduleName%}:{%className%}:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
+      </el-form-item>
+    </el-form>
+    <el-table
+      :data="dataList"
+      border
+      v-loading="dataListLoading"
+      @selection-change="selectionChangeHandle"
+      style="width: 100%;">
+      <el-table-column
+        type="selection"
+        header-align="center"
+        align="center"
+        width="50">
+      </el-table-column>
+        {%schemas%}
+      <el-table-column
+        fixed="right"
+        header-align="center"
+        align="center"
+        width="150"
+        label="操作">
+        <template slot-scope="scope">
+          <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.{%primarykey%})">修改</el-button>
+          <el-button type="text" size="small" @click="deleteHandle(scope.row.{%primarykey%})">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      @size-change="sizeChangeHandle"
+      @current-change="currentChangeHandle"
+      :current-page="pageIndex"
+      :page-sizes="[10, 20, 50, 100]"
+      :page-size="pageSize"
+      :total="totalPage"
+      layout="total, sizes, prev, pager, next, jumper">
+    </el-pagination>
+    <!-- 弹窗, 新增 / 修改 -->
+    <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
+  </div>
+</template>
+
+<script>
+  import AddOrUpdate from './{%className%}-add-or-update'
+  export default {
+    data () {
+      return {
+        dataForm: {
+          key: ''
+        },
+        dataList: [],
+        pageIndex: 1,
+        pageSize: 10,
+        totalPage: 0,
+        dataListLoading: false,
+        dataListSelections: [],
+        addOrUpdateVisible: false
+      }
+    },
+    components: {
+      AddOrUpdate
+    },
+    activated () {
+      this.getDataList()
+    },
+    methods: {
+      // 获取数据列表
+      getDataList () {
+        this.dataListLoading = true
+        this.$http({
+          url: this.$http.adornUrl('/{%moduleName%}/{%className%}/index'),
+          method: 'get',
+          params: this.$http.adornParams({
+            'page': this.pageIndex,
+            'limit': this.pageSize,
+            'key': this.dataForm.key
+          })
+        }).then(({res}) => {
+          if (res && res.code === 0) {
+            this.dataList = res.data.data
+            this.totalPage = res.data.total
+          } else {
+            this.dataList = []
+            this.totalPage = 0
+          }
+          this.dataListLoading = false
+        })
+      },
+      // 每页数
+      sizeChangeHandle (val) {
+        this.pageSize = val
+        this.pageIndex = 1
+        this.getDataList()
+      },
+      // 当前页
+      currentChangeHandle (val) {
+        this.pageIndex = val
+        this.getDataList()
+      },
+      // 多选
+      selectionChangeHandle (val) {
+        this.dataListSelections = val
+      },
+      // 新增 / 修改
+      addOrUpdateHandle (id) {
+        this.addOrUpdateVisible = true
+        this.$nextTick(() => {
+          this.$refs.addOrUpdate.init(id)
+        })
+      },
+      // 删除
+      deleteHandle (id) {
+        var ids = id ? [id] : this.dataListSelections.map(item => {
+          return item.{%primarykey%}
+        })
+        this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          this.$http({
+            url: this.$http.adornUrl('/{%moduleName%}/{%className%}/delete'),
+            method: 'post',
+            data: this.$http.adornData(ids, false)
+          }).then(({data}) => {
+            if (data && data.code === 0) {
+              this.$message({
+                message: '操作成功',
+                type: 'success',
+                duration: 1500,
+                onClose: () => {
+                  this.getDataList()
+                }
+              })
+            } else {
+              this.$message.error(data.msg)
+            }
+          })
+        }).catch(() => {})
+      }
+    }
+  }
+</script>

+ 18 - 0
app/common/utils/stubs/model.stub

@@ -0,0 +1,18 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Model;
+
+/**
+ * @mixin \think\Model
+ */
+class {%className%} extends Model
+{
+    {%primarykey%}
+    protected $schema = [{%schemas%}
+    ];
+
+    {%autoWriteTimestamp%}    
+}

+ 21 - 0
app/common/utils/stubs/validate.stub

@@ -0,0 +1,21 @@
+<?php
+declare (strict_types = 1);
+
+namespace {%namespace%};
+
+use think\Validate;
+
+/**
+ * @mixin \think\Validate
+ */
+class {%className%} extends Validate
+{
+    /**
+     * 定义验证规则
+     * 格式:'字段名' =>  ['规则1','规则2'...]
+     *
+     * @var array
+     */
+    protected $rule = [{%schemas%}
+    ];   
+}