Skip to content

模板类型详解

代码生成器提供三种主要的模板类型,分别适用于不同的业务场景。本章将详细介绍每种模板类型的特点、适用场景、生成的代码结构以及配置要点。

模板类型概览

模板类型标识符适用场景特点
单表CRUDcrud独立表的基本增删改查标准化CRUD操作,支持分页、搜索、导入导出
树形结构tree具有父子层级关系的数据树形展示,支持层级操作和节点管理
主子表sub一对多关系的业务数据主子表联动,在同一页面管理关联数据

单表CRUD模板(crud)

概述

单表CRUD模板是最常用的模板类型,适用于大部分标准的业务表。它生成完整的增删改查功能,包括列表展示、条件查询、数据维护等核心功能。

适用场景

  • 用户管理、角色管理等基础数据维护
  • 字典数据、配置参数等系统设置
  • 日志记录、通知公告等信息展示
  • 独立的业务实体管理

生成的代码结构

后端代码

java
// 1. 实体类 (Entity)
@TableName("sys_user")
public class SysUser extends BaseEntity {
    @TableId(value = "user_id")
    private Long userId;
    private String userName;
    private String nickName;
    // ... 其他字段
}

// 2. 业务对象 (Bo)
@Data
@AutoMapper(target = SysUser.class)
public class SysUserBo extends BaseEntity {
    @NotBlank(message = "用户名不能为空", groups = {AddGroup.class, EditGroup.class})
    private String userName;
    // ... 其他字段和验证注解
}

// 3. 视图对象 (Vo)
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = SysUser.class)
public class SysUserVo implements Serializable {
    @ExcelProperty(value = "用户ID")
    private Long userId;
    // ... 其他字段和Excel注解
}

// 4. 控制器 (Controller)
@RestController
@RequestMapping("/system/user")
public class SysUserController {
    
    @GetMapping("/pageSysUsers")
    public R<PageResult<SysUserVo>> pageSysUsers(SysUserBo bo, PageQuery pageQuery) {
        return R.ok(sysUserService.page(bo, pageQuery));
    }
    
    @PostMapping("/addSysUser")
    public R<Long> addSysUser(@Validated(AddGroup.class) @RequestBody SysUserBo bo) {
        return R.ok(sysUserService.add(bo));
    }
    // ... 其他CRUD方法
}

// 5. 服务接口 (Service)
public interface ISysUserService extends IBaseService<SysUser, SysUserBo, SysUserVo> {
    // 继承基础服务接口,可添加自定义方法
}

// 6. 服务实现 (ServiceImpl)
@Service
public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser, SysUserBo, SysUserVo> 
    implements ISysUserService {
    
    @Override
    protected PlusLambdaQuery<SysUser> boToQuery(SysUserBo bo) {
        PlusLambdaQuery<SysUser> lqw = of();
        lqw.eq(SysUser::getUserName, bo.getUserName());
        // ... 查询条件构建
        return lqw;
    }
}

前端代码

vue
<!-- 主页面组件 -->
<template>
  <div class="p-2">
    <!-- 搜索栏 -->
    <ASearchForm ref="queryFormRef" v-model="queryParams" :visible="showSearch">
      <AFormInput label="用户名" v-model="queryParams.userName" @input="handleQuery"></AFormInput>
      <AFormSelect label="状态" v-model="queryParams.status" :options="sys_normal_disable" @change="handleQuery"></AFormSelect>
    </ASearchForm>

    <el-card shadow="hover">
      <!-- 工具栏 -->
      <template #header>
        <el-row :gutter="10" class="mb-2">
          <el-col :span="1.5">
            <el-button type="primary" plain icon="Plus" @click="handleAdd">
              {{ t('新增') }}
            </el-button>
          </el-col>
          <!-- 其他按钮... -->
        </el-row>
      </template>

      <!-- 数据表格 -->
      <el-table ref="userTableRef" v-loading="isLoading" :data="userList">
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column label="用户ID" prop="userId" align="center" />
        <el-table-column label="用户名" prop="userName" align="center" />
        <el-table-column label="状态" prop="status" align="center">
          <template #default="{ row }">
            <DictTag :options="sys_normal_disable" :value="row.status" />
          </template>
        </el-table-column>
        <!-- 操作列 -->
      </el-table>

      <!-- 分页组件 -->
      <Pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" 
                  :total="total" @pagination="getList" />
    </el-card>

    <!-- 新增/编辑对话框 -->
    <el-dialog v-model="dialog.visible" :title="dialog.title">
      <el-form ref="userFormRef" :model="form" :rules="rules">
        <AFormInput label="用户名" v-model="form.userName" prop="userName"></AFormInput>
        <AFormInput label="昵称" v-model="form.nickName" prop="nickName"></AFormInput>
        <!-- 其他表单项... -->
      </el-form>
    </el-dialog>
  </div>
