zoukankan      html  css  js  c++  java
  • 基于表的前后端代码生成工具

    基于表的前后端代码生成工具

    基于数据库表结构和注释的前后端代码生成工具。
    后端生成基于SpringBoo2.x的MVC结构代码,前端生成基于vue-cli和elementUI的后台前端解决方案vue-admin-template
    的页面组件代码,点击github地址了解更多关于vue-admin-template

    基础准备

    后端

    前端

    • WebStorm(推荐使用)或Visual Studio Code
    • node环境
    • vue-cli3 脚手架
    • 后台前端解决方案 vue-admin-template (一个基于vue-cli和elementUI的组件集成框架,包含router、axios、vuex和登录权限以及侧边菜单栏)

    IDEA系列软件需要购买或者破解才能使用,获取破解方法

    使用

    使用之前先了解EasyCode插件使用方法

    • EasyCode 选择 Group 为 MybatisPlus 生成代码
    • entity层代码需要自定义一下,让它生成的代码是lombok形式的
    ##导入宏定义
    $!define
    
    ##保存文件(宏定义)
    #save("/entity", ".java")
    
    ##包路径(宏定义)
    #setPackageSuffix("entity")
    
    ##自动导入包(全局变量)
    $!autoImport
    import lombok.Data;
    ##表注释(宏定义)
    #tableComment("Entity")
    @Data
    public class $!{tableInfo.name} {
    #foreach($column in $tableInfo.fullColumn)
    #if(${column.comment})
        /**
         * ${column.comment}
         */
    #end
        private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
    #end
    }
    
    • controller层的代码也需要自定义,让它继承自定义的通用BaseController类
    ##导入宏定义
    $!define
    
    ##设置表后缀(宏定义)
    #setTableSuffix("Controller")
    
    ##保存文件(宏定义)
    #save("/controller", "Controller.java")
    
    ##包路径(宏定义)
    #setPackageSuffix("controller")
    
    ##定义服务名
    #set($serviceName = $!tool.append($!tool.firstLowerCase($!tableInfo.name), "Service"))
    
    ##定义实体对象名
    #set($entityName = $!tool.firstLowerCase($!tableInfo.name))
    ##拿到主键
    #if(!$tableInfo.pkColumn.isEmpty())
        #set($pk = $tableInfo.pkColumn.get(0))
    #end
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import $!{tableInfo.savePackageName}.entity.$!tableInfo.name;
    import $!{tableInfo.savePackageName}.service.$!{tableInfo.name}Service;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.*;
    import javax.annotation.Resource;
    import java.util.List;
    ##表注释(宏定义)
    #tableComment("Controller")
    @RestController
    @RequestMapping("/$!tool.firstLowerCase($!tableInfo.name)")
    public class $!{tableName} extends BaseController {
        
        @Resource
        private $!{tableInfo.name}Service $!{serviceName};
    
        /**
         * 分页查询所有数据
         *
         * @param page 分页对象
         * @param $!entityName 查询实体
         * @return 所有数据
         */
        @GetMapping
        public Response selectAll(Page<$!tableInfo.name> page, $!tableInfo.name $!entityName, String column, Boolean isAsc) {
            QueryWrapper<$!tableInfo.name> queryWrapper = new QueryWrapper<>($!entityName);
            if (isAsc != null && StringUtils.hasLength(column)) {
                queryWrapper.orderBy(true,isAsc,column);
            }
            return success(this.$!{serviceName}.page(page, queryWrapper));
        }
    
        /**
         * 通过主键查询单条数据
         *
         * @param id 主键
         * @return 单条数据
         */
        @GetMapping("/{id}")
        public Response selectOne(@PathVariable $!pk.shortType id) {
            return success(this.$!{serviceName}.getById(id));
        }
    
        /**
         * 新增数据
         *
         * @param $!entityName 实体对象
         * @return 新增结果
         */
        @PostMapping
        public Response insert(@RequestBody $!tableInfo.name $!entityName) {
            return success(this.$!{serviceName}.save($!entityName));
        }
    
        /**
         * 修改数据
         *
         * @param $!entityName 实体对象
         * @return 修改结果
         */
        @PutMapping
        public Response update(@RequestBody $!tableInfo.name $!entityName) {
            return success(this.$!{serviceName}.updateById($!entityName));
        }
    
        /**
         * 删除数据
         *
         * @param idList 主键结合
         * @return 删除结果
         */
        @DeleteMapping
        public Response delete(@RequestBody List<$!pk.shortType> idList) {
            return success(this.$!{serviceName}.removeByIds(idList));
        }
    }
    
    • 自定义的BaseController抽象类代码如下,把它放在controller层下
    public abstract class BaseController {
    
        public <T> Response<T> success(T data) {
            return new Response<>(data,20000,"success");
        }
    
        @Data
        @AllArgsConstructor
        class Response<T> implements Serializable {
            private T data;
            private int code;
            private String message;
        }
    
        @Data
        @AllArgsConstructor
        class PageAccept {
            private int current = 1;
            private int size = 10;
            private String column;
            private boolean asc = false;
        }
    }
    
    • EasyCode插件没有关于生成前端的代码,所以需要我们去定义

    生成的vue文件要放在vue-admin-template前端工程下的 src/views/page 目录下,page目录需要自己创建

    ##引入mybatis支持
    $!mybatisSupport
    
    ##设置保存名称与保存位置
    $!callback.setFileName($tool.append($!{tableInfo.name}, "List.vue"))
    $!callback.setSavePath($tool.append($modulePath, "/src/main/resources/vue"))
    #foreach($column in $tableInfo.fullColumn)
        #if($column.name.startsWith("is") && $column.type.equals("java.lang.Boolean"))
            $!column.setName($tool.firstLowerCase($column.name.substring(2)))
        #end
    #end
    ##拿到主键
    #if(!$tableInfo.pkColumn.isEmpty())
        #set($pk = $tableInfo.pkColumn.get(0))
    #end
    <template>
      <div class="app-container">
        <el-button :disabled="multipleSelection.length==0" class="el-icon-delete" type="danger" size="small" @click="deleteRows">删除</el-button>
        <el-button :disabled="multipleSelection.length!=1" class="el-icon-edit" type="primary" size="small" @click="openForm(true)">修改</el-button>
        <el-button class="el-icon-plus" type="success" size="small" @click="openForm(false)">添加</el-button>
        <el-table
          v-loading="listLoading"
          :data="list"
          element-loading-text="Loading"
          border
          fit
          highlight-current-row
          @selection-change="handleSelectionChange"
          @sort-change="tableOrderBy"
        >
          <el-table-column
            type="selection"
            width="40"/>
          <el-table-column label="ID" align="center" width="100" prop="$!{pk.obj.name}" sortable="custom">
            <template slot-scope="scope">
              {{ scope.row.$!{pk.name} }}
            </template>
          </el-table-column>
          #foreach($column in $tableInfo.otherColumn)
        #if(${column.name.equals('createTime')} || ${column.name.equals('updateTime')})
          <el-table-column align="center" label="${column.name}" prop="${column.obj.name}" sortable="custom">
            <template slot-scope="scope">
              <i class="el-icon-time" />
              <span>{{ scope.row.${column.name} }}</span>
            </template>
          </el-table-column>
          #else
            #if($column.type.equals("java.lang.Boolean"))
          <el-table-column label="${column.name}" align="center" prop="${column.obj.name}" sortable="custom">
            <template slot-scope="scope">
              <i :style="'color:'+(scope.row.${column.name} ? '#67C23A' : '#F56C6C')" :class="scope.row.${column.name} ? 'el-icon-check' : 'el-icon-close'"></i>
            </template>
          </el-table-column>
          #elseif($column.type.equals("java.lang.Short"))
          <el-table-column label="${column.name}" align="center" prop="${column.obj.name}" sortable="custom">
            <template slot-scope="scope">
              <el-tag :type="scope.row.${column.name} | ${column.name}TypeFilter">{{ scope.row.${column.name} | ${column.name}NameFilter }}</el-tag>
            </template>
          </el-table-column>
          #else
          <el-table-column label="${column.name}" align="center" prop="${column.obj.name}" sortable="custom">
            <template slot-scope="scope">
              {{ scope.row.${column.name} }}
            </template>
          </el-table-column>
          #end
        #end
    #end
        </el-table>
        <div class="block">
          <MyPagination :total="total" :orderBy="orderBy" :fetchList="fetchData"></MyPagination>
        </div>
    
        <!--form表单-->
        <el-dialog
          :title="form.$!{pk.name}?'修改':'添加'"
          :visible.sync="dialogVisible"
          :close-on-click-modal="false"
          width="30%">
          <el-form ref="form" :model="form" label-width="120px">
            #foreach($column in $tableInfo.otherColumn)
        #if(!${column.name.equals('createTime')} && !${column.name.equals('updateTime')})
            #if($column.type.equals("java.lang.Boolean"))
            <el-form-item label="${column.name}">
              <el-radio-group v-model="form.${column.name}">
                <el-radio :label="true" />
                <el-radio :label="false" />
              </el-radio-group>
            </el-form-item>
            #elseif($column.type.equals("java.lang.Short"))
            <el-form-item label="${column.name}">
              <el-select v-model="form.${column.name}" placeholder="请选择">
                #set($startIdx = ${column.comment.indexOf("[")}+1)
                #set($endIdx = ${column.comment.indexOf("]")})
                #set($enumComments = ${column.comment.substring($startIdx,$endIdx).split(",")})
                #foreach($enumComment in $enumComments)
                <el-option label="${enumComment}" :value="${foreach.index}" />
                #end
              </el-select>
            </el-form-item>
            #else
            <el-form-item label="${column.name}">
              <el-input v-model="form.${column.name}" />
            </el-form-item>
            #end
        #end
    #end
          </el-form>
          <span slot="footer" class="dialog-footer">
            <el-button @click="dialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveRow">确 定</el-button>
          </span>
        </el-dialog>
      </div>
    </template>
    
    <script>
    import { getList, deleteRows, saveRow } from '@/api/$!{tool.firstLowerCase($tableInfo.name)}List'
    import MyPagination from '@/components/Pagination'
    
    export default {
    components: { MyPagination },
    filters: {
    #foreach($column in $tableInfo.otherColumn)
        #if($column.type.equals("java.lang.Short"))
            #set($startIdx = ${column.comment.indexOf("[")}+1)
            #set($endIdx = ${column.comment.indexOf("]")})
            #set($enumComments = ${column.comment.substring($startIdx,$endIdx).split(",")})
            ${column.name}TypeFilter(${column.name}) {
        const ${column.name}Map = {
    #foreach($enumComment in $enumComments)
            ${foreach.index}: 'success',
    #end
        }
        return ${column.name}Map[${column.name}]
      },
      ${column.name}NameFilter(${column.name}) {
        const ${column.name}Map = {
    #foreach($enumComment in $enumComments)
            ${foreach.index}: '${enumComment}',
    #end
        }
        return ${column.name}Map[${column.name}]
      },
    #end
    #end
      },
      data() {
        return {
          list: [],
          listLoading: true,
          multipleSelection: [],
          total: 0,
          dialogVisible: false,
          form: {},
          orderBy: {}
        }
      },
      methods: {
        fetchData(current, size, column, isAsc) {
          this.multipleSelection.length = 0
          this.listLoading = true
          const params = {
            size: size,
            current: current
          }
          if (column) {
            params.column = column
            params.isAsc = isAsc
          }
          getList(params).then(response => {
            this.list = response.data.records
            this.total = response.data.total
            this.listLoading = false
          })
        },
        handleSelectionChange(val) {
          this.multipleSelection = val
        },
        deleteRows() {
          if (this.multipleSelection.length > 0) {
            this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(() => {
              this.listLoading = true
              const ids = []
              this.multipleSelection.map(row => ids.push(row.$!{pk.name}))
              deleteRows(ids).then(response => {
                this.listLoading = false
                if (response.data) {
                  this.$message({
                    type: 'success',
                    message: '删除成功!'
                  })
                  this.$router.replace('/reload')
                } else {
                  this.$message({
                    type: 'error',
                    message: '删除失败!'
                  })
                }
              })
            }).catch(() => {
              this.$message({
                type: 'info',
                message: '已取消删除'
              })
            })
          } else {
            this.$message.info('请至少选择一项')
          }
        },
        openForm(hasId) {
          if (hasId) {
            this.form = JSON.parse(JSON.stringify(this.multipleSelection[0]))
          } else {
            this.form = {}
          }
          this.dialogVisible = true
        },
        saveRow() {
          let msg = '添加'
          if (this.form.$!{pk.name}) {
            msg = '修改'
            this.form.createTime = null
            this.form.updateTime = null
          }
          saveRow(this.form).then(response => {
            this.listLoading = false
            if (response.data) {
              this.$message({
                type: 'success',
                message: msg + '成功'
              })
              this.$router
              .replace(this.$route.path)
            } else {
              this.$message({
                type: 'error',
                message: msg + '失败!'
              })
            }
          })
          this.dialogVisible = false
        },
        tableOrderBy(val) {
          const orderBy = {}
          if (val.order) {
            orderBy.column = val.prop
            orderBy.isAsc = (val.order === 'ascending')
          } else {
            orderBy.column = ''
          }
          this.orderBy = orderBy
        }
      }
    }
    </script>
    

    生成的ajax js文件要放在vue-admin-template前端工程下的 src/api 目录下

    ##引入mybatis支持
    $!mybatisSupport
    ##设置保存名称与保存位置
    $!callback.setFileName($tool.append($!tool.firstLowerCase($tableInfo.name), "List.js"))
    $!callback.setSavePath($tool.append($modulePath, "/src/main/resources/js"))
    ##拿到主键
    #if(!$tableInfo.pkColumn.isEmpty())
        #set($pk = $tableInfo.pkColumn.get(0))
    #end
    import request from '@/utils/request'
    
    const url = '/$!{tool.firstLowerCase($tableInfo.name)}'
    
    export function getList(params) {
      return request({
        url: url,
        method: 'get',
        params
      })
    }
    
    export function deleteRows(data) {
      return request({
        url: url,
        method: 'delete',
        data
      })
    }
    
    export function saveRow(data) {
      return request({
        url: url,
        method: data.$!{pk.name} ? 'put' : 'post',
        data
      })
    }
    
    • 由于vue-admin-template中的分页和路由重载不是很友好,自己封装了一个分页组件和重载组件

    分页组件放在vue-admin-template前端工程下的 src/components/Pagination/index.vue,Pagination 目录需要自己创建

    <template>
      <el-pagination
        background
        :current-page="current"
        :page-sizes="pageSizes"
        :page-size="size"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </template>
    
    <script>
    export default {
      name: 'MyPagination',
      props: {
        fetchList: {
          type: Function,
          default: null
        },
        total: {
          type: Number,
          default: 0
        },
        orderBy: {
          type: Object
        }
      },
      data() {
        return {
          current: 1,
          size: 10,
          pageSizes: [5, 10, 20, 50],
          query: ''
        }
      },
      methods: {
        handleSizeChange(val) {
          this.size = val
          this.querySetter()
        },
        handleCurrentChange(val) {
          this.current = val
          this.querySetter()
        },
        queryGetter() {
          let current = parseInt(this.$route.query.current)
          if (!current || current <= 0) {
            current = 1
          }
          this.current = current
          let size = parseInt(this.$route.query.size)
          if (this.pageSizes.indexOf(size) === -1) {
            size = 10
          }
          this.size = size
        },
        querySetter() {
          const current = this.current
          const size = this.size
          this.$router.push({
            query: {
              current: current,
              size: size
            }
          })
          this.query = `current=${current}&size=${size}`
        },
        pageInit() {
          this.queryGetter()
          this.querySetter()
        },
        pageFetch() {
          if (this.fetchList) {
            this.fetchList(this.current, this.size, this.orderBy.column, this.orderBy.isAsc)
          }
        }
      },
      created() {
        this.pageInit()
      },
      watch: {
        query() {
          this.pageFetch()
        },
        orderBy() {
          this.pageFetch()
        }
      }
    }
    </script>
    

    重载组件放在vue-admin-template前端工程下的 src/views/Reload.vue,和 404.vue 平级,并在 src/router/index.js 中添加对应路由


    重载组件

    <template>
      <div />
    </template>
    
    <script>
    export default {
      name: 'Reload',
      beforeRouteEnter(to, from, next) {
        next(vm => {
          vm.$router.replace(from.fullPath)
        })
      }
    }
    </script>
    
    

    重载组件路由

    {
      path: '/reload',
      component: () => import('@/views/Reload'),
      hidden: true
    }
    

    调用

    //这样就起到刷新页面,但是浏览器没有重新请求页面的功能
    this.$router.replace('/reload')
    
    • 此时我们的前后端工程基本已完成,但还需要将生成的vue组件注册到侧边栏路由中,以及将ajax的访问地址指向后端服务

    路由在 src/router/index.js中。将不需要的路由删除,为自己的组件添加路由。
    简单的路由添加如下,有关路由和侧边栏配置点击这里

    {
        path: '/xxx', //路由地址
        component: Layout,
        children: [
          {
            path: '',
            name: 'xxx', //模块名称
            component: () => import('@/views/page/xxx'), //被引入组件名
            meta: { 
                title: 'xxx', //侧边栏显示的名称
                icon: 'el-icon-monitor' //侧边栏显示的图标
            } 
          }
        ]
    }
    

    在 src/utils/request.js 中,修改 baseURL 为后端服务地址

    不积跬步无以至千里
  • 相关阅读:
    Go语言http之请求接收和处理 代码
    C++之IO流的状态以及使用
    C++之指向函数的指针
    C++之数组类型的形参
    C++之vector类型的形参
    C++之形参
    C++之运算符
    C++之多维数组
    C++之动态数组
    C++之指针
  • 原文地址:https://www.cnblogs.com/xiaogblog/p/14445927.html
Copyright © 2011-2022 走看看