</template>

核心特性

1. 标准CRUD操作

  • 查询功能:支持分页查询、条件筛选、模糊搜索
  • 新增功能:表单验证、数据提交、成功提示
  • 编辑功能:数据回显、表单更新、乐观锁控制
  • 删除功能:单条/批量删除、确认提示、软删除支持

2. 数据导入导出

javascript
// 导出功能
const handleExport = () => {
  useDownload().exportExcel('用户数据', '/system/user/exportSysUsers', queryParams.value)
}

// 导入功能
<AImportExcel
  title="用户数据"
  templateUrl="/system/user/templateSysUsers"
  importUrl="/system/user/importSysUsers"
  @import-success="getList"
>
  <el-button type="info" plain icon="Top">
    {{ t('导入') }}
  </el-button>
</AImportExcel>

3. 字段权限控制

根据配置自动生成字段的增删改查权限:

  • isInsert:是否允许新增时填写
  • isEdit:是否允许编辑时修改
  • isList:是否在列表中显示
  • isQuery:是否作为查询条件

配置要点

表配置

json
{
  "tplCategory": "crud",
  "packageName": "plus.ruoyi.system",
  "moduleName": "system",
  "businessName": "user",
  "functionName": "用户管理",
  "className": "SysUser"
}

字段配置示例

json
{
  "columnName": "user_name",
  "javaField": "userName",
  "javaType": "String",
  "columnComment": "用户名",
  "htmlType": "input",
  "isInsert": "1",
  "isEdit": "1",
  "isList": "1",
  "isQuery": "1",
  "queryType": "LIKE",
  "isRequired": "1"
}

树形结构模板(tree)

概述

树形结构模板专门用于处理具有父子层级关系的数据,如组织机构、菜单管理、分类管理等。它提供树形展示和层级操作功能。

适用场景

  • 组织架构管理
  • 系统菜单管理
  • 商品分类管理
  • 地区行政区划
  • 部门层级管理

必要字段要求

树形结构模板需要数据表包含以下字段:

  • 树编码字段:用于标识节点ID(如:dept_id
  • 树父编码字段:用于标识父节点ID(如:parent_id
  • 树名称字段:用于显示节点名称(如:dept_name

生成的代码结构

后端特殊处理

java
// 服务实现中的树形查询
@Override
protected PlusLambdaQuery<SysDept> boToQuery(SysDeptBo bo) {
    PlusLambdaQuery<SysDept> lqw = of();
    lqw.eq(SysDept::getDeptName, bo.getDeptName());
    lqw.eq(SysDept::getParentId, bo.getParentId());
    // 不需要分页,返回所有数据用于构建树形结构
    return lqw;
}

// 控制器中返回树形数据
@GetMapping("/pageSysDepts")
public R<PageResult<SysDeptVo>> pageSysDepts(SysDeptBo bo, PageQuery pageQuery) {
    // 查询所有数据
    List<SysDeptVo> list = sysDeptService.list(bo);
    // 构建树形结构
    List<SysDeptVo> treeList = buildTree(list);
    return R.ok(PageResult.of(treeList));
}

前端树形展示

vue
<template>
  <div class="p-2">
    <!-- 树形表格 -->
    <el-table
      ref="deptTableRef"
      v-loading="isLoading"
      :data="deptList"
      row-key="deptId"
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      :default-expand-all="isAllExpanded"
    >
      <!-- 部门名称列(树形显示) -->
      <el-table-column label="部门名称" prop="deptName" align="center" />
      <el-table-column label="排序" prop="orderNum" align="center" />
      <el-table-column label="状态" prop="status" align="center">
        <template #default="{ row }">
          <AFormSwitch v-model="row.status" @change="handleStatusChange(row)" />
        </template>
      </el-table-column>
      
      <!-- 操作列 -->
      <el-table-column label="操作" align="center">
        <template #default="{ row }">
          <el-button link type="success" icon="Edit" @click="handleUpdate(row)">修改</el-button>
          <el-button link type="primary" icon="Plus" @click="handleAdd(row)">新增</el-button>
          <el-button link type="danger" icon="Delete" @click="handleDelete(row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 新增/编辑对话框 -->
    <el-dialog v-model="dialog.visible" :title="dialog.title">
      <el-form ref="deptFormRef" :model="form" :rules="rules">
        <!-- 上级部门选择(树形选择器) -->
        <AFormTreeSelect 
          label="上级部门" 
          v-model="form.parentId" 
          :data="deptOptions"
          :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
        />
        <AFormInput label="部门名称" v-model="form.deptName" prop="deptName" />
        <AFormInput label="显示排序" v-model="form.orderNum" prop="orderNum" />
      </el-form>
    </el-dialog>
  </div>
</template>

<script setup>
// 树形数据构建
const buildTree = (data) => {
  return buildTree(data, {
    id: 'deptId',
    parentId: 'parentId'
  })
}

// 展开/折叠控制
const toggleAllExpansion = () => {
  isAllExpanded.value = !isAllExpanded.value
  setAllRowsExpansion(deptList.value, isAllExpanded.value)
}

// 新增子节点
const handleAdd = (row) => {
  reset()
  form.value.parentId = row?.deptId || 0
  dialog.value.visible = true
  dialog.value.title = '新增部门'
}
</script>

核心特性

1. 树形展示

  • 层级结构:自动构建父子关系的树形结构
  • 展开折叠:支持节点的展开和折叠操作
  • 层级标识:通过缩进和图标显示层级关系

2. 树形操作

  • 添加子节点:在任意节点下添加子级数据
  • 移动节点:修改节点的父级关系
  • 删除节点:删除节点及其所有子节点(需要确认)

3. 树形选择器

vue
<!-- 父级节点选择 -->
<AFormTreeSelect 
  label="上级部门" 
  v-model="form.parentId" 
  :data="deptOptions"
  :props="{ 
    value: 'deptId', 
    label: 'deptName', 
    children: 'children' 
  }"
  placeholder="选择上级部门"
/>

配置要点

树形字段配置

在表配置的 options 中需要指定树形字段:

json
{
  "tplCategory": "tree",
  "options": "{
    \"treeCode\": \"dept_id\",
    \"treeParentCode\": \"parent_id\", 
    \"treeName\": \"dept_name\",
    \"parentMenuId\": \"1\"
  }"
}

树形字段说明

  • treeCode:树节点ID字段,必须唯一
  • treeParentCode:父节点ID字段,根节点通常为0
  • treeName:节点显示名称字段

注意事项

  1. 数据完整性:确保父子关系的数据完整性
  2. 循环引用:避免节点的循环引用问题
  3. 根节点:通常根节点的 parent_id 为 0
  4. 排序字段:建议添加 order_num 字段控制同级节点排序

主子表模板(sub)

概述

主子表模板用于处理一对多的业务关系,如订单与订单明细、文章与评论等。它在同一个页面中管理主表和子表数据,提供关联操作功能。

适用场景

  • 订单管理(订单 + 订单明细)
  • 文章管理(文章 + 评论列表)
  • 项目管理(项目 + 任务列表)
  • 合同管理(合同 + 合同条款)
  • 考试管理(试卷 + 题目列表)

表关系要求

主子表模板需要两个相关联的数据表:

  • 主表:存储主要业务数据
  • 子表:存储明细数据,包含指向主表的外键

生成的代码结构

主表代码结构

主表的代码结构与单表CRUD类似,但在页面中会包含子表组件:

vue
<!-- 主表页面 -->
<template>
  <div class="p-2">
    <!-- 主表的搜索和列表... -->
    
    <!-- 新增/编辑对话框 -->
    <el-dialog v-model="dialog.visible" :title="dialog.title" width="1200px">
      <!-- 主表表单 -->
      <el-form ref="orderFormRef" :model="form" :rules="rules">
        <el-row>
          <AFormInput label="订单号" v-model="form.orderNo" prop="orderNo" :span="12" />
          <AFormDate label="订单日期" v-model="form.orderDate" prop="orderDate" :span="12" />
        </el-row>
      </el-form>

      <!-- 子表组件 -->
      <OrderDetailChild :parent-id="form.orderId" />

      <template #footer>
        <el-button :loading="buttonLoading" type="primary" @click="submitForm">确定</el-button>
        <el-button @click="cancel">取消</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import OrderDetailChild from './OrderDetailChild.vue'

// 提交主表表单
const submitForm = async () => {
  // 如果是新增且有子表,保存成功后更新表单ID,保持弹窗打开以便编辑子表
  if (!form.value.orderId && data) {
    form.value.orderId = data
    buttonLoading.value = false
    return
  }
  dialog.value.visible = false
  await getList()
}
</script>

子表组件代码

子表组件专门处理子表的CRUD操作:

vue
<!-- 子表组件 OrderDetailChild.vue -->
<template>
  <div>
    <!-- 提示信息 -->
    <div v-if="!parentId">
      <el-alert title="请先保存主表" type="info" :closable="false" show-icon />
    </div>
    
    <div v-else>
      <el-divider content-position="left">
        <span class="text-16px font-bold">子表: 订单明细</span>
      </el-divider>
      
      <!-- 子表工具栏 -->
      <el-row :gutter="10" class="mb-2">
        <el-col :span="1.5">
          <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
        </el-col>
        <el-col :span="1.5">
          <el-button type="success" plain icon="Edit" :disabled="selectionItems.length !== 1" 
                     @click="handleUpdate()">修改</el-button>
        </el-col>
        <!-- 其他按钮... -->
      </el-row>

      <!-- 子表数据表格 -->
      <el-table ref="detailTableRef" v-loading="isLoading" :data="detailList" height="420" 
                @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column label="商品名称" prop="productName" align="center" />
        <el-table-column label="数量" prop="quantity" align="center" />
        <el-table-column label="单价" prop="price" align="center" />
        <el-table-column label="小计" prop="amount" align="center" />
        <!-- 操作列 -->
      </el-table>

      <!-- 子表新增/编辑对话框 -->
      <el-dialog v-model="dialog.visible" :title="dialog.title" width="600px">
        <el-form ref="detailFormRef" :model="form" :rules="rules">
          <AFormSelect label="商品" v-model="form.productId" :options="productOptions" />
          <AFormInput label="数量" v-model="form.quantity" type="number" />
          <AFormInput label="单价" v-model="form.price" type="number" />
        </el-form>
      </el-dialog>
    </div>
  </div>
</template>

<script setup>
// Props定义
interface OrderDetailChildProps {
  parentId?: string | number  // 主表ID
}

const props = defineProps<OrderDetailChildProps>()

// 查询子表数据
const getList = async () => {
  if (!props.parentId) return
  
  queryParams.value.orderId = props.parentId  // 设置主表ID作为查询条件
  const [err, data] = await pageOrderDetails(queryParams.value)
  if (!err) {
    detailList.value = data.records
    total.value = data.total
  }
}

// 监听父ID变化
watch(
  () => props.parentId,
  (newId) => {
    if (newId) {
      queryParams.value.orderId = newId
      getList()
    } else {
      detailList.value = []
      total.value = 0
    }
  },
  { immediate: true }
)

// 新增子表记录
const handleAdd = () => {
  reset()
  form.value.orderId = props.parentId  // 设置主表关联ID
  dialog.value.visible = true
  dialog.value.title = '新增订单明细'
}
</script>

核心特性

1. 主子表关联

  • 自动关联:子表记录自动关联主表ID
  • 联动操作:主表保存后才能操作子表
  • 级联删除:删除主表时可选择是否删除子表数据

2. 数据完整性

  • 事务处理:主子表操作在同一事务中处理
  • 约束检查:子表数据的外键约束验证
  • 数据同步:主子表数据的同步更新

3. 用户体验

  • 分步操作:先保存主表,再编辑子表
  • 状态提示:明确显示操作状态和提示信息
  • 批量操作:支持子表数据的批量导入导出

配置要点

主表配置

json
{
  "tplCategory": "sub",
  "subTableName": "order_detail",      // 子表名
  "subTableFkName": "order_id"         // 子表外键字段名
}

子表字段配置

子表需要包含指向主表的外键字段:

json
{
  "columnName": "order_id",
  "javaField": "orderId", 
  "columnComment": "订单ID",
  "javaType": "Long",
  "isInsert": "1",    // 新增时必填
  "isEdit": "0",      // 编辑时不可修改
  "isList": "0",      // 列表中不显示(隐藏字段)
  "isQuery": "1"      // 可作为查询条件
}

业务流程

新增流程

mermaid
sequenceDiagram
    participant U as 用户
    participant M as 主表组件
    participant S as 子表组件
    
    U->>M: 点击新增按钮
    M->>U: 显示主表表单
    U->>M: 填写主表信息并保存
    M->>M: 保存主表数据,获取主表ID
    M->>S: 传递主表ID给子表组件
    S->>U: 显示子表操作界面
    U->>S: 添加子表数据
    S->>S: 保存子表数据(包含主表ID)

编辑流程

mermaid
sequenceDiagram
    participant U as 用户
    participant M as 主表组件  
    participant S as 子表组件
    
    U->>M: 点击编辑按钮
    M->>M: 加载主表数据
    M->>S: 传递主表ID
    S->>S: 加载子表数据列表
    M->>U: 显示主子表编辑界面
    U->>M: 修改主表信息
    U->>S: 修改子表信息
    U->>M: 保存全部修改

注意事项

  1. 数据依赖关系

    • 必须先保存主表才能操作子表
    • 子表记录必须关联有效的主表ID
  2. 删除策略

    • 删除主表前需要处理子表数据
    • 可以选择级联删除或阻止删除
  3. 事务控制

    • 主子表的CUD操作应在同一事务中
    • 确保数据的一致性和完整性
  4. 性能考虑

    • 子表数据量大时考虑分页加载
    • 避免一次性加载过多子表数据

模板选择指南

选择依据

业务特征推荐模板原因
独立的业务实体,无复杂关联单表CRUD标准化程度高,功能完善
数据具有明确的层级关系树形结构天然支持层级展示和操作
存在一对多的业务关系主子表在同一界面管理关联数据
需要批量数据处理单表CRUD内置导入导出功能
需要复杂的权限控制树形结构支持按层级控制权限

实际案例

1. 用户管理 → 单表CRUD

sql
CREATE TABLE sys_user (
    user_id BIGINT PRIMARY KEY,
    user_name VARCHAR(30) NOT NULL,
    nick_name VARCHAR(30),
    email VARCHAR(50),
    phone VARCHAR(11),
    sex CHAR(1),
    status CHAR(1) DEFAULT '0',
    create_time DATETIME,
    update_time DATETIME
);

2. 部门管理 → 树形结构

sql
CREATE TABLE sys_dept (
    dept_id BIGINT PRIMARY KEY,
    parent_id BIGINT DEFAULT 0,
    dept_name VARCHAR(30) NOT NULL,
    order_num INT DEFAULT 0,
    leader VARCHAR(20),
    phone VARCHAR(11),
    status CHAR(1) DEFAULT '0',
    create_time DATETIME,
    update_time DATETIME
);

3. 订单管理 → 主子表

sql
-- 主表
CREATE TABLE sales_order (
    order_id BIGINT PRIMARY KEY,
    order_no VARCHAR(50) NOT NULL,
    customer_name VARCHAR(100),
    order_date DATE,
    total_amount DECIMAL(10,2),
    status CHAR(1),
    create_time DATETIME,
    update_time DATETIME
);

-- 子表
CREATE TABLE order_detail (
    detail_id BIGINT PRIMARY KEY,
    order_id BIGINT NOT NULL,  -- 外键
    product_name VARCHAR(100),
    quantity INT,
    price DECIMAL(8,2),
    amount DECIMAL(10,2),
    create_time DATETIME,
    FOREIGN KEY (order_id) REFERENCES sales_order(order_id)
);

总结

选择合适的模板类型是代码生成成功的关键因素:

  • 单表CRUD:适用于大部分标准业务场景,生成完整的数据管理功能
  • 树形结构:专门处理层级数据,提供直观的树形操作界面
  • 主子表:处理复杂的关联关系,在同一界面管理主从数据

在实际使用中,需要根据业务需求、数据结构和用户体验需求来选择合适的模板类型,确保生成的代码既满足功能需求,又符合用户操作习惯。