Commit 12bcee2a7249c0b0d819861fff92cce5838c407f

Authored by 芋道源码
Committed by Gitee
2 parents 7ac74b44 6d228828

!734 代码生成:重构 vue2 代码生成模版,使用 async await 优化代码层次,适配树表和主子表

Merge pull request !734 from puhui999/feature/sub-table
Showing 120 changed files with 10743 additions and 790 deletions
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -104,6 +104,18 @@ public class CodegenEngine { @@ -104,6 +104,18 @@ public class CodegenEngine {
104 vueFilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 104 vueFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
105 .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("api/api.js"), 105 .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("api/api.js"),
106 vueFilePath("api/${table.moduleName}/${classNameVar}.js")) 106 vueFilePath("api/${table.moduleName}/${classNameVar}.js"))
  107 + .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/form.vue"),
  108 + vueFilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))
  109 + .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑
  110 + vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
  111 + .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑
  112 + vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
  113 + .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑
  114 + vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
  115 + .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑
  116 + vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
  117 + .put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑
  118 + vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
107 // Vue3 标准模版 119 // Vue3 标准模版
108 .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/index.vue"), 120 .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/index.vue"),
109 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) 121 vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
@@ -285,6 +297,10 @@ public class CodegenEngine { @@ -285,6 +297,10 @@ public class CodegenEngine {
285 if (StrUtil.count(content, "dateFormatter") == 1) { 297 if (StrUtil.count(content, "dateFormatter") == 1) {
286 content = StrUtils.removeLineContains(content, "dateFormatter"); 298 content = StrUtils.removeLineContains(content, "dateFormatter");
287 } 299 }
  300 + // Vue2 界面:修正 $refs
  301 + if (StrUtil.count(content, "this.refs") >= 1) {
  302 + content = content.replace("this.refs", "this.$refs");
  303 + }
288 // Vue 界面:去除多的 dict 相关,只有一个的情况下,说明没使用到 304 // Vue 界面:去除多的 dict 相关,只有一个的情况下,说明没使用到
289 if (StrUtil.count(content, "getIntDictOptions") == 1) { 305 if (StrUtil.count(content, "getIntDictOptions") == 1) {
290 content = content.replace("getIntDictOptions, ", ""); 306 content = content.replace("getIntDictOptions, ", "");
@@ -452,7 +468,7 @@ public class CodegenEngine { @@ -452,7 +468,7 @@ public class CodegenEngine {
452 } 468 }
453 469
454 private static String vueFilePath(String path) { 470 private static String vueFilePath(String path) {
455 - return "yudao-ui-${sceneEnum.basePackage}/" + // 顶级目录 471 + return "yudao-ui-${sceneEnum.basePackage}-vue2/" + // 顶级目录
456 "src/" + path; 472 "src/" + path;
457 } 473 }
458 474
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm
@@ -35,21 +35,113 @@ export function get${simpleClassName}(id) { @@ -35,21 +35,113 @@ export function get${simpleClassName}(id) {
35 }) 35 })
36 } 36 }
37 37
  38 +#if ( $table.templateType != 2 )
38 // 获得${table.classComment}分页 39 // 获得${table.classComment}分页
39 -export function get${simpleClassName}Page(query) { 40 +export function get${simpleClassName}Page(params) {
40 return request({ 41 return request({
41 url: '${baseURL}/page', 42 url: '${baseURL}/page',
42 method: 'get', 43 method: 'get',
43 - params: query 44 + params
44 }) 45 })
45 } 46 }
46 - 47 +#else
  48 +// 获得${table.classComment}列表
  49 +export function get${simpleClassName}List(params) {
  50 + return request({
  51 + url: '${baseURL}/list',
  52 + method: 'get',
  53 + params
  54 + })
  55 +}
  56 +#end
47 // 导出${table.classComment} Excel 57 // 导出${table.classComment} Excel
48 -export function export${simpleClassName}Excel(query) { 58 +export function export${simpleClassName}Excel(params) {
49 return request({ 59 return request({
50 url: '${baseURL}/export-excel', 60 url: '${baseURL}/export-excel',
51 method: 'get', 61 method: 'get',
52 - params: query, 62 + params,
53 responseType: 'blob' 63 responseType: 'blob'
54 }) 64 })
55 } 65 }
  66 +## 特殊:主子表专属逻辑
  67 +#foreach ($subTable in $subTables)
  68 + #set ($index = $foreach.count - 1)
  69 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  70 + #set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
  71 + #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
  72 + #set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
  73 + #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
  74 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  75 + #set ($subClassNameVar = $subClassNameVars.get($index))
  76 +
  77 +// ==================== 子表($subTable.classComment) ====================
  78 + ## 情况一:MASTER_ERP 时,需要分查询页子表
  79 + #if ( $table.templateType == 11 )
  80 +
  81 + // 获得${subTable.classComment}分页
  82 + export function get${subSimpleClassName}Page(params) {
  83 + return request({
  84 + url: '${baseURL}/${subSimpleClassName_strikeCase}/page',
  85 + method: 'get',
  86 + params
  87 + })
  88 + }
  89 + ## 情况二:非 MASTER_ERP 时,需要列表查询子表
  90 + #else
  91 + #if ( $subTable.subJoinMany )
  92 +
  93 + // 获得${subTable.classComment}列表
  94 + export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) {
  95 + return request({
  96 + url: `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField},
  97 + method: 'get'
  98 + })
  99 + }
  100 + #else
  101 +
  102 + // 获得${subTable.classComment}
  103 + export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}) {
  104 + return request({
  105 + url: `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField},
  106 + method: 'get'
  107 + })
  108 + }
  109 + #end
  110 + #end
  111 + ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
  112 + #if ( $table.templateType == 11 )
  113 + // 新增${subTable.classComment}
  114 + export function create${subSimpleClassName}(data) {
  115 + return request({
  116 + url: `${baseURL}/${subSimpleClassName_strikeCase}/create`,
  117 + method: 'post',
  118 + data
  119 + })
  120 + }
  121 +
  122 + // 修改${subTable.classComment}
  123 + export function update${subSimpleClassName}(data) {
  124 + return request({
  125 + url: `${baseURL}/${subSimpleClassName_strikeCase}/update`,
  126 + method: 'post',
  127 + data
  128 + })
  129 + }
  130 +
  131 + // 删除${subTable.classComment}
  132 + export function delete${subSimpleClassName}(id) {
  133 + return request({
  134 + url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id,
  135 + method: 'delete'
  136 + })
  137 + }
  138 +
  139 + // 获得${subTable.classComment}
  140 + export function get${subSimpleClassName}(id) {
  141 + return request({
  142 + url: `${baseURL}/${subSimpleClassName_strikeCase}/get?id=` + id,
  143 + method: 'get'
  144 + })
  145 + }
  146 + #end
  147 +#end
56 \ No newline at end of file 148 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm 0 → 100644
  1 +#set ($subTable = $subTables.get($subIndex))##当前表
  2 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
  3 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
  4 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
  5 +<template>
  6 + <div class="app-container">
  7 + <!-- 对话框(添加 / 修改) -->
  8 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  9 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  10 + #foreach($column in $subColumns)
  11 + #if ($column.createOperation || $column.updateOperation)
  12 + #set ($dictType = $column.dictType)
  13 + #set ($javaField = $column.javaField)
  14 + #set ($javaType = $column.javaType)
  15 + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  16 + #set ($comment = $column.columnComment)
  17 + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
  18 + #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
  19 + <el-form-item label="${comment}" prop="${javaField}">
  20 + <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
  21 + </el-form-item>
  22 + #elseif($column.htmlType == "imageUpload")## 图片上传
  23 + #set ($hasImageUploadColumn = true)
  24 + <el-form-item label="${comment}" prop="${javaField}">
  25 + <ImageUpload v-model="formData.${javaField}"/>
  26 + </el-form-item>
  27 + #elseif($column.htmlType == "fileUpload")## 文件上传
  28 + #set ($hasFileUploadColumn = true)
  29 + <el-form-item label="${comment}" prop="${javaField}">
  30 + <FileUpload v-model="formData.${javaField}"/>
  31 + </el-form-item>
  32 + #elseif($column.htmlType == "editor")## 文本编辑器
  33 + #set ($hasEditorColumn = true)
  34 + <el-form-item label="${comment}" prop="${javaField}">
  35 + <editor v-model="formData.${javaField}" :min-height="192"/>
  36 + </el-form-item>
  37 + #elseif($column.htmlType == "select")## 下拉框
  38 + <el-form-item label="${comment}" prop="${javaField}">
  39 + <el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
  40 + #if ("" != $dictType)## 有数据字典
  41 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  42 + :key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
  43 + #else##没数据字典
  44 + <el-option label="请选择字典生成" value="" />
  45 + #end
  46 + </el-select>
  47 + </el-form-item>
  48 + #elseif($column.htmlType == "checkbox")## 多选框
  49 + <el-form-item label="${comment}" prop="${javaField}">
  50 + <el-checkbox-group v-model="formData.${javaField}">
  51 + #if ("" != $dictType)## 有数据字典
  52 + <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  53 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
  54 + #else##没数据字典
  55 + <el-checkbox>请选择字典生成</el-checkbox>
  56 + #end
  57 + </el-checkbox-group>
  58 + </el-form-item>
  59 + #elseif($column.htmlType == "radio")## 单选框
  60 + <el-form-item label="${comment}" prop="${javaField}">
  61 + <el-radio-group v-model="formData.${javaField}">
  62 + #if ("" != $dictType)## 有数据字典
  63 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  64 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
  65 + #else:label="dict.value"#end>{{dict.label}}</el-radio>
  66 + #else##没数据字典
  67 + <el-radio label="1">请选择字典生成</el-radio>
  68 + #end
  69 + </el-radio-group>
  70 + </el-form-item>
  71 + #elseif($column.htmlType == "datetime")## 时间框
  72 + <el-form-item label="${comment}" prop="${javaField}">
  73 + <el-date-picker clearable v-model="formData.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
  74 + </el-form-item>
  75 + #elseif($column.htmlType == "textarea")## 文本框
  76 + <el-form-item label="${comment}" prop="${javaField}">
  77 + <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入内容" />
  78 + </el-form-item>
  79 + #end
  80 + #end
  81 + #end
  82 + </el-form>
  83 + <div slot="footer" class="dialog-footer">
  84 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  85 + <el-button @click="dialogVisible = false">取 消</el-button>
  86 + </div>
  87 + </el-dialog>
  88 + </div>
  89 +</template>
  90 +
  91 +<script>
  92 + import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  93 + #if ($hasImageUploadColumn)
  94 + import ImageUpload from '@/components/ImageUpload';
  95 + #end
  96 + #if ($hasFileUploadColumn)
  97 + import FileUpload from '@/components/FileUpload';
  98 + #end
  99 + #if ($hasEditorColumn)
  100 + import Editor from '@/components/Editor';
  101 + #end
  102 + export default {
  103 + name: "${subSimpleClassName}Form",
  104 + components: {
  105 + #if ($hasImageUploadColumn)
  106 + ImageUpload,
  107 + #end
  108 + #if ($hasFileUploadColumn)
  109 + FileUpload,
  110 + #end
  111 + #if ($hasEditorColumn)
  112 + Editor,
  113 + #end
  114 + },
  115 + data() {
  116 + return {
  117 + // 弹出层标题
  118 + dialogTitle: "",
  119 + // 是否显示弹出层
  120 + dialogVisible: false,
  121 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  122 + formLoading: false,
  123 + // 表单参数
  124 + formData: {
  125 + #foreach ($column in $subColumns)
  126 + #if ($column.createOperation || $column.updateOperation)
  127 + #if ($column.htmlType == "checkbox")
  128 + $column.javaField: [],
  129 + #else
  130 + $column.javaField: undefined,
  131 + #end
  132 + #end
  133 + #end
  134 + },
  135 + // 表单校验
  136 + formRules: {
  137 + #foreach ($column in $subColumns)
  138 + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
  139 + #set($comment=$column.columnComment)
  140 + $column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
  141 + #end
  142 + #end
  143 + },
  144 + };
  145 + },
  146 + methods: {
  147 + /** 打开弹窗 */
  148 + async open(id, ${subJoinColumn.javaField}) {
  149 + this.dialogVisible = true;
  150 + this.reset();
  151 + this.formData.${subJoinColumn.javaField} = ${subJoinColumn.javaField};
  152 + // 修改时,设置数据
  153 + if (id) {
  154 + this.formLoading = true;
  155 + try {
  156 + const res = await ${simpleClassName}Api.get${subSimpleClassName}(id);
  157 + this.formData = res.data;
  158 + this.dialogTitle = "修改${subTable.classComment}";
  159 + } finally {
  160 + this.formLoading = false;
  161 + }
  162 + }
  163 + this.dialogTitle = "新增${subTable.classComment}";
  164 + },
  165 + /** 提交按钮 */
  166 + async submitForm() {
  167 + await this.#[[$]]#refs["formRef"].validate();
  168 + this.formLoading = true;
  169 + try {
  170 + const data = this.formData;
  171 + // 修改的提交
  172 + if (data.${primaryColumn.javaField}) {
  173 + await ${simpleClassName}Api.update${subSimpleClassName}(data);
  174 + this.#[[$modal]]#.msgSuccess("修改成功");
  175 + this.dialogVisible = false;
  176 + this.#[[$]]#emit('success');
  177 + return;
  178 + }
  179 + // 添加的提交
  180 + await ${simpleClassName}Api.create${subSimpleClassName}(data);
  181 + this.#[[$modal]]#.msgSuccess("新增成功");
  182 + this.dialogVisible = false;
  183 + this.#[[$]]#emit('success');
  184 + }finally {
  185 + this.formLoading = false;
  186 + }
  187 + },
  188 + /** 表单重置 */
  189 + reset() {
  190 + this.formData = {
  191 + #foreach ($column in $subColumns)
  192 + #if ($column.createOperation || $column.updateOperation)
  193 + #if ($column.htmlType == "checkbox")
  194 + $column.javaField: [],
  195 + #else
  196 + $column.javaField: undefined,
  197 + #end
  198 + #end
  199 + #end
  200 + };
  201 + this.resetForm("formRef");
  202 + },
  203 + }
  204 + };
  205 +</script>
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm 0 → 100644
  1 +## 主表的 normal 和 inner 使用相同的 form 表单
  2 +#parse("codegen/vue/views/components/form_sub_normal.vue.vm")
0 \ No newline at end of file 3 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm 0 → 100644
  1 +#set ($subTable = $subTables.get($subIndex))##当前表
  2 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
  3 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
  4 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
  5 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
  6 +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
  7 +<template>
  8 + <div class="app-container">
  9 + #if ( $subTable.subJoinMany )## 情况一:一对多,table + form
  10 + <el-form
  11 + ref="formRef"
  12 + :model="formData"
  13 + :rules="formRules"
  14 + v-loading="formLoading"
  15 + label-width="0px"
  16 + :inline-message="true"
  17 + >
  18 + <el-table :data="formData" class="-mt-10px">
  19 + <el-table-column label="序号" type="index" width="100" />
  20 + #foreach($column in $subColumns)
  21 + #if ($column.createOperation || $column.updateOperation)
  22 + #set ($dictType = $column.dictType)
  23 + #set ($javaField = $column.javaField)
  24 + #set ($javaType = $column.javaType)
  25 + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  26 + #set ($comment = $column.columnComment)
  27 + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
  28 + #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
  29 + <el-table-column label="${comment}" min-width="150">
  30 + <template v-slot="{ row, $index }">
  31 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  32 + <el-input v-model="row.${javaField}" placeholder="请输入${comment}" />
  33 + </el-form-item>
  34 + </template>
  35 + </el-table-column>
  36 + #elseif($column.htmlType == "imageUpload")## 图片上传
  37 + #set ($hasImageUploadColumn = true)
  38 + <el-table-column label="${comment}" min-width="200">
  39 + <template v-slot="{ row, $index }">
  40 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  41 + <ImageUpload v-model="row.${javaField}"/>
  42 + </el-form-item>
  43 + </template>
  44 + </el-table-column>
  45 + #elseif($column.htmlType == "fileUpload")## 文件上传
  46 + #set ($hasFileUploadColumn = true)
  47 + <el-table-column label="${comment}" min-width="200">
  48 + <template v-slot="{ row, $index }">
  49 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  50 + <FileUpload v-model="row.${javaField}"/>
  51 + </el-form-item>
  52 + </template>
  53 + </el-table-column>
  54 + #elseif($column.htmlType == "editor")## 文本编辑器
  55 + #set ($hasEditorColumn = true)
  56 + <el-table-column label="${comment}" min-width="400">
  57 + <template v-slot="{ row, $index }">
  58 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  59 + <Editor v-model="row.${javaField}" :min-height="192"/>
  60 + </el-form-item>
  61 + </template>
  62 + </el-table-column>
  63 + #elseif($column.htmlType == "select")## 下拉框
  64 + <el-table-column label="${comment}" min-width="150">
  65 + <template v-slot="{ row, $index }">
  66 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  67 + <el-select v-model="row.${javaField}" placeholder="请选择${comment}">
  68 + #if ("" != $dictType)## 有数据字典
  69 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  70 + :key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
  71 + #else##没数据字典
  72 + <el-option label="请选择字典生成" value="" />
  73 + #end
  74 + </el-select>
  75 + </el-form-item>
  76 + </template>
  77 + </el-table-column>
  78 + #elseif($column.htmlType == "checkbox")## 多选框
  79 + <el-table-column label="${comment}" min-width="150">
  80 + <template v-slot="{ row, $index }">
  81 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  82 + <el-checkbox-group v-model="row.${javaField}">
  83 + #if ("" != $dictType)## 有数据字典
  84 + <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  85 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
  86 + #else##没数据字典
  87 + <el-checkbox>请选择字典生成</el-checkbox>
  88 + #end
  89 + </el-checkbox-group>
  90 + </el-form-item>
  91 + </template>
  92 + </el-table-column>
  93 + #elseif($column.htmlType == "radio")## 单选框
  94 + <el-table-column label="${comment}" min-width="150">
  95 + <template v-slot="{ row, $index }">
  96 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  97 + <el-radio-group v-model="row.${javaField}">
  98 + #if ("" != $dictType)## 有数据字典
  99 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  100 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
  101 + #else:label="dict.value"#end>{{dict.label}}</el-radio>
  102 + #else##没数据字典
  103 + <el-radio label="1">请选择字典生成</el-radio>
  104 + #end
  105 + </el-radio-group>
  106 + </el-form-item>
  107 + </template>
  108 + </el-table-column>
  109 + #elseif($column.htmlType == "datetime")## 时间框
  110 + <el-table-column label="${comment}" min-width="150">
  111 + <template v-slot="{ row, $index }">
  112 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  113 + <el-date-picker clearable v-model="row.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
  114 + </el-form-item>
  115 + </template>
  116 + </el-table-column>
  117 + #elseif($column.htmlType == "textarea")## 文本框
  118 + <el-table-column label="${comment}" min-width="200">
  119 + <template v-slot="{ row, $index }">
  120 + <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
  121 + <el-input v-model="row.${javaField}" type="textarea" placeholder="请输入${comment}" />
  122 + </el-form-item>
  123 + </template>
  124 + </el-table-column>
  125 + #end
  126 + #end
  127 + #end
  128 + <el-table-column align="center" fixed="right" label="操作" width="60">
  129 + <template v-slot="{ $index }">
  130 + <el-link @click="handleDelete($index)">—</el-link>
  131 + </template>
  132 + </el-table-column>
  133 + </el-table>
  134 + </el-form>
  135 + <el-row justify="center" class="mt-3">
  136 + <el-button @click="handleAdd" round>+ 添加${subTable.classComment}</el-button>
  137 + </el-row>
  138 + #else## 情况二:一对一,form
  139 + <el-form
  140 + ref="formRef"
  141 + :model="formData"
  142 + :rules="formRules"
  143 + label-width="100px"
  144 + v-loading="formLoading"
  145 + >
  146 + #foreach($column in $subColumns)
  147 + #if ($column.createOperation || $column.updateOperation)
  148 + #set ($dictType = $column.dictType)
  149 + #set ($javaField = $column.javaField)
  150 + #set ($javaType = $column.javaType)
  151 + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  152 + #set ($comment = $column.columnComment)
  153 + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
  154 + #elseif ($column.htmlType == "input" && !$column.primaryKey)
  155 + <el-form-item label="${comment}" prop="${javaField}">
  156 + <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
  157 + </el-form-item>
  158 + #elseif($column.htmlType == "imageUpload")## 图片上传
  159 + #set ($hasImageUploadColumn = true)
  160 + <el-form-item label="${comment}">
  161 + <ImageUpload v-model="formData.${javaField}"/>
  162 + </el-form-item>
  163 + #elseif($column.htmlType == "fileUpload")## 文件上传
  164 + #set ($hasFileUploadColumn = true)
  165 + <el-form-item label="${comment}">
  166 + <FileUpload v-model="formData.${javaField}"/>
  167 + </el-form-item>
  168 + #elseif($column.htmlType == "editor")## 文本编辑器
  169 + #set ($hasEditorColumn = true)
  170 + <el-form-item label="${comment}">
  171 + <Editor v-model="formData.${javaField}" :min-height="192"/>
  172 + </el-form-item>
  173 + #elseif($column.htmlType == "select")## 下拉框
  174 + <el-form-item label="${comment}" prop="${javaField}">
  175 + <el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
  176 + #if ("" != $dictType)## 有数据字典
  177 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  178 + :key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
  179 + #else##没数据字典
  180 + <el-option label="请选择字典生成" value="" />
  181 + #end
  182 + </el-select>
  183 + </el-form-item>
  184 + #elseif($column.htmlType == "checkbox")## 多选框
  185 + <el-form-item label="${comment}" prop="${javaField}">
  186 + <el-checkbox-group v-model="formData.${javaField}">
  187 + #if ("" != $dictType)## 有数据字典
  188 + <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  189 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
  190 + #else##没数据字典
  191 + <el-checkbox>请选择字典生成</el-checkbox>
  192 + #end
  193 + </el-checkbox-group>
  194 + </el-form-item>
  195 + #elseif($column.htmlType == "radio")## 单选框
  196 + <el-form-item label="${comment}" prop="${javaField}">
  197 + <el-radio-group v-model="formData.${javaField}">
  198 + #if ("" != $dictType)## 有数据字典
  199 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  200 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
  201 + #else:label="dict.value"#end>{{dict.label}}</el-radio>
  202 + #else##没数据字典
  203 + <el-radio label="1">请选择字典生成</el-radio>
  204 + #end
  205 + </el-radio-group>
  206 + </el-form-item>
  207 + #elseif($column.htmlType == "datetime")## 时间框
  208 + <el-form-item label="${comment}" prop="${javaField}">
  209 + <el-date-picker clearable v-model="formData.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
  210 + </el-form-item>
  211 + #elseif($column.htmlType == "textarea")## 文本框
  212 + <el-form-item label="${comment}" prop="${javaField}">
  213 + <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
  214 + </el-form-item>
  215 + #end
  216 + #end
  217 + #end
  218 + </el-form>
  219 + #end
  220 + </div>
  221 +</template>
  222 +
  223 +<script>
  224 + import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  225 + #if ($hasImageUploadColumn)
  226 + import ImageUpload from '@/components/ImageUpload';
  227 + #end
  228 + #if ($hasFileUploadColumn)
  229 + import FileUpload from '@/components/FileUpload';
  230 + #end
  231 + #if ($hasEditorColumn)
  232 + import Editor from '@/components/Editor';
  233 + #end
  234 + export default {
  235 + name: "${subSimpleClassName}Form",
  236 + components: {
  237 + #if ($hasImageUploadColumn)
  238 + ImageUpload,
  239 + #end
  240 + #if ($hasFileUploadColumn)
  241 + FileUpload,
  242 + #end
  243 + #if ($hasEditorColumn)
  244 + Editor,
  245 + #end
  246 + },
  247 + props:[
  248 + '${subJoinColumn.javaField}'
  249 + ],// ${subJoinColumn.columnComment}(主表的关联字段)
  250 + data() {
  251 + return {
  252 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  253 + formLoading: false,
  254 + // 表单参数
  255 + formData: [],
  256 + // 表单校验
  257 + formRules: {
  258 + #foreach ($column in $subColumns)
  259 + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
  260 + #set($comment=$column.columnComment)
  261 + $column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
  262 + #end
  263 + #end
  264 + },
  265 + };
  266 + },
  267 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  268 + ${subJoinColumn.javaField}:{
  269 + handler(val) {
  270 + // 1. 重置表单
  271 + #if ( $subTable.subJoinMany )
  272 + this.formData = []
  273 + #else
  274 + this.formData = {
  275 + #foreach ($column in $subColumns)
  276 + #if ($column.createOperation || $column.updateOperation)
  277 + #if ($column.htmlType == "checkbox")
  278 + $column.javaField: [],
  279 + #else
  280 + $column.javaField: undefined,
  281 + #end
  282 + #end
  283 + #end
  284 + }
  285 + #end
  286 + // 2. val 非空,则加载数据
  287 + if (!val) {
  288 + return;
  289 + }
  290 + try {
  291 + this.formLoading = true;
  292 + // 这里还是需要获取一下 this 的不然取不到 formData
  293 + const that = this;
  294 + #if ( $subTable.subJoinMany )
  295 + ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val).then(function (res){
  296 + that.formData = res.data;
  297 + })
  298 + #else
  299 + ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val).then(function (res){
  300 + const data = res.data;
  301 + if (!data) {
  302 + return
  303 + }
  304 + that.formData = data;
  305 + })
  306 + #end
  307 + } finally {
  308 + this.formLoading = false;
  309 + }
  310 + },
  311 + immediate: true
  312 + }
  313 + },
  314 + methods: {
  315 + #if ( $subTable.subJoinMany )
  316 + /** 新增按钮操作 */
  317 + handleAdd() {
  318 + const row = {
  319 + #foreach ($column in $subColumns)
  320 + #if ($column.createOperation || $column.updateOperation)
  321 + #if ($column.htmlType == "checkbox")
  322 + $column.javaField: [],
  323 + #else
  324 + $column.javaField: undefined,
  325 + #end
  326 + #end
  327 + #end
  328 + }
  329 + row.${subJoinColumn.javaField} = this.${subJoinColumn.javaField};
  330 + this.formData.push(row);
  331 + },
  332 + /** 删除按钮操作 */
  333 + handleDelete(index) {
  334 + this.formData.splice(index, 1);
  335 + },
  336 + #end
  337 + /** 表单校验 */
  338 + validate(){
  339 + return this.#[[$]]#refs["formRef"].validate();
  340 + },
  341 + /** 表单值 */
  342 + getData(){
  343 + return this.formData;
  344 + }
  345 + }
  346 + };
  347 +</script>
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm 0 → 100644
  1 +#set ($subTable = $subTables.get($subIndex))##当前表
  2 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
  3 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
  4 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
  5 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
  6 +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
  7 +<template>
  8 + <div class="app-container">
  9 +#if ($table.templateType == 11)
  10 + <!-- 操作工具栏 -->
  11 + <el-row :gutter="10" class="mb8">
  12 + <el-col :span="1.5">
  13 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  14 + v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
  15 + </el-col>
  16 + </el-row>
  17 +#end
  18 + ## 列表
  19 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  20 + #foreach($column in $subColumns)
  21 + #if ($column.listOperationResult)
  22 + #set ($dictType=$column.dictType)
  23 + #set ($javaField = $column.javaField)
  24 + #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  25 + #set ($comment=$column.columnComment)
  26 + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
  27 + #elseif ($column.javaType == "LocalDateTime")## 时间类型
  28 + <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
  29 + <template v-slot="scope">
  30 + <span>{{ parseTime(scope.row.${javaField}) }}</span>
  31 + </template>
  32 + </el-table-column>
  33 + #elseif($column.dictType && "" != $column.dictType)## 数据字典
  34 + <el-table-column label="${comment}" align="center" prop="${javaField}">
  35 + <template v-slot="scope">
  36 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
  37 + </template>
  38 + </el-table-column>
  39 + #else
  40 + <el-table-column label="${comment}" align="center" prop="${javaField}" />
  41 + #end
  42 + #end
  43 + #end
  44 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  45 + <template v-slot="scope">
  46 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.${primaryColumn.javaField})"
  47 + v-hasPermi="['${permissionPrefix}:update']">修改</el-button>
  48 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  49 + v-hasPermi="['${permissionPrefix}:delete']">删除</el-button>
  50 + </template>
  51 + </el-table-column>
  52 + </el-table>
  53 +#if ($table.templateType == 11)
  54 + <!-- 分页组件 -->
  55 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  56 + @pagination="getList"/>
  57 + <!-- 对话框(添加 / 修改) -->
  58 + <${subSimpleClassName}Form ref="formRef" @success="getList" />
  59 +#end
  60 + </div>
  61 +</template>
  62 +
  63 +<script>
  64 + import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  65 + #if ($table.templateType == 11)
  66 + import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue'
  67 + #end
  68 + export default {
  69 + name: "${subSimpleClassName}List",
  70 +#if ($table.templateType == 11)
  71 + components: {
  72 + ${subSimpleClassName}Form
  73 + },
  74 +#end
  75 + props:[
  76 + '${subJoinColumn.javaField}'
  77 + ],// ${subJoinColumn.columnComment}(主表的关联字段)
  78 + data() {
  79 + return {
  80 + // 遮罩层
  81 + loading: true,
  82 + // 列表的数据
  83 + list: [],
  84 +#if ($table.templateType == 11)
  85 + // 列表的总页数
  86 + total: 0,
  87 + // 查询参数
  88 + queryParams: {
  89 + pageNo: 1,
  90 + pageSize: 10,
  91 + ${subJoinColumn.javaField}: undefined
  92 + }
  93 +#end
  94 + };
  95 + },
  96 +#if ($table.templateType != 11)
  97 + created() {
  98 + this.getList();
  99 + },
  100 +#end
  101 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  102 + ${subJoinColumn.javaField}:{
  103 + handler(val) {
  104 + this.queryParams.${subJoinColumn.javaField} = val;
  105 + if (val){
  106 + this.handleQuery();
  107 + }
  108 + },
  109 + immediate: true
  110 + }
  111 + },
  112 + methods: {
  113 + /** 查询列表 */
  114 + async getList() {
  115 + try {
  116 + this.loading = true;
  117 + #if ($table.templateType == 11)
  118 + const res = await ${simpleClassName}Api.get${subSimpleClassName}Page(this.queryParams);
  119 + this.list = res.data.list;
  120 + this.total = res.data.total;
  121 + #else
  122 + #if ( $subTable.subJoinMany )
  123 + const res = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(this.${subJoinColumn.javaField});
  124 + this.list = res.data;
  125 + #else
  126 + const res = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(this.${subJoinColumn.javaField});
  127 + const data = res.data;
  128 + if (!data) {
  129 + return;
  130 + }
  131 + this.list.push(data);
  132 + #end
  133 + #end
  134 + } finally {
  135 + this.loading = false;
  136 + }
  137 + },
  138 + /** 搜索按钮操作 */
  139 + handleQuery() {
  140 + this.queryParams.pageNo = 1;
  141 + this.getList();
  142 + },
  143 +#if ($table.templateType == 11)
  144 + /** 添加/修改操作 */
  145 + openForm(id) {
  146 + if (!this.${subJoinColumn.javaField}) {
  147 + this.#[[$modal]]#.msgError('请选择一个${table.classComment}');
  148 + return;
  149 + }
  150 + this.#[[$]]#refs["formRef"].open(id, this.${subJoinColumn.javaField});
  151 + },
  152 + /** 删除按钮操作 */
  153 + async handleDelete(row) {
  154 + const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
  155 + await this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField} + '"的数据项?');
  156 + try {
  157 + await ${simpleClassName}Api.delete${subSimpleClassName}(${primaryColumn.javaField});
  158 + await this.getList();
  159 + this.#[[$modal]]#.msgSuccess("删除成功");
  160 + } catch {}
  161 + },
  162 +#end
  163 + }
  164 + };
  165 +</script>
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm 0 → 100644
  1 +## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
  2 +## 1)inner 使用 list 不分页,erp 使用 page 分页
  3 +## 2)erp 支持单个子表的新增、修改、删除,inner 不支持
  4 +#parse("codegen/vue/views/components/list_sub_erp.vue.vm")
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/form.vue.vm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + #foreach($column in $columns)
  7 + #if ($column.createOperation || $column.updateOperation)
  8 + #set ($dictType = $column.dictType)
  9 + #set ($javaField = $column.javaField)
  10 + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  11 + #set ($comment = $column.columnComment)
  12 + #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
  13 + <el-form-item label="${comment}" prop="${javaField}">
  14 + <TreeSelect
  15 + v-model="formData.${javaField}"
  16 + :options="${classNameVar}Tree"
  17 + :normalizer="normalizer"
  18 + placeholder="请选择${comment}"
  19 + />
  20 + </el-form-item>
  21 + #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
  22 + <el-form-item label="${comment}" prop="${javaField}">
  23 + <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
  24 + </el-form-item>
  25 + #elseif($column.htmlType == "imageUpload")## 图片上传
  26 + #set ($hasImageUploadColumn = true)
  27 + <el-form-item label="${comment}">
  28 + <ImageUpload v-model="formData.${javaField}"/>
  29 + </el-form-item>
  30 + #elseif($column.htmlType == "fileUpload")## 文件上传
  31 + #set ($hasFileUploadColumn = true)
  32 + <el-form-item label="${comment}">
  33 + <FileUpload v-model="formData.${javaField}"/>
  34 + </el-form-item>
  35 + #elseif($column.htmlType == "editor")## 文本编辑器
  36 + #set ($hasEditorColumn = true)
  37 + <el-form-item label="${comment}">
  38 + <Editor v-model="formData.${javaField}" :min-height="192"/>
  39 + </el-form-item>
  40 + #elseif($column.htmlType == "select")## 下拉框
  41 + <el-form-item label="${comment}" prop="${javaField}">
  42 + <el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
  43 + #if ("" != $dictType)## 有数据字典
  44 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  45 + :key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
  46 + #else##没数据字典
  47 + <el-option label="请选择字典生成" value="" />
  48 + #end
  49 + </el-select>
  50 + </el-form-item>
  51 + #elseif($column.htmlType == "checkbox")## 多选框
  52 + <el-form-item label="${comment}" prop="${javaField}">
  53 + <el-checkbox-group v-model="formData.${javaField}">
  54 + #if ("" != $dictType)## 有数据字典
  55 + <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  56 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
  57 + #else##没数据字典
  58 + <el-checkbox>请选择字典生成</el-checkbox>
  59 + #end
  60 + </el-checkbox-group>
  61 + </el-form-item>
  62 + #elseif($column.htmlType == "radio")## 单选框
  63 + <el-form-item label="${comment}" prop="${javaField}">
  64 + <el-radio-group v-model="formData.${javaField}">
  65 + #if ("" != $dictType)## 有数据字典
  66 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
  67 + :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
  68 + #else:label="dict.value"#end>{{dict.label}}</el-radio>
  69 + #else##没数据字典
  70 + <el-radio label="1">请选择字典生成</el-radio>
  71 + #end
  72 + </el-radio-group>
  73 + </el-form-item>
  74 + #elseif($column.htmlType == "datetime")## 时间框
  75 + <el-form-item label="${comment}" prop="${javaField}">
  76 + <el-date-picker clearable v-model="formData.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
  77 + </el-form-item>
  78 + #elseif($column.htmlType == "textarea")## 文本框
  79 + <el-form-item label="${comment}" prop="${javaField}">
  80 + <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入内容" />
  81 + </el-form-item>
  82 + #end
  83 + #end
  84 + #end
  85 + </el-form>
  86 + ## 特殊:主子表专属逻辑
  87 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  88 + <!-- 子表的表单 -->
  89 + <el-tabs v-model="subTabsName">
  90 + #foreach ($subTable in $subTables)
  91 + #set ($index = $foreach.count - 1)
  92 + #set ($subClassNameVar = $subClassNameVars.get($index))
  93 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  94 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  95 + <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
  96 + <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />
  97 + </el-tab-pane>
  98 + #end
  99 + </el-tabs>
  100 + #end
  101 + <div slot="footer" class="dialog-footer">
  102 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  103 + <el-button @click="dialogVisible = false">取 消</el-button>
  104 + </div>
  105 + </el-dialog>
  106 + </div>
  107 +</template>
  108 +
  109 +<script>
  110 + import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  111 + #if ($hasImageUploadColumn)
  112 + import ImageUpload from '@/components/ImageUpload';
  113 + #end
  114 + #if ($hasFileUploadColumn)
  115 + import FileUpload from '@/components/FileUpload';
  116 + #end
  117 + #if ($hasEditorColumn)
  118 + import Editor from '@/components/Editor';
  119 + #end
  120 + ## 特殊:树表专属逻辑
  121 + #if ( $table.templateType == 2 )
  122 + import TreeSelect from "@riophae/vue-treeselect";
  123 + import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  124 + #end
  125 + ## 特殊:主子表专属逻辑
  126 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  127 + #foreach ($subSimpleClassName in $subSimpleClassNames)
  128 + import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'
  129 + #end
  130 + #end
  131 + export default {
  132 + name: "${simpleClassName}Form",
  133 + components: {
  134 + #if ($hasImageUploadColumn)
  135 + ImageUpload,
  136 + #end
  137 + #if ($hasFileUploadColumn)
  138 + FileUpload,
  139 + #end
  140 + #if ($hasEditorColumn)
  141 + Editor,
  142 + #end
  143 + ## 特殊:树表专属逻辑
  144 + #if ( $table.templateType == 2 )
  145 + TreeSelect,
  146 + #end
  147 + ## 特殊:主子表专属逻辑
  148 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  149 + #foreach ($subSimpleClassName in $subSimpleClassNames)
  150 + ${subSimpleClassName}Form,
  151 + #end
  152 + #end
  153 + },
  154 + data() {
  155 + return {
  156 + // 弹出层标题
  157 + dialogTitle: "",
  158 + // 是否显示弹出层
  159 + dialogVisible: false,
  160 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  161 + formLoading: false,
  162 + // 表单参数
  163 + formData: {
  164 + #foreach ($column in $columns)
  165 + #if ($column.createOperation || $column.updateOperation)
  166 + #if ($column.htmlType == "checkbox")
  167 + $column.javaField: [],
  168 + #else
  169 + $column.javaField: undefined,
  170 + #end
  171 + #end
  172 + #end
  173 + },
  174 + // 表单校验
  175 + formRules: {
  176 + #foreach ($column in $columns)
  177 + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
  178 + #set($comment=$column.columnComment)
  179 + $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
  180 + #end
  181 + #end
  182 + },
  183 + ## 特殊:树表专属逻辑
  184 + #if ( $table.templateType == 2 )
  185 + ${classNameVar}Tree: [], // 树形结构
  186 + #end
  187 + ## 特殊:主子表专属逻辑
  188 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  189 + #if ( $subTables && $subTables.size() > 0 )
  190 + /** 子表的表单 */
  191 + subTabsName: '$subClassNameVars.get(0)'
  192 + #end
  193 + #end
  194 + };
  195 + },
  196 + methods: {
  197 + /** 打开弹窗 */
  198 + async open(id) {
  199 + this.dialogVisible = true;
  200 + this.reset();
  201 + // 修改时,设置数据
  202 + if (id) {
  203 + this.formLoading = true;
  204 + try {
  205 + const res = await ${simpleClassName}Api.get${simpleClassName}(id);
  206 + this.formData = res.data;
  207 + this.title = "修改${table.classComment}";
  208 + } finally {
  209 + this.formLoading = false;
  210 + }
  211 + }
  212 + this.title = "新增${table.classComment}";
  213 + ## 特殊:树表专属逻辑
  214 + #if ( $table.templateType == 2 )
  215 + this.get${simpleClassName}Tree();
  216 + #end
  217 + },
  218 + /** 提交按钮 */
  219 + async submitForm() {
  220 + // 校验主表
  221 + await this.$refs["formRef"].validate();
  222 + ## 特殊:主子表专属逻辑
  223 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  224 + #if ( $subTables && $subTables.size() > 0 )
  225 + // 校验子表
  226 + #foreach ($subTable in $subTables)
  227 + #set ($index = $foreach.count - 1)
  228 + #set ($subClassNameVar = $subClassNameVars.get($index))
  229 + try {
  230 + ## 代码生成后会替换为正确的 refs
  231 + await this.refs['${subClassNameVar}FormRef'].validate();
  232 + } catch (e) {
  233 + this.subTabsName = '${subClassNameVar}';
  234 + return;
  235 + }
  236 + #end
  237 + #end
  238 + #end
  239 + this.formLoading = true;
  240 + try {
  241 + const data = this.formData;
  242 + ## 特殊:主子表专属逻辑
  243 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  244 + #if ( $subTables && $subTables.size() > 0 )
  245 + // 拼接子表的数据
  246 + #foreach ($subTable in $subTables)
  247 + #set ($index = $foreach.count - 1)
  248 + #set ($subClassNameVar = $subClassNameVars.get($index))
  249 + data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = this.refs['${subClassNameVar}FormRef'].getData();
  250 + #end
  251 + #end
  252 + #end
  253 + // 修改的提交
  254 + if (data.${primaryColumn.javaField}) {
  255 + await ${simpleClassName}Api.update${simpleClassName}(data);
  256 + this.#[[$modal]]#.msgSuccess("修改成功");
  257 + this.dialogVisible = false;
  258 + this.#[[$]]#emit('success');
  259 + return;
  260 + }
  261 + // 添加的提交
  262 + await ${simpleClassName}Api.create${simpleClassName}(data);
  263 + this.#[[$modal]]#.msgSuccess("新增成功");
  264 + this.dialogVisible = false;
  265 + this.#[[$]]#emit('success');
  266 + }finally {
  267 + this.formLoading = false;
  268 + }
  269 + },
  270 + ## 特殊:树表专属逻辑
  271 + #if ( $table.templateType == 2 )
  272 + /** 获得${table.classComment}树 */
  273 + async get${simpleClassName}Tree() {
  274 + this.${classNameVar}Tree = [];
  275 + const res = await ${simpleClassName}Api.get${simpleClassName}List();
  276 + const root = { id: 0, name: '顶级${table.classComment}', children: [] };
  277 + root.children = this.handleTree(res.data, 'id', '${treeParentColumn.javaField}')
  278 + this.${classNameVar}Tree.push(root)
  279 + },
  280 + #end
  281 + ## 特殊:树表专属逻辑
  282 + #if ( $table.templateType == 2 )
  283 + /** 转换${table.classComment}数据结构 */
  284 + normalizer(node) {
  285 + if (node.children && !node.children.length) {
  286 + delete node.children;
  287 + }
  288 + #if ($treeNameColumn.javaField == "name")
  289 + return {
  290 + id: node.id,
  291 + label: node.name,
  292 + children: node.children
  293 + };
  294 + #else
  295 + return {
  296 + id: node.id,
  297 + label: node['$treeNameColumn.javaField'],
  298 + children: node.children
  299 + };
  300 + #end
  301 + },
  302 + #end
  303 + /** 表单重置 */
  304 + reset() {
  305 + this.formData = {
  306 + #foreach ($column in $columns)
  307 + #if ($column.createOperation || $column.updateOperation)
  308 + #if ($column.htmlType == "checkbox")
  309 + $column.javaField: [],
  310 + #else
  311 + $column.javaField: undefined,
  312 + #end
  313 + #end
  314 + #end
  315 + };
  316 + this.resetForm("formRef");
  317 + }
  318 + }
  319 + };
  320 +</script>
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm
@@ -47,18 +47,68 @@ @@ -47,18 +47,68 @@
47 <!-- 操作工具栏 --> 47 <!-- 操作工具栏 -->
48 <el-row :gutter="10" class="mb8"> 48 <el-row :gutter="10" class="mb8">
49 <el-col :span="1.5"> 49 <el-col :span="1.5">
50 - <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" 50 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
51 v-hasPermi="['${permissionPrefix}:create']">新增</el-button> 51 v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
52 </el-col> 52 </el-col>
53 <el-col :span="1.5"> 53 <el-col :span="1.5">
54 <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading" 54 <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
55 v-hasPermi="['${permissionPrefix}:export']">导出</el-button> 55 v-hasPermi="['${permissionPrefix}:export']">导出</el-button>
56 </el-col> 56 </el-col>
  57 + ## 特殊:树表专属逻辑
  58 + #if ( $table.templateType == 2 )
  59 + <el-col :span="1.5">
  60 + <el-button type="danger" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">
  61 + 展开/折叠
  62 + </el-button>
  63 + </el-col>
  64 + #end
57 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> 65 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
58 </el-row> 66 </el-row>
59 67
60 - <!-- 列表 -->  
61 - <el-table v-loading="loading" :data="list"> 68 + ## 特殊:主子表专属逻辑
  69 + #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
  70 + <el-table
  71 + v-loading="loading"
  72 + :data="list"
  73 + :stripe="true"
  74 + :highlight-current-row="true"
  75 + :show-overflow-tooltip="true"
  76 + @current-change="handleCurrentChange"
  77 + >
  78 + ## 特殊:树表专属逻辑
  79 + #elseif ( $table.templateType == 2 )
  80 + <el-table
  81 + v-loading="loading"
  82 + :data="list"
  83 + :stripe="true"
  84 + :show-overflow-tooltip="true"
  85 + v-if="refreshTable"
  86 + row-key="id"
  87 + :default-expand-all="isExpandAll"
  88 + :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
  89 + >
  90 + #else
  91 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  92 + #end
  93 + ## 特殊:主子表专属逻辑
  94 + #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
  95 + <!-- 子表的列表 -->
  96 + <el-table-column type="expand">
  97 + <template #default="scope">
  98 + <el-tabs value="$subClassNameVars.get(0)">
  99 + #foreach ($subTable in $subTables)
  100 + #set ($index = $foreach.count - 1)
  101 + #set ($subClassNameVar = $subClassNameVars.get($index))
  102 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  103 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  104 + <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
  105 + <${subSimpleClassName}List :${subJoinColumn_strikeCase}="scope.row.id" />
  106 + </el-tab-pane>
  107 + #end
  108 + </el-tabs>
  109 + </template>
  110 + </el-table-column>
  111 + #end
62 #foreach($column in $columns) 112 #foreach($column in $columns)
63 #if ($column.listOperationResult) 113 #if ($column.listOperationResult)
64 #set ($dictType=$column.dictType) 114 #set ($dictType=$column.dictType)
@@ -84,102 +134,42 @@ @@ -84,102 +134,42 @@
84 #end 134 #end
85 <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> 135 <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
86 <template v-slot="scope"> 136 <template v-slot="scope">
87 - <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" 137 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.${primaryColumn.javaField})"
88 v-hasPermi="['${permissionPrefix}:update']">修改</el-button> 138 v-hasPermi="['${permissionPrefix}:update']">修改</el-button>
89 <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" 139 <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
90 v-hasPermi="['${permissionPrefix}:delete']">删除</el-button> 140 v-hasPermi="['${permissionPrefix}:delete']">删除</el-button>
91 </template> 141 </template>
92 </el-table-column> 142 </el-table-column>
93 </el-table> 143 </el-table>
  144 +## 特殊:树表专属逻辑(树不需要分页)
  145 +#if ( $table.templateType != 2 )
94 <!-- 分页组件 --> 146 <!-- 分页组件 -->
95 <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 147 <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
96 @pagination="getList"/> 148 @pagination="getList"/>
97 - 149 +#end
98 <!-- 对话框(添加 / 修改) --> 150 <!-- 对话框(添加 / 修改) -->
99 - <el-dialog :title="title" :visible.sync="open" width="500px" v-dialogDrag append-to-body>  
100 - <el-form ref="form" :model="form" :rules="rules" label-width="80px">  
101 -#foreach($column in $columns)  
102 -#if ($column.createOperation || $column.updateOperation)  
103 - #set ($dictType = $column.dictType)  
104 - #set ($javaField = $column.javaField)  
105 - #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})  
106 - #set ($comment = $column.columnComment)  
107 -#if ($column.htmlType == "input")  
108 - #if (!$column.primaryKey)## 忽略主键,不用在表单里  
109 - <el-form-item label="${comment}" prop="${javaField}">  
110 - <el-input v-model="form.${javaField}" placeholder="请输入${comment}" />  
111 - </el-form-item> 151 + <${simpleClassName}Form ref="formRef" @success="getList" />
  152 + ## 特殊:主子表专属逻辑
  153 + #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
  154 + <!-- 子表的列表 -->
  155 + <el-tabs v-model="subTabsName">
  156 + #foreach ($subTable in $subTables)
  157 + #set ($index = $foreach.count - 1)
  158 + #set ($subClassNameVar = $subClassNameVars.get($index))
  159 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  160 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  161 + <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
  162 + <${subSimpleClassName}List v-if="currentRow.id" :${subJoinColumn_strikeCase}="currentRow.id" />
  163 + </el-tab-pane>
  164 + #end
  165 + </el-tabs>
112 #end 166 #end
113 -#elseif($column.htmlType == "imageUpload")## 图片上传  
114 - #set ($hasImageUploadColumn = true)  
115 - <el-form-item label="${comment}">  
116 - <imageUpload v-model="form.${javaField}"/>  
117 - </el-form-item>  
118 -#elseif($column.htmlType == "fileUpload")## 文件上传  
119 - #set ($hasFileUploadColumn = true)  
120 - <el-form-item label="${comment}">  
121 - <fileUpload v-model="form.${javaField}"/>  
122 - </el-form-item>  
123 -#elseif($column.htmlType == "editor")## 文本编辑器  
124 - #set ($hasEditorColumn = true)  
125 - <el-form-item label="${comment}">  
126 - <editor v-model="form.${javaField}" :min-height="192"/>  
127 - </el-form-item>  
128 -#elseif($column.htmlType == "select")## 下拉框  
129 - <el-form-item label="${comment}" prop="${javaField}">  
130 - <el-select v-model="form.${javaField}" placeholder="请选择${comment}">  
131 - #if ("" != $dictType)## 有数据字典  
132 - <el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"  
133 - :key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />  
134 - #else##没数据字典  
135 - <el-option label="请选择字典生成" value="" />  
136 - #end  
137 - </el-select>  
138 - </el-form-item>  
139 -#elseif($column.htmlType == "checkbox")## 多选框  
140 - <el-form-item label="${comment}" prop="${javaField}">  
141 - <el-checkbox-group v-model="form.${javaField}">  
142 - #if ("" != $dictType)## 有数据字典  
143 - <el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"  
144 - :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>  
145 - #else##没数据字典  
146 - <el-checkbox>请选择字典生成</el-checkbox>  
147 - #end  
148 - </el-checkbox-group>  
149 - </el-form-item>  
150 -#elseif($column.htmlType == "radio")## 单选框  
151 - <el-form-item label="${comment}" prop="${javaField}">  
152 - <el-radio-group v-model="form.${javaField}">  
153 - #if ("" != $dictType)## 有数据字典  
154 - <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"  
155 - :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-radio>  
156 - #else##没数据字典  
157 - <el-radio label="1">请选择字典生成</el-radio>  
158 - #end  
159 - </el-radio-group>  
160 - </el-form-item>  
161 -#elseif($column.htmlType == "datetime")## 时间框  
162 - <el-form-item label="${comment}" prop="${javaField}">  
163 - <el-date-picker clearable v-model="form.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />  
164 - </el-form-item>  
165 -#elseif($column.htmlType == "textarea")## 文本框  
166 - <el-form-item label="${comment}" prop="${javaField}">  
167 - <el-input v-model="form.${javaField}" type="textarea" placeholder="请输入内容" />  
168 - </el-form-item>  
169 -#end  
170 -#end  
171 -#end  
172 - </el-form>  
173 - <div slot="footer" class="dialog-footer">  
174 - <el-button type="primary" @click="submitForm">确 定</el-button>  
175 - <el-button @click="cancel">取 消</el-button>  
176 - </div>  
177 - </el-dialog>  
178 </div> 167 </div>
179 </template> 168 </template>
180 169
181 <script> 170 <script>
182 -import { create${simpleClassName}, update${simpleClassName}, delete${simpleClassName}, get${simpleClassName}, get${simpleClassName}Page, export${simpleClassName}Excel } from "@/api/${table.moduleName}/${classNameVar}"; 171 +import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';
  172 +import ${simpleClassName}Form from './${simpleClassName}Form.vue';
183 #if ($hasImageUploadColumn) 173 #if ($hasImageUploadColumn)
184 import ImageUpload from '@/components/ImageUpload'; 174 import ImageUpload from '@/components/ImageUpload';
185 #end 175 #end
@@ -189,10 +179,26 @@ import FileUpload from &#39;@/components/FileUpload&#39;; @@ -189,10 +179,26 @@ import FileUpload from &#39;@/components/FileUpload&#39;;
189 #if ($hasEditorColumn) 179 #if ($hasEditorColumn)
190 import Editor from '@/components/Editor'; 180 import Editor from '@/components/Editor';
191 #end 181 #end
192 - 182 +## 特殊:主子表专属逻辑
  183 +#if ( $table.templateType != 10 )
  184 +#if ( $subTables && $subTables.size() > 0 )
  185 + #foreach ($subSimpleClassName in $subSimpleClassNames)
  186 + import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'
  187 + #end
  188 +#end
  189 +#end
193 export default { 190 export default {
194 name: "${simpleClassName}", 191 name: "${simpleClassName}",
195 components: { 192 components: {
  193 + ${simpleClassName}Form,
  194 +## 特殊:主子表专属逻辑
  195 +#if ( $table.templateType != 10 )
  196 +#if ( $subTables && $subTables.size() > 0 )
  197 + #foreach ($subSimpleClassName in $subSimpleClassNames)
  198 + ${subSimpleClassName}List,
  199 + #end
  200 +#end
  201 +#end
196 #if ($hasImageUploadColumn) 202 #if ($hasImageUploadColumn)
197 ImageUpload, 203 ImageUpload,
198 #end 204 #end
@@ -211,40 +217,44 @@ export default { @@ -211,40 +217,44 @@ export default {
211 exportLoading: false, 217 exportLoading: false,
212 // 显示搜索条件 218 // 显示搜索条件
213 showSearch: true, 219 showSearch: true,
214 - // 总条数  
215 - total: 0, 220 + ## 特殊:树表专属逻辑(树不需要分页接口)
  221 + #if ( $table.templateType != 2 )
  222 + // 总条数
  223 + total: 0,
  224 + #end
216 // ${table.classComment}列表 225 // ${table.classComment}列表
217 list: [], 226 list: [],
218 - // 弹出层标题  
219 - title: "",  
220 - // 是否显示弹出层  
221 - open: false, 227 + // 是否展开,默认全部展开
  228 + isExpandAll: true,
  229 + // 重新渲染表格状态
  230 + refreshTable: true,
  231 + // 选中行
  232 + currentRow: {},
222 // 查询参数 233 // 查询参数
223 queryParams: { 234 queryParams: {
224 - pageNo: 1,  
225 - pageSize: 10, 235 + ## 特殊:树表专属逻辑(树不需要分页接口)
  236 + #if ( $table.templateType != 2 )
  237 + pageNo: 1,
  238 + pageSize: 10,
  239 + #end
226 #foreach ($column in $columns) 240 #foreach ($column in $columns)
227 #if ($column.listOperation) 241 #if ($column.listOperation)
228 #if ($column.listOperationCondition != 'BETWEEN') 242 #if ($column.listOperationCondition != 'BETWEEN')
229 $column.javaField: null, 243 $column.javaField: null,
230 #end 244 #end
231 - #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN") 245 + #if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
232 $column.javaField: [], 246 $column.javaField: [],
233 #end 247 #end
234 #end 248 #end
235 #end 249 #end
236 }, 250 },
237 - // 表单参数  
238 - form: {},  
239 - // 表单校验  
240 - rules: {  
241 - #foreach ($column in $columns)  
242 - #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键  
243 - #set($comment=$column.columnComment)  
244 - $column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],  
245 - #end  
246 - #end  
247 - } 251 + ## 特殊:主子表专属逻辑-erp
  252 + #if ( $table.templateType == 11)
  253 + #if ( $subTables && $subTables.size() > 0 )
  254 + /** 子表的列表 */
  255 + subTabsName: '$subClassNameVars.get(0)'
  256 + #end
  257 + #end
248 }; 258 };
249 }, 259 },
250 created() { 260 created() {
@@ -252,34 +262,21 @@ export default { @@ -252,34 +262,21 @@ export default {
252 }, 262 },
253 methods: { 263 methods: {
254 /** 查询列表 */ 264 /** 查询列表 */
255 - getList() { 265 + async getList() {
  266 + try {
256 this.loading = true; 267 this.loading = true;
257 - // 执行查询  
258 - get${simpleClassName}Page(this.queryParams).then(response => {  
259 - this.list = response.data.list;  
260 - this.total = response.data.total; 268 + ## 特殊:树表专属逻辑(树不需要分页接口)
  269 + #if ( $table.templateType == 2 )
  270 + const res = await ${simpleClassName}Api.get${simpleClassName}List(this.queryParams);
  271 + this.list = this.handleTree(res.data, 'id', '${treeParentColumn.javaField}');
  272 + #else
  273 + const res = await ${simpleClassName}Api.get${simpleClassName}Page(this.queryParams);
  274 + this.list = res.data.list;
  275 + this.total = res.data.total;
  276 + #end
  277 + } finally {
261 this.loading = false; 278 this.loading = false;
262 - });  
263 - },  
264 - /** 取消按钮 */  
265 - cancel() {  
266 - this.open = false;  
267 - this.reset();  
268 - },  
269 - /** 表单重置 */  
270 - reset() {  
271 - this.form = {  
272 - #foreach ($column in $columns)  
273 - #if ($column.createOperation || $column.updateOperation)  
274 - #if ($column.htmlType == "checkbox")  
275 - $column.javaField: [],  
276 - #else  
277 - $column.javaField: undefined,  
278 - #end  
279 - #end  
280 - #end  
281 - };  
282 - this.resetForm("form"); 279 + }
283 }, 280 },
284 /** 搜索按钮操作 */ 281 /** 搜索按钮操作 */
285 handleQuery() { 282 handleQuery() {
@@ -291,79 +288,54 @@ export default { @@ -291,79 +288,54 @@ export default {
291 this.resetForm("queryForm"); 288 this.resetForm("queryForm");
292 this.handleQuery(); 289 this.handleQuery();
293 }, 290 },
294 - /** 新增按钮操作 */  
295 - handleAdd() {  
296 - this.reset();  
297 - this.open = true;  
298 - this.title = "添加${table.classComment}";  
299 - },  
300 - /** 修改按钮操作 */  
301 - handleUpdate(row) {  
302 - this.reset();  
303 - const ${primaryColumn.javaField} = row.${primaryColumn.javaField};  
304 - get${simpleClassName}(${primaryColumn.javaField}).then(response => {  
305 - this.form = response.data;  
306 - #foreach ($column in $columns)  
307 - #if($column.htmlType == "checkbox")## checkbox 特殊处理  
308 - this.form.$column.javaField = this.form.${column.javaField}.split(",");  
309 - #end  
310 - #end  
311 - this.open = true;  
312 - this.title = "修改${table.classComment}";  
313 - });  
314 - },  
315 - /** 提交按钮 */  
316 - submitForm() {  
317 - this.#[[$]]#refs["form"].validate(valid => {  
318 - if (!valid) {  
319 - return;  
320 - }  
321 - #foreach ($column in $columns)  
322 - #if($column.htmlType == "checkbox")  
323 - this.form.$column.javaField = this.form.${column.javaField}.join(",");  
324 - #end  
325 - #end  
326 - // 修改的提交  
327 - if (this.form.${primaryColumn.javaField} != null) {  
328 - update${simpleClassName}(this.form).then(response => {  
329 - this.#[[$modal]]#.msgSuccess("修改成功");  
330 - this.open = false;  
331 - this.getList();  
332 - });  
333 - return;  
334 - }  
335 - // 添加的提交  
336 - create${simpleClassName}(this.form).then(response => {  
337 - this.#[[$modal]]#.msgSuccess("新增成功");  
338 - this.open = false;  
339 - this.getList();  
340 - });  
341 - }); 291 + /** 添加/修改操作 */
  292 + openForm(id) {
  293 + this.#[[$]]#refs["formRef"].open(id);
342 }, 294 },
343 /** 删除按钮操作 */ 295 /** 删除按钮操作 */
344 - handleDelete(row) { 296 + async handleDelete(row) {
345 const ${primaryColumn.javaField} = row.${primaryColumn.javaField}; 297 const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
346 - this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField} + '"的数据项?').then(function() {  
347 - return delete${simpleClassName}(${primaryColumn.javaField});  
348 - }).then(() => {  
349 - this.getList();  
350 - this.#[[$modal]]#.msgSuccess("删除成功");  
351 - }).catch(() => {}); 298 + await this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField} + '"的数据项?')
  299 + try {
  300 + await ${simpleClassName}Api.delete${simpleClassName}(${primaryColumn.javaField});
  301 + this.getList();
  302 + this.#[[$modal]]#.msgSuccess("删除成功");
  303 + } catch {}
352 }, 304 },
353 /** 导出按钮操作 */ 305 /** 导出按钮操作 */
354 - handleExport() {  
355 - // 处理查询参数  
356 - let params = {...this.queryParams};  
357 - params.pageNo = undefined;  
358 - params.pageSize = undefined;  
359 - this.#[[$modal]]#.confirm('是否确认导出所有${table.classComment}数据项?').then(() => {  
360 - this.exportLoading = true;  
361 - return export${simpleClassName}Excel(params);  
362 - }).then(response => {  
363 - this.#[[$]]#download.excel(response, '${table.classComment}.xls');  
364 - this.exportLoading = false;  
365 - }).catch(() => {});  
366 - } 306 + async handleExport() {
  307 + await this.#[[$modal]]#.confirm('是否确认导出所有${table.classComment}数据项?');
  308 + try {
  309 + this.exportLoading = true;
  310 + const res = await ${simpleClassName}Api.export${simpleClassName}Excel(this.queryParams);
  311 + this.#[[$]]#download.excel(res.data, '${table.classComment}.xls');
  312 + } catch {
  313 + } finally {
  314 + this.exportLoading = false;
  315 + }
  316 + },
  317 + ## 特殊:主子表专属逻辑
  318 + #if ( $table.templateType == 11 )
  319 + /** 选中行操作 */
  320 + handleCurrentChange(row) {
  321 + this.currentRow = row;
  322 + #if ( $subTables && $subTables.size() > 0 )
  323 + /** 子表的列表 */
  324 + this.subTabsName = '$subClassNameVars.get(0)';
  325 + #end
  326 + },
  327 + #end
  328 + ## 特殊:树表专属逻辑
  329 + #if ( $table.templateType == 2 )
  330 + /** 展开/折叠操作 */
  331 + toggleExpandAll() {
  332 + this.refreshTable = false
  333 + this.isExpandAll = !this.isExpandAll
  334 + this.$nextTick(function () {
  335 + this.refreshTable = true
  336 + })
  337 + }
  338 + #end
367 } 339 }
368 }; 340 };
369 </script> 341 </script>
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm
1 <template> 1 <template>
2 <Dialog :title="dialogTitle" v-model="dialogVisible"> 2 <Dialog :title="dialogTitle" v-model="dialogVisible">
3 <el-form 3 <el-form
4 - ref="formRef"  
5 - :model="formData"  
6 - :rules="formRules"  
7 - label-width="100px"  
8 - v-loading="formLoading" 4 + ref="formRef"
  5 + :model="formData"
  6 + :rules="formRules"
  7 + label-width="100px"
  8 + v-loading="formLoading"
9 > 9 >
10 -#foreach($column in $columns)  
11 - #if ($column.createOperation || $column.updateOperation)  
12 - #set ($dictType = $column.dictType)  
13 - #set ($javaField = $column.javaField)  
14 - #set ($javaType = $column.javaType)  
15 - #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})  
16 - #set ($comment = $column.columnComment)  
17 - #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法  
18 - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")  
19 - #set ($dictMethod = "getIntDictOptions")  
20 - #elseif ($javaType == "String")  
21 - #set ($dictMethod = "getStrDictOptions")  
22 - #elseif ($javaType == "Boolean")  
23 - #set ($dictMethod = "getBoolDictOptions")  
24 - #end  
25 - #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )  
26 - <el-form-item label="${comment}" prop="${javaField}">  
27 - <el-tree-select  
28 - v-model="formData.${javaField}"  
29 - :data="${classNameVar}Tree"  
30 - #if ($treeNameColumn.javaField == "name")  
31 - :props="defaultProps"  
32 - #else  
33 - :props="{...defaultProps, label: '$treeNameColumn.javaField'}"  
34 - #end  
35 - check-strictly  
36 - default-expand-all  
37 - placeholder="请选择${comment}"  
38 - />  
39 - </el-form-item>  
40 - #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里  
41 - <el-form-item label="${comment}" prop="${javaField}">  
42 - <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />  
43 - </el-form-item>  
44 - #elseif($column.htmlType == "imageUpload")## 图片上传  
45 - <el-form-item label="${comment}" prop="${javaField}">  
46 - <UploadImg v-model="formData.${javaField}" />  
47 - </el-form-item>  
48 - #elseif($column.htmlType == "fileUpload")## 文件上传  
49 - <el-form-item label="${comment}" prop="${javaField}">  
50 - <UploadFile v-model="formData.${javaField}" />  
51 - </el-form-item>  
52 - #elseif($column.htmlType == "editor")## 文本编辑器  
53 - <el-form-item label="${comment}" prop="${javaField}">  
54 - <Editor v-model="formData.${javaField}" height="150px" />  
55 - </el-form-item>  
56 - #elseif($column.htmlType == "select")## 下拉框  
57 - <el-form-item label="${comment}" prop="${javaField}">  
58 - <el-select v-model="formData.${javaField}" placeholder="请选择${comment}">  
59 - #if ("" != $dictType)## 有数据字典  
60 - <el-option  
61 - v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"  
62 - :key="dict.value"  
63 - :label="dict.label"  
64 - :value="dict.value"  
65 - />  
66 - #else##没数据字典  
67 - <el-option label="请选择字典生成" value="" /> 10 + #foreach($column in $columns)
  11 + #if ($column.createOperation || $column.updateOperation)
  12 + #set ($dictType = $column.dictType)
  13 + #set ($javaField = $column.javaField)
  14 + #set ($javaType = $column.javaType)
  15 + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  16 + #set ($comment = $column.columnComment)
  17 + #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
  18 + #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  19 + #set ($dictMethod = "getIntDictOptions")
  20 + #elseif ($javaType == "String")
  21 + #set ($dictMethod = "getStrDictOptions")
  22 + #elseif ($javaType == "Boolean")
  23 + #set ($dictMethod = "getBoolDictOptions")
68 #end 24 #end
69 - </el-select>  
70 - </el-form-item>  
71 - #elseif($column.htmlType == "checkbox")## 多选框  
72 - <el-form-item label="${comment}" prop="${javaField}">  
73 - <el-checkbox-group v-model="formData.${javaField}">  
74 - #if ("" != $dictType)## 有数据字典  
75 - <el-checkbox  
76 - v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"  
77 - :key="dict.value"  
78 - :label="dict.value"  
79 - >  
80 - {{ dict.label }}  
81 - </el-checkbox>  
82 - #else##没数据字典  
83 - <el-checkbox>请选择字典生成</el-checkbox> 25 + #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
  26 + <el-form-item label="${comment}" prop="${javaField}">
  27 + <el-tree-select
  28 + v-model="formData.${javaField}"
  29 + :data="${classNameVar}Tree"
  30 + #if ($treeNameColumn.javaField == "name")
  31 + :props="defaultProps"
  32 + #else
  33 + :props="{...defaultProps, label: '$treeNameColumn.javaField'}"
  34 + #end
  35 + check-strictly
  36 + default-expand-all
  37 + placeholder="请选择${comment}"
  38 + />
  39 + </el-form-item>
  40 + #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
  41 + <el-form-item label="${comment}" prop="${javaField}">
  42 + <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
  43 + </el-form-item>
  44 + #elseif($column.htmlType == "imageUpload")## 图片上传
  45 + <el-form-item label="${comment}" prop="${javaField}">
  46 + <UploadImg v-model="formData.${javaField}" />
  47 + </el-form-item>
  48 + #elseif($column.htmlType == "fileUpload")## 文件上传
  49 + <el-form-item label="${comment}" prop="${javaField}">
  50 + <UploadFile v-model="formData.${javaField}" />
  51 + </el-form-item>
  52 + #elseif($column.htmlType == "editor")## 文本编辑器
  53 + <el-form-item label="${comment}" prop="${javaField}">
  54 + <Editor v-model="formData.${javaField}" height="150px" />
  55 + </el-form-item>
  56 + #elseif($column.htmlType == "select")## 下拉框
  57 + <el-form-item label="${comment}" prop="${javaField}">
  58 + <el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
  59 + #if ("" != $dictType)## 有数据字典
  60 + <el-option
  61 + v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
  62 + :key="dict.value"
  63 + :label="dict.label"
  64 + :value="dict.value"
  65 + />
  66 + #else##没数据字典
  67 + <el-option label="请选择字典生成" value="" />
  68 + #end
  69 + </el-select>
  70 + </el-form-item>
  71 + #elseif($column.htmlType == "checkbox")## 多选框
  72 + <el-form-item label="${comment}" prop="${javaField}">
  73 + <el-checkbox-group v-model="formData.${javaField}">
  74 + #if ("" != $dictType)## 有数据字典
  75 + <el-checkbox
  76 + v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
  77 + :key="dict.value"
  78 + :label="dict.value"
  79 + >
  80 + {{ dict.label }}
  81 + </el-checkbox>
  82 + #else##没数据字典
  83 + <el-checkbox>请选择字典生成</el-checkbox>
  84 + #end
  85 + </el-checkbox-group>
  86 + </el-form-item>
  87 + #elseif($column.htmlType == "radio")## 单选框
  88 + <el-form-item label="${comment}" prop="${javaField}">
  89 + <el-radio-group v-model="formData.${javaField}">
  90 + #if ("" != $dictType)## 有数据字典
  91 + <el-radio
  92 + v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
  93 + :key="dict.value"
  94 + :label="dict.value"
  95 + >
  96 + {{ dict.label }}
  97 + </el-radio>
  98 + #else##没数据字典
  99 + <el-radio label="1">请选择字典生成</el-radio>
  100 + #end
  101 + </el-radio-group>
  102 + </el-form-item>
  103 + #elseif($column.htmlType == "datetime")## 时间框
  104 + <el-form-item label="${comment}" prop="${javaField}">
  105 + <el-date-picker
  106 + v-model="formData.${javaField}"
  107 + type="date"
  108 + value-format="x"
  109 + placeholder="选择${comment}"
  110 + />
  111 + </el-form-item>
  112 + #elseif($column.htmlType == "textarea")## 文本框
  113 + <el-form-item label="${comment}" prop="${javaField}">
  114 + <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
  115 + </el-form-item>
84 #end 116 #end
85 - </el-checkbox-group>  
86 - </el-form-item>  
87 - #elseif($column.htmlType == "radio")## 单选框  
88 - <el-form-item label="${comment}" prop="${javaField}">  
89 - <el-radio-group v-model="formData.${javaField}">  
90 - #if ("" != $dictType)## 有数据字典  
91 - <el-radio  
92 - v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"  
93 - :key="dict.value"  
94 - :label="dict.value"  
95 - >  
96 - {{ dict.label }}  
97 - </el-radio>  
98 - #else##没数据字典  
99 - <el-radio label="1">请选择字典生成</el-radio>  
100 - #end  
101 - </el-radio-group>  
102 - </el-form-item>  
103 - #elseif($column.htmlType == "datetime")## 时间框  
104 - <el-form-item label="${comment}" prop="${javaField}">  
105 - <el-date-picker  
106 - v-model="formData.${javaField}"  
107 - type="date"  
108 - value-format="x"  
109 - placeholder="选择${comment}"  
110 - />  
111 - </el-form-item>  
112 - #elseif($column.htmlType == "textarea")## 文本框  
113 - <el-form-item label="${comment}" prop="${javaField}">  
114 - <el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />  
115 - </el-form-item> 117 + #end
116 #end 118 #end
117 - #end  
118 -#end  
119 </el-form> 119 </el-form>
120 -## 特殊:主子表专属逻辑  
121 -#if ( $table.templateType == 10 || $table.templateType == 12 )  
122 - <!-- 子表的表单 -->  
123 - <el-tabs v-model="subTabsName">  
124 - #foreach ($subTable in $subTables)  
125 - #set ($index = $foreach.count - 1)  
126 - #set ($subClassNameVar = $subClassNameVars.get($index))  
127 - #set ($subSimpleClassName = $subSimpleClassNames.get($index))  
128 - #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))  
129 - <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">  
130 - <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />  
131 - </el-tab-pane>  
132 - #end  
133 - </el-tabs>  
134 -#end 120 + ## 特殊:主子表专属逻辑
  121 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  122 + <!-- 子表的表单 -->
  123 + <el-tabs v-model="subTabsName">
  124 + #foreach ($subTable in $subTables)
  125 + #set ($index = $foreach.count - 1)
  126 + #set ($subClassNameVar = $subClassNameVars.get($index))
  127 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  128 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  129 + <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
  130 + <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />
  131 + </el-tab-pane>
  132 + #end
  133 + </el-tabs>
  134 + #end
135 <template #footer> 135 <template #footer>
136 <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> 136 <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
137 <el-button @click="dialogVisible = false">取 消</el-button> 137 <el-button @click="dialogVisible = false">取 消</el-button>
@@ -139,160 +139,160 @@ @@ -139,160 +139,160 @@
139 </Dialog> 139 </Dialog>
140 </template> 140 </template>
141 <script setup lang="ts"> 141 <script setup lang="ts">
142 -import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'  
143 -import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'  
144 -## 特殊:树表专属逻辑  
145 -#if ( $table.templateType == 2 )  
146 -import { defaultProps, handleTree } from '@/utils/tree'  
147 -#end  
148 -## 特殊:主子表专属逻辑  
149 -#if ( $table.templateType == 10 || $table.templateType == 12 )  
150 -#foreach ($subSimpleClassName in $subSimpleClassNames)  
151 -import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'  
152 -#end  
153 -#end 142 + import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
  143 + import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  144 + ## 特殊:树表专属逻辑
  145 + #if ( $table.templateType == 2 )
  146 + import { defaultProps, handleTree } from '@/utils/tree'
  147 + #end
  148 + ## 特殊:主子表专属逻辑
  149 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  150 + #foreach ($subSimpleClassName in $subSimpleClassNames)
  151 + import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'
  152 + #end
  153 + #end
154 154
155 -const { t } = useI18n() // 国际化  
156 -const message = useMessage() // 消息弹窗 155 + const { t } = useI18n() // 国际化
  156 + const message = useMessage() // 消息弹窗
  157 +
  158 + const dialogVisible = ref(false) // 弹窗的是否展示
  159 + const dialogTitle = ref('') // 弹窗的标题
  160 + const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  161 + const formType = ref('') // 表单的类型:create - 新增;update - 修改
  162 + const formData = ref({
  163 + #foreach ($column in $columns)
  164 + #if ($column.createOperation || $column.updateOperation)
  165 + #if ($column.htmlType == "checkbox")
  166 + $column.javaField: [],
  167 + #else
  168 + $column.javaField: undefined,
  169 + #end
  170 + #end
  171 + #end
  172 + })
  173 + const formRules = reactive({
  174 + #foreach ($column in $columns)
  175 + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
  176 + #set($comment=$column.columnComment)
  177 + $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
  178 + #end
  179 + #end
  180 + })
  181 + const formRef = ref() // 表单 Ref
  182 + ## 特殊:树表专属逻辑
  183 + #if ( $table.templateType == 2 )
  184 + const ${classNameVar}Tree = ref() // 树形结构
  185 + #end
  186 + ## 特殊:主子表专属逻辑
  187 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  188 + #if ( $subTables && $subTables.size() > 0 )
157 189
158 -const dialogVisible = ref(false) // 弹窗的是否展示  
159 -const dialogTitle = ref('') // 弹窗的标题  
160 -const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用  
161 -const formType = ref('') // 表单的类型:create - 新增;update - 修改  
162 -const formData = ref({  
163 -#foreach ($column in $columns)  
164 - #if ($column.createOperation || $column.updateOperation)  
165 - #if ($column.htmlType == "checkbox")  
166 - $column.javaField: [],  
167 - #else  
168 - $column.javaField: undefined, 190 + /** 子表的表单 */
  191 + const subTabsName = ref('$subClassNameVars.get(0)')
  192 + #foreach ($subClassNameVar in $subClassNameVars)
  193 + const ${subClassNameVar}FormRef = ref()
  194 + #end
  195 + #end
169 #end 196 #end
170 - #end  
171 -#end  
172 -})  
173 -const formRules = reactive({  
174 -#foreach ($column in $columns)  
175 - #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键  
176 - #set($comment=$column.columnComment)  
177 - $column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],  
178 - #end  
179 -#end  
180 -})  
181 -const formRef = ref() // 表单 Ref  
182 -## 特殊:树表专属逻辑  
183 -#if ( $table.templateType == 2 )  
184 -const ${classNameVar}Tree = ref() // 树形结构  
185 -#end  
186 -## 特殊:主子表专属逻辑  
187 -#if ( $table.templateType == 10 || $table.templateType == 12 )  
188 -#if ( $subTables && $subTables.size() > 0 )  
189 197
190 -/** 子表的表单 */  
191 -const subTabsName = ref('$subClassNameVars.get(0)')  
192 -#foreach ($subClassNameVar in $subClassNameVars)  
193 -const ${subClassNameVar}FormRef = ref()  
194 -#end  
195 -#end  
196 -#end 198 + /** 打开弹窗 */
  199 + const open = async (type: string, id?: number) => {
  200 + dialogVisible.value = true
  201 + dialogTitle.value = t('action.' + type)
  202 + formType.value = type
  203 + resetForm()
  204 + // 修改时,设置数据
  205 + if (id) {
  206 + formLoading.value = true
  207 + try {
  208 + formData.value = await ${simpleClassName}Api.get${simpleClassName}(id)
  209 + } finally {
  210 + formLoading.value = false
  211 + }
  212 + }
  213 + ## 特殊:树表专属逻辑
  214 + #if ( $table.templateType == 2 )
  215 + await get${simpleClassName}Tree()
  216 + #end
  217 + }
  218 + defineExpose({ open }) // 提供 open 方法,用于打开弹窗
197 219
198 -/** 打开弹窗 */  
199 -const open = async (type: string, id?: number) => {  
200 - dialogVisible.value = true  
201 - dialogTitle.value = t('action.' + type)  
202 - formType.value = type  
203 - resetForm()  
204 - // 修改时,设置数据  
205 - if (id) { 220 + /** 提交表单 */
  221 + const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  222 + const submitForm = async () => {
  223 + // 校验表单
  224 + await formRef.value.validate()
  225 + ## 特殊:主子表专属逻辑
  226 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  227 + #if ( $subTables && $subTables.size() > 0 )
  228 + // 校验子表单
  229 + #foreach ($subTable in $subTables)
  230 + #set ($index = $foreach.count - 1)
  231 + #set ($subClassNameVar = $subClassNameVars.get($index))
  232 + try {
  233 + await ${subClassNameVar}FormRef.value.validate()
  234 + } catch (e) {
  235 + subTabsName.value = '${subClassNameVar}'
  236 + return
  237 + }
  238 + #end
  239 + #end
  240 + #end
  241 + // 提交请求
206 formLoading.value = true 242 formLoading.value = true
207 try { 243 try {
208 - formData.value = await ${simpleClassName}Api.get${simpleClassName}(id) 244 + const data = formData.value as unknown as ${simpleClassName}Api.${simpleClassName}VO
  245 + ## 特殊:主子表专属逻辑
  246 + #if ( $table.templateType == 10 || $table.templateType == 12 )
  247 + #if ( $subTables && $subTables.size() > 0 )
  248 + // 拼接子表的数据
  249 + #foreach ($subTable in $subTables)
  250 + #set ($index = $foreach.count - 1)
  251 + #set ($subClassNameVar = $subClassNameVars.get($index))
  252 + data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = ${subClassNameVar}FormRef.value.getData()
  253 + #end
  254 + #end
  255 + #end
  256 + if (formType.value === 'create') {
  257 + await ${simpleClassName}Api.create${simpleClassName}(data)
  258 + message.success(t('common.createSuccess'))
  259 + } else {
  260 + await ${simpleClassName}Api.update${simpleClassName}(data)
  261 + message.success(t('common.updateSuccess'))
  262 + }
  263 + dialogVisible.value = false
  264 + // 发送操作成功的事件
  265 + emit('success')
209 } finally { 266 } finally {
210 formLoading.value = false 267 formLoading.value = false
211 } 268 }
212 } 269 }
213 -## 特殊:树表专属逻辑  
214 -#if ( $table.templateType == 2 )  
215 - await get${simpleClassName}Tree()  
216 -#end  
217 -}  
218 -defineExpose({ open }) // 提供 open 方法,用于打开弹窗  
219 270
220 -/** 提交表单 */  
221 -const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调  
222 -const submitForm = async () => {  
223 - // 校验表单  
224 - await formRef.value.validate()  
225 -## 特殊:主子表专属逻辑  
226 -#if ( $table.templateType == 10 || $table.templateType == 12 )  
227 -#if ( $subTables && $subTables.size() > 0 )  
228 - // 校验子表单  
229 - #foreach ($subTable in $subTables)  
230 - #set ($index = $foreach.count - 1)  
231 - #set ($subClassNameVar = $subClassNameVars.get($index))  
232 - try {  
233 - await ${subClassNameVar}FormRef.value.validate()  
234 - } catch (e) {  
235 - subTabsName.value = '${subClassNameVar}'  
236 - return  
237 - }  
238 - #end  
239 -#end  
240 -#end  
241 - // 提交请求  
242 - formLoading.value = true  
243 - try {  
244 - const data = formData.value as unknown as ${simpleClassName}Api.${simpleClassName}VO  
245 -## 特殊:主子表专属逻辑  
246 -#if ( $table.templateType == 10 || $table.templateType == 12 )  
247 -#if ( $subTables && $subTables.size() > 0 )  
248 - // 拼接子表的数据  
249 - #foreach ($subTable in $subTables)  
250 - #set ($index = $foreach.count - 1)  
251 - #set ($subClassNameVar = $subClassNameVars.get($index))  
252 - data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = ${subClassNameVar}FormRef.value.getData()  
253 - #end  
254 -#end  
255 -#end  
256 - if (formType.value === 'create') {  
257 - await ${simpleClassName}Api.create${simpleClassName}(data)  
258 - message.success(t('common.createSuccess'))  
259 - } else {  
260 - await ${simpleClassName}Api.update${simpleClassName}(data)  
261 - message.success(t('common.updateSuccess')) 271 + /** 重置表单 */
  272 + const resetForm = () => {
  273 + formData.value = {
  274 + #foreach ($column in $columns)
  275 + #if ($column.createOperation || $column.updateOperation)
  276 + #if ($column.htmlType == "checkbox")
  277 + $column.javaField: [],
  278 + #else
  279 + $column.javaField: undefined,
  280 + #end
  281 + #end
  282 + #end
262 } 283 }
263 - dialogVisible.value = false  
264 - // 发送操作成功的事件  
265 - emit('success')  
266 - } finally {  
267 - formLoading.value = false 284 + formRef.value?.resetFields()
268 } 285 }
269 -} 286 + ## 特殊:树表专属逻辑
  287 + #if ( $table.templateType == 2 )
270 288
271 -/** 重置表单 */  
272 -const resetForm = () => {  
273 - formData.value = {  
274 -#foreach ($column in $columns)  
275 - #if ($column.createOperation || $column.updateOperation)  
276 - #if ($column.htmlType == "checkbox")  
277 - $column.javaField: [],  
278 - #else  
279 - $column.javaField: undefined, 289 + /** 获得${table.classComment}树 */
  290 + const get${simpleClassName}Tree = async () => {
  291 + ${classNameVar}Tree.value = []
  292 + const data = await ${simpleClassName}Api.get${simpleClassName}List()
  293 + const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }
  294 + root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')
  295 + ${classNameVar}Tree.value.push(root)
  296 + }
280 #end 297 #end
281 - #end  
282 -#end  
283 - }  
284 - formRef.value?.resetFields()  
285 -}  
286 -## 特殊:树表专属逻辑  
287 -#if ( $table.templateType == 2 )  
288 -  
289 -/** 获得${table.classComment}树 */  
290 -const get${simpleClassName}Tree = async () => {  
291 - ${classNameVar}Tree.value = []  
292 - const data = await ${simpleClassName}Api.get${simpleClassName}List()  
293 - const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }  
294 - root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')  
295 - ${classNameVar}Tree.value.push(root)  
296 -}  
297 -#end  
298 </script> 298 </script>
299 \ No newline at end of file 299 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
@@ -2,372 +2,375 @@ @@ -2,372 +2,375 @@
2 <ContentWrap> 2 <ContentWrap>
3 <!-- 搜索工作栏 --> 3 <!-- 搜索工作栏 -->
4 <el-form 4 <el-form
5 - class="-mb-15px"  
6 - :model="queryParams"  
7 - ref="queryFormRef"  
8 - :inline="true"  
9 - label-width="68px" 5 + class="-mb-15px"
  6 + :model="queryParams"
  7 + ref="queryFormRef"
  8 + :inline="true"
  9 + label-width="68px"
10 > 10 >
11 - #foreach($column in $columns)  
12 - #if ($column.listOperation)  
13 - #set ($dictType = $column.dictType)  
14 - #set ($javaField = $column.javaField)  
15 - #set ($javaType = $column.javaType)  
16 - #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})  
17 - #set ($comment = $column.columnComment)  
18 - #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法  
19 - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")  
20 - #set ($dictMethod = "getIntDictOptions")  
21 - #elseif ($javaType == "String")  
22 - #set ($dictMethod = "getStrDictOptions")  
23 - #elseif ($javaType == "Boolean")  
24 - #set ($dictMethod = "getBoolDictOptions")  
25 - #end  
26 - #if ($column.htmlType == "input")  
27 - <el-form-item label="${comment}" prop="${javaField}">  
28 - <el-input  
29 - v-model="queryParams.${javaField}"  
30 - placeholder="请输入${comment}"  
31 - clearable  
32 - @keyup.enter="handleQuery"  
33 - class="!w-240px"  
34 - />  
35 - </el-form-item>  
36 - #elseif ($column.htmlType == "select" || $column.htmlType == "radio")  
37 - <el-form-item label="${comment}" prop="${javaField}">  
38 - <el-select  
39 - v-model="queryParams.${javaField}"  
40 - placeholder="请选择${comment}"  
41 - clearable  
42 - class="!w-240px"  
43 - >  
44 - #if ("" != $dictType)## 设置了 dictType 数据字典的情况  
45 - <el-option  
46 - v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"  
47 - :key="dict.value"  
48 - :label="dict.label"  
49 - :value="dict.value"  
50 - />  
51 - #else## 未设置 dictType 数据字典的情况  
52 - <el-option label="请选择字典生成" value="" /> 11 + #foreach($column in $columns)
  12 + #if ($column.listOperation)
  13 + #set ($dictType = $column.dictType)
  14 + #set ($javaField = $column.javaField)
  15 + #set ($javaType = $column.javaType)
  16 + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  17 + #set ($comment = $column.columnComment)
  18 + #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
  19 + #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  20 + #set ($dictMethod = "getIntDictOptions")
  21 + #elseif ($javaType == "String")
  22 + #set ($dictMethod = "getStrDictOptions")
  23 + #elseif ($javaType == "Boolean")
  24 + #set ($dictMethod = "getBoolDictOptions")
53 #end 25 #end
54 - </el-select>  
55 - </el-form-item>  
56 - #elseif($column.htmlType == "datetime")  
57 - #if ($column.listOperationCondition != "BETWEEN")## 非范围  
58 - <el-form-item label="${comment}" prop="${javaField}">  
59 - <el-date-picker  
60 - v-model="queryParams.${javaField}"  
61 - value-format="YYYY-MM-DD"  
62 - type="date"  
63 - placeholder="选择${comment}"  
64 - clearable  
65 - class="!w-240px"  
66 - />  
67 - </el-form-item>  
68 - #else## 范围  
69 - <el-form-item label="${comment}" prop="${javaField}">  
70 - <el-date-picker  
71 - v-model="queryParams.${javaField}"  
72 - value-format="YYYY-MM-DD HH:mm:ss"  
73 - type="daterange"  
74 - start-placeholder="开始日期"  
75 - end-placeholder="结束日期"  
76 - :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"  
77 - class="!w-240px"  
78 - />  
79 - </el-form-item>  
80 - #end  
81 - #end  
82 - #end  
83 - #end 26 + #if ($column.htmlType == "input")
  27 + <el-form-item label="${comment}" prop="${javaField}">
  28 + <el-input
  29 + v-model="queryParams.${javaField}"
  30 + placeholder="请输入${comment}"
  31 + clearable
  32 + @keyup.enter="handleQuery"
  33 + class="!w-240px"
  34 + />
  35 + </el-form-item>
  36 + #elseif ($column.htmlType == "select" || $column.htmlType == "radio")
  37 + <el-form-item label="${comment}" prop="${javaField}">
  38 + <el-select
  39 + v-model="queryParams.${javaField}"
  40 + placeholder="请选择${comment}"
  41 + clearable
  42 + class="!w-240px"
  43 + >
  44 + #if ("" != $dictType)## 设置了 dictType 数据字典的情况
  45 + <el-option
  46 + v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
  47 + :key="dict.value"
  48 + :label="dict.label"
  49 + :value="dict.value"
  50 + />
  51 + #else## 未设置 dictType 数据字典的情况
  52 + <el-option label="请选择字典生成" value="" />
  53 + #end
  54 + </el-select>
  55 + </el-form-item>
  56 + #elseif($column.htmlType == "datetime")
  57 + #if ($column.listOperationCondition != "BETWEEN")## 非范围
  58 + <el-form-item label="${comment}" prop="${javaField}">
  59 + <el-date-picker
  60 + v-model="queryParams.${javaField}"
  61 + value-format="YYYY-MM-DD"
  62 + type="date"
  63 + placeholder="选择${comment}"
  64 + clearable
  65 + class="!w-240px"
  66 + />
  67 + </el-form-item>
  68 + #else## 范围
  69 + <el-form-item label="${comment}" prop="${javaField}">
  70 + <el-date-picker
  71 + v-model="queryParams.${javaField}"
  72 + value-format="YYYY-MM-DD HH:mm:ss"
  73 + type="daterange"
  74 + start-placeholder="开始日期"
  75 + end-placeholder="结束日期"
  76 + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
  77 + class="!w-240px"
  78 + />
  79 + </el-form-item>
  80 + #end
  81 + #end
  82 + #end
  83 + #end
84 <el-form-item> 84 <el-form-item>
85 <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> 85 <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
86 <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> 86 <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
87 <el-button 87 <el-button
88 - type="primary"  
89 - plain  
90 - @click="openForm('create')"  
91 - v-hasPermi="['${permissionPrefix}:create']" 88 + type="primary"
  89 + plain
  90 + @click="openForm('create')"
  91 + v-hasPermi="['${permissionPrefix}:create']"
92 > 92 >
93 <Icon icon="ep:plus" class="mr-5px" /> 新增 93 <Icon icon="ep:plus" class="mr-5px" /> 新增
94 </el-button> 94 </el-button>
95 <el-button 95 <el-button
96 - type="success"  
97 - plain  
98 - @click="handleExport"  
99 - :loading="exportLoading"  
100 - v-hasPermi="['${permissionPrefix}:export']" 96 + type="success"
  97 + plain
  98 + @click="handleExport"
  99 + :loading="exportLoading"
  100 + v-hasPermi="['${permissionPrefix}:export']"
101 > 101 >
102 <Icon icon="ep:download" class="mr-5px" /> 导出 102 <Icon icon="ep:download" class="mr-5px" /> 导出
103 </el-button> 103 </el-button>
104 -## 特殊:树表专属逻辑  
105 -#if ( $table.templateType == 2 )  
106 - <el-button type="danger" plain @click="toggleExpandAll">  
107 - <Icon icon="ep:sort" class="mr-5px" /> 展开/折叠  
108 - </el-button>  
109 -#end 104 + ## 特殊:树表专属逻辑
  105 + #if ( $table.templateType == 2 )
  106 + <el-button type="danger" plain @click="toggleExpandAll">
  107 + <Icon icon="ep:sort" class="mr-5px" /> 展开/折叠
  108 + </el-button>
  109 + #end
110 </el-form-item> 110 </el-form-item>
111 </el-form> 111 </el-form>
112 </ContentWrap> 112 </ContentWrap>
113 113
114 <!-- 列表 --> 114 <!-- 列表 -->
115 <ContentWrap> 115 <ContentWrap>
116 -## 特殊:主子表专属逻辑  
117 -#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )  
118 - <el-table  
119 - v-loading="loading"  
120 - :data="list"  
121 - :stripe="true"  
122 - :show-overflow-tooltip="true"  
123 - highlight-current-row  
124 - @current-change="handleCurrentChange"  
125 - >  
126 -## 特殊:树表专属逻辑  
127 -#elseif ( $table.templateType == 2 )  
128 - <el-table  
129 - v-loading="loading"  
130 - :data="list"  
131 - :stripe="true"  
132 - :show-overflow-tooltip="true"  
133 - row-key="id"  
134 - :default-expand-all="isExpandAll"  
135 - v-if="refreshTable"  
136 - >  
137 -#else  
138 - <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">  
139 -#end  
140 -## 特殊:主子表专属逻辑  
141 -#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )  
142 - <!-- 子表的列表 -->  
143 - <el-table-column type="expand">  
144 - <template #default="scope">  
145 - <el-tabs model-value="$subClassNameVars.get(0)">  
146 - #foreach ($subTable in $subTables)  
147 - #set ($index = $foreach.count - 1)  
148 - #set ($subClassNameVar = $subClassNameVars.get($index))  
149 - #set ($subSimpleClassName = $subSimpleClassNames.get($index))  
150 - #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))  
151 - <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">  
152 - <${subSimpleClassName}List :${subJoinColumn_strikeCase}="scope.row.id" />  
153 - </el-tab-pane>  
154 - #end  
155 - </el-tabs>  
156 - </template>  
157 - </el-table-column>  
158 -#end 116 + ## 特殊:主子表专属逻辑
  117 + #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
  118 + <el-table
  119 + v-loading="loading"
  120 + :data="list"
  121 + :stripe="true"
  122 + :show-overflow-tooltip="true"
  123 + highlight-current-row
  124 + @current-change="handleCurrentChange"
  125 + >
  126 + ## 特殊:树表专属逻辑
  127 + #elseif ( $table.templateType == 2 )
  128 + <el-table
  129 + v-loading="loading"
  130 + :data="list"
  131 + :stripe="true"
  132 + :show-overflow-tooltip="true"
  133 + row-key="id"
  134 + :default-expand-all="isExpandAll"
  135 + v-if="refreshTable"
  136 + >
  137 + #else
  138 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  139 + #end
  140 + ## 特殊:主子表专属逻辑
  141 + #if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
  142 + <!-- 子表的列表 -->
  143 + <el-table-column type="expand">
  144 + <template #default="scope">
  145 + <el-tabs model-value="$subClassNameVars.get(0)">
  146 + #foreach ($subTable in $subTables)
  147 + #set ($index = $foreach.count - 1)
  148 + #set ($subClassNameVar = $subClassNameVars.get($index))
  149 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  150 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  151 + <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
  152 + <${subSimpleClassName}List :${subJoinColumn_strikeCase}="scope.row.id" />
  153 + </el-tab-pane>
  154 + #end
  155 + </el-tabs>
  156 + </template>
  157 + </el-table-column>
  158 + #end
159 #foreach($column in $columns) 159 #foreach($column in $columns)
160 - #if ($column.listOperationResult)  
161 - #set ($dictType=$column.dictType)  
162 - #set ($javaField = $column.javaField)  
163 - #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})  
164 - #set ($comment=$column.columnComment)  
165 - #if ($column.javaType == "LocalDateTime")## 时间类型  
166 - <el-table-column  
167 - label="${comment}"  
168 - align="center"  
169 - prop="${javaField}"  
170 - :formatter="dateFormatter"  
171 - width="180px"  
172 - />  
173 - #elseif($column.dictType && "" != $column.dictType)## 数据字典  
174 - <el-table-column label="${comment}" align="center" prop="${javaField}">  
175 - <template #default="scope">  
176 - <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />  
177 - </template>  
178 - </el-table-column>  
179 - #else  
180 - <el-table-column label="${comment}" align="center" prop="${javaField}" />  
181 - #end 160 + #if ($column.listOperationResult)
  161 + #set ($dictType=$column.dictType)
  162 + #set ($javaField = $column.javaField)
  163 + #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
  164 + #set ($comment=$column.columnComment)
  165 + #if ($column.javaType == "LocalDateTime")## 时间类型
  166 + <el-table-column
  167 + label="${comment}"
  168 + align="center"
  169 + prop="${javaField}"
  170 + :formatter="dateFormatter"
  171 + width="180px"
  172 + />
  173 + #elseif($column.dictType && "" != $column.dictType)## 数据字典
  174 + <el-table-column label="${comment}" align="center" prop="${javaField}">
  175 + <template #default="scope">
  176 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
  177 + </template>
  178 + </el-table-column>
  179 + #else
  180 + <el-table-column label="${comment}" align="center" prop="${javaField}" />
  181 + #end
  182 + #end
182 #end 183 #end
183 - #end  
184 - <el-table-column label="操作" align="center">  
185 - <template #default="scope">  
186 - <el-button 184 + <el-table-column label="操作" align="center">
  185 + <template #default="scope">
  186 + <el-button
187 link 187 link
188 type="primary" 188 type="primary"
189 @click="openForm('update', scope.row.id)" 189 @click="openForm('update', scope.row.id)"
190 v-hasPermi="['${permissionPrefix}:update']" 190 v-hasPermi="['${permissionPrefix}:update']"
191 - >  
192 - 编辑  
193 - </el-button>  
194 - <el-button 191 + >
  192 + 编辑
  193 + </el-button>
  194 + <el-button
195 link 195 link
196 type="danger" 196 type="danger"
197 @click="handleDelete(scope.row.id)" 197 @click="handleDelete(scope.row.id)"
198 v-hasPermi="['${permissionPrefix}:delete']" 198 v-hasPermi="['${permissionPrefix}:delete']"
199 - >  
200 - 删除  
201 - </el-button>  
202 - </template>  
203 - </el-table-column>  
204 - </el-table> 199 + >
  200 + 删除
  201 + </el-button>
  202 + </template>
  203 + </el-table-column>
  204 + </el-table>
  205 +## 特殊:树表专属逻辑(树不需要分页)
  206 +#if ( $table.templateType != 2 )
205 <!-- 分页 --> 207 <!-- 分页 -->
206 <Pagination 208 <Pagination
207 - :total="total"  
208 - v-model:page="queryParams.pageNo"  
209 - v-model:limit="queryParams.pageSize"  
210 - @pagination="getList" 209 + :total="total"
  210 + v-model:page="queryParams.pageNo"
  211 + v-model:limit="queryParams.pageSize"
  212 + @pagination="getList"
211 /> 213 />
  214 +#end
212 </ContentWrap> 215 </ContentWrap>
213 216
214 <!-- 表单弹窗:添加/修改 --> 217 <!-- 表单弹窗:添加/修改 -->
215 <${simpleClassName}Form ref="formRef" @success="getList" /> 218 <${simpleClassName}Form ref="formRef" @success="getList" />
216 -## 特殊:主子表专属逻辑  
217 -#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )  
218 - <!-- 子表的列表 -->  
219 - <ContentWrap>  
220 - <el-tabs model-value="$subClassNameVars.get(0)">  
221 - #foreach ($subTable in $subTables)  
222 - #set ($index = $foreach.count - 1)  
223 - #set ($subClassNameVar = $subClassNameVars.get($index))  
224 - #set ($subSimpleClassName = $subSimpleClassNames.get($index))  
225 - #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))  
226 - <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">  
227 - <${subSimpleClassName}List :${subJoinColumn_strikeCase}="currentRow.id" />  
228 - </el-tab-pane>  
229 - #end  
230 - </el-tabs>  
231 - </ContentWrap>  
232 -#end 219 + ## 特殊:主子表专属逻辑
  220 + #if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
  221 + <!-- 子表的列表 -->
  222 + <ContentWrap>
  223 + <el-tabs model-value="$subClassNameVars.get(0)">
  224 + #foreach ($subTable in $subTables)
  225 + #set ($index = $foreach.count - 1)
  226 + #set ($subClassNameVar = $subClassNameVars.get($index))
  227 + #set ($subSimpleClassName = $subSimpleClassNames.get($index))
  228 + #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
  229 + <el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
  230 + <${subSimpleClassName}List :${subJoinColumn_strikeCase}="currentRow.id" />
  231 + </el-tab-pane>
  232 + #end
  233 + </el-tabs>
  234 + </ContentWrap>
  235 + #end
233 </template> 236 </template>
234 237
235 <script setup lang="ts"> 238 <script setup lang="ts">
236 -import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'  
237 -import { dateFormatter } from '@/utils/formatTime'  
238 -## 特殊:树表专属逻辑  
239 -#if ( $table.templateType == 2 )  
240 -import { handleTree } from '@/utils/tree'  
241 -#end  
242 -import download from '@/utils/download'  
243 -import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'  
244 -import ${simpleClassName}Form from './${simpleClassName}Form.vue'  
245 -## 特殊:主子表专属逻辑  
246 -#if ( $table.templateType != 10 )  
247 -#foreach ($subSimpleClassName in $subSimpleClassNames)  
248 -import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'  
249 -#end  
250 -#end 239 + import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
  240 + import { dateFormatter } from '@/utils/formatTime'
  241 + ## 特殊:树表专属逻辑
  242 + #if ( $table.templateType == 2 )
  243 + import { handleTree } from '@/utils/tree'
  244 + #end
  245 + import download from '@/utils/download'
  246 + import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
  247 + import ${simpleClassName}Form from './${simpleClassName}Form.vue'
  248 + ## 特殊:主子表专属逻辑
  249 + #if ( $table.templateType != 10 )
  250 + #foreach ($subSimpleClassName in $subSimpleClassNames)
  251 + import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'
  252 + #end
  253 + #end
251 254
252 -defineOptions({ name: '${table.className}' }) 255 + defineOptions({ name: '${table.className}' })
253 256
254 -const message = useMessage() // 消息弹窗  
255 -const { t } = useI18n() // 国际化 257 + const message = useMessage() // 消息弹窗
  258 + const { t } = useI18n() // 国际化
256 259
257 -const loading = ref(true) // 列表的加载中  
258 -const list = ref([]) // 列表的数据  
259 -## 特殊:树表专属逻辑(树不需要分页接口)  
260 -#if ( $table.templateType != 2 )  
261 -const total = ref(0) // 列表的总页数  
262 -#end  
263 -const queryParams = reactive({  
264 -## 特殊:树表专属逻辑(树不需要分页接口)  
265 -#if ( $table.templateType != 2 )  
266 - pageNo: 1,  
267 - pageSize: 10,  
268 -#end  
269 - #foreach ($column in $columns)  
270 - #if ($column.listOperation)  
271 - #if ($column.listOperationCondition != 'BETWEEN')  
272 - $column.javaField: null,  
273 - #end  
274 - #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")  
275 - $column.javaField: [], 260 + const loading = ref(true) // 列表的加载中
  261 + const list = ref([]) // 列表的数据
  262 + ## 特殊:树表专属逻辑(树不需要分页接口)
  263 + #if ( $table.templateType != 2 )
  264 + const total = ref(0) // 列表的总页数
276 #end 265 #end
277 - #end  
278 - #end  
279 -})  
280 -const queryFormRef = ref() // 搜索的表单  
281 -const exportLoading = ref(false) // 导出的加载中 266 + const queryParams = reactive({
  267 + ## 特殊:树表专属逻辑(树不需要分页接口)
  268 + #if ( $table.templateType != 2 )
  269 + pageNo: 1,
  270 + pageSize: 10,
  271 + #end
  272 + #foreach ($column in $columns)
  273 + #if ($column.listOperation)
  274 + #if ($column.listOperationCondition != 'BETWEEN')
  275 + $column.javaField: null,
  276 + #end
  277 + #if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
  278 + $column.javaField: [],
  279 + #end
  280 + #end
  281 + #end
  282 + })
  283 + const queryFormRef = ref() // 搜索的表单
  284 + const exportLoading = ref(false) // 导出的加载中
282 285
283 -/** 查询列表 */  
284 -const getList = async () => {  
285 - loading.value = true  
286 - try {  
287 -## 特殊:树表专属逻辑(树不需要分页接口)  
288 - #if ( $table.templateType == 2 )  
289 - const data = await ${simpleClassName}Api.get${simpleClassName}List(queryParams)  
290 - list.value = handleTree(data, 'id', '${treeParentColumn.javaField}')  
291 - #else  
292 - const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams)  
293 - list.value = data.list  
294 - total.value = data.total  
295 - #end  
296 - } finally {  
297 - loading.value = false 286 + /** 查询列表 */
  287 + const getList = async () => {
  288 + loading.value = true
  289 + try {
  290 + ## 特殊:树表专属逻辑(树不需要分页接口)
  291 + #if ( $table.templateType == 2 )
  292 + const data = await ${simpleClassName}Api.get${simpleClassName}List(queryParams)
  293 + list.value = handleTree(data, 'id', '${treeParentColumn.javaField}')
  294 + #else
  295 + const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams)
  296 + list.value = data.list
  297 + total.value = data.total
  298 + #end
  299 + } finally {
  300 + loading.value = false
  301 + }
298 } 302 }
299 -}  
300 303
301 -/** 搜索按钮操作 */  
302 -const handleQuery = () => {  
303 - queryParams.pageNo = 1  
304 - getList()  
305 -} 304 + /** 搜索按钮操作 */
  305 + const handleQuery = () => {
  306 + queryParams.pageNo = 1
  307 + getList()
  308 + }
306 309
307 -/** 重置按钮操作 */  
308 -const resetQuery = () => {  
309 - queryFormRef.value.resetFields()  
310 - handleQuery()  
311 -} 310 + /** 重置按钮操作 */
  311 + const resetQuery = () => {
  312 + queryFormRef.value.resetFields()
  313 + handleQuery()
  314 + }
312 315
313 -/** 添加/修改操作 */  
314 -const formRef = ref()  
315 -const openForm = (type: string, id?: number) => {  
316 - formRef.value.open(type, id)  
317 -} 316 + /** 添加/修改操作 */
  317 + const formRef = ref()
  318 + const openForm = (type: string, id?: number) => {
  319 + formRef.value.open(type, id)
  320 + }
318 321
319 -/** 删除按钮操作 */  
320 -const handleDelete = async (id: number) => {  
321 - try {  
322 - // 删除的二次确认  
323 - await message.delConfirm()  
324 - // 发起删除  
325 - await ${simpleClassName}Api.delete${simpleClassName}(id)  
326 - message.success(t('common.delSuccess'))  
327 - // 刷新列表  
328 - await getList()  
329 - } catch {}  
330 -} 322 + /** 删除按钮操作 */
  323 + const handleDelete = async (id: number) => {
  324 + try {
  325 + // 删除的二次确认
  326 + await message.delConfirm()
  327 + // 发起删除
  328 + await ${simpleClassName}Api.delete${simpleClassName}(id)
  329 + message.success(t('common.delSuccess'))
  330 + // 刷新列表
  331 + await getList()
  332 + } catch {}
  333 + }
331 334
332 -/** 导出按钮操作 */  
333 -const handleExport = async () => {  
334 - try {  
335 - // 导出的二次确认  
336 - await message.exportConfirm()  
337 - // 发起导出  
338 - exportLoading.value = true  
339 - const data = await ${simpleClassName}Api.export${simpleClassName}(queryParams)  
340 - download.excel(data, '${table.classComment}.xls')  
341 - } catch {  
342 - } finally {  
343 - exportLoading.value = false 335 + /** 导出按钮操作 */
  336 + const handleExport = async () => {
  337 + try {
  338 + // 导出的二次确认
  339 + await message.exportConfirm()
  340 + // 发起导出
  341 + exportLoading.value = true
  342 + const data = await ${simpleClassName}Api.export${simpleClassName}(queryParams)
  343 + download.excel(data, '${table.classComment}.xls')
  344 + } catch {
  345 + } finally {
  346 + exportLoading.value = false
  347 + }
344 } 348 }
345 -}  
346 -## 特殊:主子表专属逻辑  
347 -#if ( $table.templateType == 11 ) 349 + ## 特殊:主子表专属逻辑
  350 + #if ( $table.templateType == 11 )
348 351
349 -/** 选中行操作 */  
350 -const currentRow = ref({}) // 选中行  
351 -const handleCurrentChange = (row) => {  
352 - currentRow.value = row  
353 -}  
354 -#end  
355 -## 特殊:树表专属逻辑  
356 -#if ( $table.templateType == 2 ) 352 + /** 选中行操作 */
  353 + const currentRow = ref({}) // 选中行
  354 + const handleCurrentChange = (row) => {
  355 + currentRow.value = row
  356 + }
  357 + #end
  358 + ## 特殊:树表专属逻辑
  359 + #if ( $table.templateType == 2 )
357 360
358 -/** 展开/折叠操作 */  
359 -const isExpandAll = ref(true) // 是否展开,默认全部展开  
360 -const refreshTable = ref(true) // 重新渲染表格状态  
361 -const toggleExpandAll = async () => {  
362 - refreshTable.value = false  
363 - isExpandAll.value = !isExpandAll.value  
364 - await nextTick()  
365 - refreshTable.value = true  
366 -}  
367 -#end 361 + /** 展开/折叠操作 */
  362 + const isExpandAll = ref(true) // 是否展开,默认全部展开
  363 + const refreshTable = ref(true) // 重新渲染表格状态
  364 + const toggleExpandAll = async () => {
  365 + refreshTable.value = false
  366 + isExpandAll.value = !isExpandAll.value
  367 + await nextTick()
  368 + refreshTable.value = true
  369 + }
  370 + #end
368 371
369 -/** 初始化 **/  
370 -onMounted(() => {  
371 - getList()  
372 -}) 372 + /** 初始化 **/
  373 + onMounted(() => {
  374 + getList()
  375 + })
373 </script> 376 </script>
374 \ No newline at end of file 377 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.codegen.inner;
  2 +
  3 +import cn.hutool.core.io.FileUtil;
  4 +import cn.hutool.core.io.IoUtil;
  5 +import cn.hutool.core.io.resource.ResourceUtil;
  6 +import cn.hutool.core.map.MapUtil;
  7 +import cn.hutool.core.util.StrUtil;
  8 +import cn.hutool.core.util.ZipUtil;
  9 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  10 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  13 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
  14 +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  15 +import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
  16 +import org.junit.jupiter.api.BeforeEach;
  17 +import org.junit.jupiter.api.Test;
  18 +import org.mockito.InjectMocks;
  19 +import org.mockito.Spy;
  20 +
  21 +import java.io.ByteArrayInputStream;
  22 +import java.io.ByteArrayOutputStream;
  23 +import java.util.*;
  24 +
  25 +import static org.junit.jupiter.api.Assertions.assertEquals;
  26 +
  27 +/**
  28 + * {@link CodegenEngine} 的单元测试
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +public class CodegenEngineVue2Test extends BaseMockitoUnitTest {
  33 +
  34 + @InjectMocks
  35 + private CodegenEngine codegenEngine;
  36 +
  37 + @Spy
  38 + private CodegenProperties codegenProperties = new CodegenProperties()
  39 + .setBasePackage("cn.iocoder.yudao");
  40 +
  41 + @BeforeEach
  42 + public void setUp() {
  43 + codegenEngine.initGlobalBindingMap();
  44 + }
  45 +
  46 + @Test
  47 + public void testExecute_vue2_one() {
  48 + // 准备参数
  49 + CodegenTableDO table = getTable("student")
  50 + .setFrontType(CodegenFrontTypeEnum.VUE2.getType())
  51 + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
  52 + List<CodegenColumnDO> columns = getColumnList("student");
  53 +
  54 + // 调用
  55 + Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  56 + // 断言
  57 + assertResult(result, "codegen/vue2_one");
  58 +// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one");
  59 + }
  60 +
  61 + @Test
  62 + public void testExecute_vue2_tree() {
  63 + // 准备参数
  64 + CodegenTableDO table = getTable("category")
  65 + .setFrontType(CodegenFrontTypeEnum.VUE2.getType())
  66 + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
  67 + List<CodegenColumnDO> columns = getColumnList("category");
  68 +
  69 + // 调用
  70 + Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  71 + // 断言
  72 + assertResult(result, "codegen/vue2_tree");
  73 +// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree");
  74 +// writeFile(result, "/Users/yunai/test/demo66.zip");
  75 + }
  76 +
  77 + @Test
  78 + public void testExecute_vue2_master_normal() {
  79 + testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "codegen/vue2_master_normal");
  80 + }
  81 +
  82 + @Test
  83 + public void testExecute_vue2_master_erp() {
  84 + testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_ERP, "codegen/vue2_master_erp");
  85 + }
  86 +
  87 + @Test
  88 + public void testExecute_vue2_master_inner() {
  89 + testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_INNER, "codegen/vue2_master_inner");
  90 + }
  91 +
  92 + private void testExecute_vue2_master(CodegenTemplateTypeEnum templateType,
  93 + String path) {
  94 + // 准备参数
  95 + CodegenTableDO table = getTable("student")
  96 + .setFrontType(CodegenFrontTypeEnum.VUE2.getType())
  97 + .setTemplateType(templateType.getType());
  98 + List<CodegenColumnDO> columns = getColumnList("student");
  99 + // 准备参数(子表)
  100 + CodegenTableDO contactTable = getTable("contact")
  101 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  102 + .setFrontType(CodegenFrontTypeEnum.VUE2.getType())
  103 + .setSubJoinColumnId(100L).setSubJoinMany(true);
  104 + List<CodegenColumnDO> contactColumns = getColumnList("contact");
  105 + // 准备参数(班主任)
  106 + CodegenTableDO teacherTable = getTable("teacher")
  107 + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
  108 + .setFrontType(CodegenFrontTypeEnum.VUE2.getType())
  109 + .setSubJoinColumnId(200L).setSubJoinMany(false);
  110 + List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
  111 +
  112 + // 调用
  113 + Map<String, String> result = codegenEngine.execute(table, columns,
  114 + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
  115 + // 断言
  116 + assertResult(result, path);
  117 +// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/" + path);
  118 +// writeFile(result, "/Users/yunai/test/demo11.zip");
  119 + }
  120 +
  121 + private static CodegenTableDO getTable(String name) {
  122 + String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json");
  123 + return JsonUtils.parseObject(content, "table", CodegenTableDO.class);
  124 + }
  125 +
  126 + private static List<CodegenColumnDO> getColumnList(String name) {
  127 + String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json");
  128 + List<CodegenColumnDO> list = JsonUtils.parseArray(content, "columns", CodegenColumnDO.class);
  129 + list.forEach(column -> {
  130 + if (column.getNullable() == null) {
  131 + column.setNullable(false);
  132 + }
  133 + if (column.getCreateOperation() == null) {
  134 + column.setCreateOperation(false);
  135 + }
  136 + if (column.getUpdateOperation() == null) {
  137 + column.setUpdateOperation(false);
  138 + }
  139 + if (column.getListOperation() == null) {
  140 + column.setListOperation(false);
  141 + }
  142 + if (column.getListOperationResult() == null) {
  143 + column.setListOperationResult(false);
  144 + }
  145 + });
  146 + return list;
  147 + }
  148 +
  149 + @SuppressWarnings("rawtypes")
  150 + private static void assertResult(Map<String, String> result, String path) {
  151 + String assertContent = ResourceUtil.readUtf8Str(path + "/assert.json");
  152 + List<HashMap> asserts = JsonUtils.parseArray(assertContent, HashMap.class);
  153 + assertEquals(asserts.size(), result.size());
  154 + // 校验每个文件
  155 + asserts.forEach(assertMap -> {
  156 + String contentPath = (String) assertMap.get("contentPath");
  157 + String filePath = (String) assertMap.get("filePath");
  158 + String content = ResourceUtil.readUtf8Str(path + "/" + contentPath);
  159 + assertEquals(content, result.get(filePath), filePath + ":不匹配");
  160 + });
  161 + }
  162 +
  163 + // ==================== 调试专用 ====================
  164 +
  165 + /**
  166 + * 【调试使用】将生成的代码,写入到文件
  167 + *
  168 + * @param result 生成的代码
  169 + * @param path 写入文件的路径
  170 + */
  171 + private void writeFile(Map<String, String> result, String path) {
  172 + // 生成压缩包
  173 + String[] paths = result.keySet().toArray(new String[0]);
  174 + ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
  175 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  176 + ZipUtil.zip(outputStream, paths, ins);
  177 + // 写入文件
  178 + FileUtil.writeBytes(outputStream.toByteArray(), path);
  179 + }
  180 +
  181 + /**
  182 + * 【调试使用】将生成的结果,写入到文件
  183 + *
  184 + * @param result 生成的代码
  185 + * @param basePath 写入文件的路径(绝对路径)
  186 + */
  187 + private void writeResult(Map<String, String> result, String basePath) {
  188 + // 写入文件内容
  189 + List<Map<String, String>> asserts = new ArrayList<>();
  190 + result.forEach((filePath, fileContent) -> {
  191 + String lastFilePath = StrUtil.subAfter(filePath, '/', true);
  192 + String contentPath = StrUtil.subAfter(lastFilePath, '.', true)
  193 + + '/' + StrUtil.subBefore(lastFilePath, '.', true);
  194 + asserts.add(MapUtil.<String, String>builder().put("filePath", filePath)
  195 + .put("contentPath", contentPath).build());
  196 + FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
  197 + });
  198 + // 写入 assert.json 文件
  199 + FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath +"/assert.json");
  200 + }
  201 +
  202 +}
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/assert.json 0 → 100644
  1 +[ {
  2 + "contentPath" : "java/InfraStudentPageReqVO",
  3 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
  4 +}, {
  5 + "contentPath" : "java/InfraStudentRespVO",
  6 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
  7 +}, {
  8 + "contentPath" : "java/InfraStudentSaveReqVO",
  9 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
  10 +}, {
  11 + "contentPath" : "java/InfraStudentController",
  12 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
  13 +}, {
  14 + "contentPath" : "java/InfraStudentDO",
  15 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
  16 +}, {
  17 + "contentPath" : "java/InfraStudentContactDO",
  18 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
  19 +}, {
  20 + "contentPath" : "java/InfraStudentTeacherDO",
  21 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
  22 +}, {
  23 + "contentPath" : "java/InfraStudentMapper",
  24 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
  25 +}, {
  26 + "contentPath" : "java/InfraStudentContactMapper",
  27 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
  28 +}, {
  29 + "contentPath" : "java/InfraStudentTeacherMapper",
  30 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
  31 +}, {
  32 + "contentPath" : "xml/InfraStudentMapper",
  33 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
  34 +}, {
  35 + "contentPath" : "java/InfraStudentServiceImpl",
  36 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
  37 +}, {
  38 + "contentPath" : "java/InfraStudentService",
  39 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
  40 +}, {
  41 + "contentPath" : "java/InfraStudentServiceImplTest",
  42 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
  43 +}, {
  44 + "contentPath" : "java/ErrorCodeConstants_手动操作",
  45 + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
  46 +}, {
  47 + "contentPath" : "sql/sql",
  48 + "filePath" : "sql/sql.sql"
  49 +}, {
  50 + "contentPath" : "sql/h2",
  51 + "filePath" : "sql/h2.sql"
  52 +}, {
  53 + "contentPath" : "vue/index",
  54 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
  55 +}, {
  56 + "contentPath" : "js/student",
  57 + "filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
  58 +}, {
  59 + "contentPath" : "vue/StudentForm",
  60 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
  61 +}, {
  62 + "contentPath" : "vue/StudentContactForm",
  63 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
  64 +}, {
  65 + "contentPath" : "vue/StudentTeacherForm",
  66 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
  67 +}, {
  68 + "contentPath" : "vue/StudentContactList",
  69 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue"
  70 +}, {
  71 + "contentPath" : "vue/StudentTeacherList",
  72 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue"
  73 +} ]
0 \ No newline at end of file 74 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/ErrorCodeConstants_手动操作 0 → 100644
  1 +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
  2 +// ========== 学生 TODO 补充编号 ==========
  3 +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
  4 +ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在");
  5 +ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在");
  6 +ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在");
0 \ No newline at end of file 7 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentContactDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生联系人 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student_contact")
  16 +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentContactDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 学生编号
  32 + */
  33 + private Long studentId;
  34 + /**
  35 + * 名字
  36 + */
  37 + private String name;
  38 + /**
  39 + * 简介
  40 + */
  41 + private String description;
  42 + /**
  43 + * 出生日期
  44 + */
  45 + private LocalDateTime birthday;
  46 + /**
  47 + * 性别
  48 + *
  49 + * 枚举 {@link TODO system_user_sex 对应的类}
  50 + */
  51 + private Integer sex;
  52 + /**
  53 + * 是否有效
  54 + *
  55 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  56 + */
  57 + private Boolean enabled;
  58 + /**
  59 + * 头像
  60 + */
  61 + private String avatar;
  62 + /**
  63 + * 附件
  64 + */
  65 + private String video;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String memo;
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentContactMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +/**
  13 + * 学生联系人 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
  19 +
  20 + default PageResult<InfraStudentContactDO> selectPage(PageParam reqVO, Long studentId) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentContactDO>()
  22 + .eq(InfraStudentContactDO::getStudentId, studentId)
  23 + .orderByDesc(InfraStudentContactDO::getId));
  24 + }
  25 +
  26 + default int deleteByStudentId(Long studentId) {
  27 + return delete(InfraStudentContactDO::getStudentId, studentId);
  28 + }
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentController 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import javax.validation.constraints.*;
  12 +import javax.validation.*;
  13 +import javax.servlet.http.*;
  14 +import java.util.*;
  15 +import java.io.IOException;
  16 +
  17 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  18 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  19 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  20 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  24 +
  25 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
  27 +
  28 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  29 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  30 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  31 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  32 +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
  33 +
  34 +@Tag(name = "管理后台 - 学生")
  35 +@RestController
  36 +@RequestMapping("/infra/student")
  37 +@Validated
  38 +public class InfraStudentController {
  39 +
  40 + @Resource
  41 + private InfraStudentService studentService;
  42 +
  43 + @PostMapping("/create")
  44 + @Operation(summary = "创建学生")
  45 + @PreAuthorize("@ss.hasPermission('infra:student:create')")
  46 + public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
  47 + return success(studentService.createStudent(createReqVO));
  48 + }
  49 +
  50 + @PutMapping("/update")
  51 + @Operation(summary = "更新学生")
  52 + @PreAuthorize("@ss.hasPermission('infra:student:update')")
  53 + public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
  54 + studentService.updateStudent(updateReqVO);
  55 + return success(true);
  56 + }
  57 +
  58 + @DeleteMapping("/delete")
  59 + @Operation(summary = "删除学生")
  60 + @Parameter(name = "id", description = "编号", required = true)
  61 + @PreAuthorize("@ss.hasPermission('infra:student:delete')")
  62 + public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
  63 + studentService.deleteStudent(id);
  64 + return success(true);
  65 + }
  66 +
  67 + @GetMapping("/get")
  68 + @Operation(summary = "获得学生")
  69 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  70 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  71 + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
  72 + InfraStudentDO student = studentService.getStudent(id);
  73 + return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/page")
  77 + @Operation(summary = "获得学生分页")
  78 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  79 + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
  80 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
  81 + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
  82 + }
  83 +
  84 + @GetMapping("/export-excel")
  85 + @Operation(summary = "导出学生 Excel")
  86 + @PreAuthorize("@ss.hasPermission('infra:student:export')")
  87 + @OperateLog(type = EXPORT)
  88 + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
  89 + HttpServletResponse response) throws IOException {
  90 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  91 + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
  92 + // 导出 Excel
  93 + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
  94 + BeanUtils.toBean(list, InfraStudentRespVO.class));
  95 + }
  96 +
  97 + // ==================== 子表(学生联系人) ====================
  98 +
  99 + @GetMapping("/student-contact/page")
  100 + @Operation(summary = "获得学生联系人分页")
  101 + @Parameter(name = "studentId", description = "学生编号")
  102 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  103 + public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO,
  104 + @RequestParam("studentId") Long studentId) {
  105 + return success(studentService.getStudentContactPage(pageReqVO, studentId));
  106 + }
  107 +
  108 + @PostMapping("/student-contact/create")
  109 + @Operation(summary = "创建学生联系人")
  110 + @PreAuthorize("@ss.hasPermission('infra:student:create')")
  111 + public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
  112 + return success(studentService.createStudentContact(studentContact));
  113 + }
  114 +
  115 + @PutMapping("/student-contact/update")
  116 + @Operation(summary = "更新学生联系人")
  117 + @PreAuthorize("@ss.hasPermission('infra:student:update')")
  118 + public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
  119 + studentService.updateStudentContact(studentContact);
  120 + return success(true);
  121 + }
  122 +
  123 + @DeleteMapping("/student-contact/delete")
  124 + @Parameter(name = "id", description = "编号", required = true)
  125 + @Operation(summary = "删除学生联系人")
  126 + @PreAuthorize("@ss.hasPermission('infra:student:delete')")
  127 + public CommonResult<Boolean> deleteStudentContact(@RequestParam("id") Long id) {
  128 + studentService.deleteStudentContact(id);
  129 + return success(true);
  130 + }
  131 +
  132 + @GetMapping("/student-contact/get")
  133 + @Operation(summary = "获得学生联系人")
  134 + @Parameter(name = "id", description = "编号", required = true)
  135 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  136 + public CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam("id") Long id) {
  137 + return success(studentService.getStudentContact(id));
  138 + }
  139 +
  140 + // ==================== 子表(学生班主任) ====================
  141 +
  142 + @GetMapping("/student-teacher/page")
  143 + @Operation(summary = "获得学生班主任分页")
  144 + @Parameter(name = "studentId", description = "学生编号")
  145 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  146 + public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO,
  147 + @RequestParam("studentId") Long studentId) {
  148 + return success(studentService.getStudentTeacherPage(pageReqVO, studentId));
  149 + }
  150 +
  151 + @PostMapping("/student-teacher/create")
  152 + @Operation(summary = "创建学生班主任")
  153 + @PreAuthorize("@ss.hasPermission('infra:student:create')")
  154 + public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
  155 + return success(studentService.createStudentTeacher(studentTeacher));
  156 + }
  157 +
  158 + @PutMapping("/student-teacher/update")
  159 + @Operation(summary = "更新学生班主任")
  160 + @PreAuthorize("@ss.hasPermission('infra:student:update')")
  161 + public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
  162 + studentService.updateStudentTeacher(studentTeacher);
  163 + return success(true);
  164 + }
  165 +
  166 + @DeleteMapping("/student-teacher/delete")
  167 + @Parameter(name = "id", description = "编号", required = true)
  168 + @Operation(summary = "删除学生班主任")
  169 + @PreAuthorize("@ss.hasPermission('infra:student:delete')")
  170 + public CommonResult<Boolean> deleteStudentTeacher(@RequestParam("id") Long id) {
  171 + studentService.deleteStudentTeacher(id);
  172 + return success(true);
  173 + }
  174 +
  175 + @GetMapping("/student-teacher/get")
  176 + @Operation(summary = "获得学生班主任")
  177 + @Parameter(name = "id", description = "编号", required = true)
  178 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  179 + public CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam("id") Long id) {
  180 + return success(studentService.getStudentTeacher(id));
  181 + }
  182 +
  183 +}
0 \ No newline at end of file 184 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student")
  16 +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 名字
  32 + */
  33 + private String name;
  34 + /**
  35 + * 简介
  36 + */
  37 + private String description;
  38 + /**
  39 + * 出生日期
  40 + */
  41 + private LocalDateTime birthday;
  42 + /**
  43 + * 性别
  44 + *
  45 + * 枚举 {@link TODO system_user_sex 对应的类}
  46 + */
  47 + private Integer sex;
  48 + /**
  49 + * 是否有效
  50 + *
  51 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  52 + */
  53 + private Boolean enabled;
  54 + /**
  55 + * 头像
  56 + */
  57 + private String avatar;
  58 + /**
  59 + * 附件
  60 + */
  61 + private String video;
  62 + /**
  63 + * 备注
  64 + */
  65 + private String memo;
  66 +
  67 +}
0 \ No newline at end of file 68 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  11 +
  12 +/**
  13 + * 学生 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
  19 +
  20 + default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
  22 + .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
  23 + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
  24 + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
  25 + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
  26 + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(InfraStudentDO::getId));
  28 + }
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentPageReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - 学生分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class InfraStudentPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "名字", example = "芋头")
  19 + private String name;
  20 +
  21 + @Schema(description = "出生日期")
  22 + private LocalDateTime birthday;
  23 +
  24 + @Schema(description = "性别", example = "1")
  25 + private Integer sex;
  26 +
  27 + @Schema(description = "是否有效", example = "true")
  28 + private Boolean enabled;
  29 +
  30 + @Schema(description = "创建时间")
  31 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  32 + private LocalDateTime[] createTime;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentRespVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import java.util.*;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +import com.alibaba.excel.annotation.*;
  10 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  11 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  12 +
  13 +@Schema(description = "管理后台 - 学生 Response VO")
  14 +@Data
  15 +@ExcelIgnoreUnannotated
  16 +public class InfraStudentRespVO {
  17 +
  18 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @ExcelProperty("编号")
  20 + private Long id;
  21 +
  22 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  23 + @ExcelProperty("名字")
  24 + private String name;
  25 +
  26 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  27 + @ExcelProperty("简介")
  28 + private String description;
  29 +
  30 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @ExcelProperty("出生日期")
  32 + private LocalDateTime birthday;
  33 +
  34 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + @ExcelProperty(value = "性别", converter = DictConvert.class)
  36 + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  37 + private Integer sex;
  38 +
  39 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  40 + @ExcelProperty(value = "是否有效", converter = DictConvert.class)
  41 + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  42 + private Boolean enabled;
  43 +
  44 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  45 + @ExcelProperty("头像")
  46 + private String avatar;
  47 +
  48 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  49 + @ExcelProperty("附件")
  50 + private String video;
  51 +
  52 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  53 + @ExcelProperty("备注")
  54 + private String memo;
  55 +
  56 + @Schema(description = "创建时间")
  57 + @ExcelProperty("创建时间")
  58 + private LocalDateTime createTime;
  59 +
  60 +}
0 \ No newline at end of file 61 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentSaveReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import javax.validation.constraints.*;
  7 +import java.util.*;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +import java.time.LocalDateTime;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  12 +
  13 +@Schema(description = "管理后台 - 学生新增/修改 Request VO")
  14 +@Data
  15 +public class InfraStudentSaveReqVO {
  16 +
  17 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + private Long id;
  19 +
  20 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  21 + @NotEmpty(message = "名字不能为空")
  22 + private String name;
  23 +
  24 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  25 + @NotEmpty(message = "简介不能为空")
  26 + private String description;
  27 +
  28 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + @NotNull(message = "出生日期不能为空")
  30 + private LocalDateTime birthday;
  31 +
  32 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + @NotNull(message = "性别不能为空")
  34 + private Integer sex;
  35 +
  36 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  37 + @NotNull(message = "是否有效不能为空")
  38 + private Boolean enabled;
  39 +
  40 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  41 + @NotEmpty(message = "头像不能为空")
  42 + private String avatar;
  43 +
  44 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  45 + @NotEmpty(message = "附件不能为空")
  46 + private String video;
  47 +
  48 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  49 + @NotEmpty(message = "备注不能为空")
  50 + private String memo;
  51 +
  52 +}
0 \ No newline at end of file 53 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentService 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import java.util.*;
  4 +import javax.validation.*;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  6 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  7 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  9 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  10 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  11 +
  12 +/**
  13 + * 学生 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface InfraStudentService {
  18 +
  19 + /**
  20 + * 创建学生
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新学生
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除学生
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deleteStudent(Long id);
  40 +
  41 + /**
  42 + * 获得学生
  43 + *
  44 + * @param id 编号
  45 + * @return 学生
  46 + */
  47 + InfraStudentDO getStudent(Long id);
  48 +
  49 + /**
  50 + * 获得学生分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return 学生分页
  54 + */
  55 + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
  56 +
  57 + // ==================== 子表(学生联系人) ====================
  58 +
  59 + /**
  60 + * 获得学生联系人分页
  61 + *
  62 + * @param pageReqVO 分页查询
  63 + * @param studentId 学生编号
  64 + * @return 学生联系人分页
  65 + */
  66 + PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId);
  67 +
  68 + /**
  69 + * 创建学生联系人
  70 + *
  71 + * @param studentContact 创建信息
  72 + * @return 编号
  73 + */
  74 + Long createStudentContact(@Valid InfraStudentContactDO studentContact);
  75 +
  76 + /**
  77 + * 更新学生联系人
  78 + *
  79 + * @param studentContact 更新信息
  80 + */
  81 + void updateStudentContact(@Valid InfraStudentContactDO studentContact);
  82 +
  83 + /**
  84 + * 删除学生联系人
  85 + *
  86 + * @param id 编号
  87 + */
  88 + void deleteStudentContact(Long id);
  89 +
  90 + /**
  91 + * 获得学生联系人
  92 + *
  93 + * @param id 编号
  94 + * @return 学生联系人
  95 + */
  96 + InfraStudentContactDO getStudentContact(Long id);
  97 +
  98 + // ==================== 子表(学生班主任) ====================
  99 +
  100 + /**
  101 + * 获得学生班主任分页
  102 + *
  103 + * @param pageReqVO 分页查询
  104 + * @param studentId 学生编号
  105 + * @return 学生班主任分页
  106 + */
  107 + PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId);
  108 +
  109 + /**
  110 + * 创建学生班主任
  111 + *
  112 + * @param studentTeacher 创建信息
  113 + * @return 编号
  114 + */
  115 + Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
  116 +
  117 + /**
  118 + * 更新学生班主任
  119 + *
  120 + * @param studentTeacher 更新信息
  121 + */
  122 + void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
  123 +
  124 + /**
  125 + * 删除学生班主任
  126 + *
  127 + * @param id 编号
  128 + */
  129 + void deleteStudentTeacher(Long id);
  130 +
  131 + /**
  132 + * 获得学生班主任
  133 + *
  134 + * @param id 编号
  135 + * @return 学生班主任
  136 + */
  137 + InfraStudentTeacherDO getStudentTeacher(Long id);
  138 +
  139 +}
0 \ No newline at end of file 140 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentServiceImpl 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.transaction.annotation.Transactional;
  7 +
  8 +import java.util.*;
  9 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  13 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  15 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  16 +
  17 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  18 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
  19 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +
  24 +/**
  25 + * 学生 Service 实现类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Service
  30 +@Validated
  31 +public class InfraStudentServiceImpl implements InfraStudentService {
  32 +
  33 + @Resource
  34 + private InfraStudentMapper studentMapper;
  35 + @Resource
  36 + private InfraStudentContactMapper studentContactMapper;
  37 + @Resource
  38 + private InfraStudentTeacherMapper studentTeacherMapper;
  39 +
  40 + @Override
  41 + public Long createStudent(InfraStudentSaveReqVO createReqVO) {
  42 + // 插入
  43 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
  44 + studentMapper.insert(student);
  45 + // 返回
  46 + return student.getId();
  47 + }
  48 +
  49 + @Override
  50 + public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
  51 + // 校验存在
  52 + validateStudentExists(updateReqVO.getId());
  53 + // 更新
  54 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
  55 + studentMapper.updateById(updateObj);
  56 + }
  57 +
  58 + @Override
  59 + @Transactional(rollbackFor = Exception.class)
  60 + public void deleteStudent(Long id) {
  61 + // 校验存在
  62 + validateStudentExists(id);
  63 + // 删除
  64 + studentMapper.deleteById(id);
  65 +
  66 + // 删除子表
  67 + deleteStudentContactByStudentId(id);
  68 + deleteStudentTeacherByStudentId(id);
  69 + }
  70 +
  71 + private void validateStudentExists(Long id) {
  72 + if (studentMapper.selectById(id) == null) {
  73 + throw exception(STUDENT_NOT_EXISTS);
  74 + }
  75 + }
  76 +
  77 + @Override
  78 + public InfraStudentDO getStudent(Long id) {
  79 + return studentMapper.selectById(id);
  80 + }
  81 +
  82 + @Override
  83 + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
  84 + return studentMapper.selectPage(pageReqVO);
  85 + }
  86 +
  87 + // ==================== 子表(学生联系人) ====================
  88 +
  89 + @Override
  90 + public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) {
  91 + return studentContactMapper.selectPage(pageReqVO, studentId);
  92 + }
  93 +
  94 + @Override
  95 + public Long createStudentContact(InfraStudentContactDO studentContact) {
  96 + studentContactMapper.insert(studentContact);
  97 + return studentContact.getId();
  98 + }
  99 +
  100 + @Override
  101 + public void updateStudentContact(InfraStudentContactDO studentContact) {
  102 + // 校验存在
  103 + validateStudentContactExists(studentContact.getId());
  104 + // 更新
  105 + studentContactMapper.updateById(studentContact);
  106 + }
  107 +
  108 + @Override
  109 + public void deleteStudentContact(Long id) {
  110 + // 校验存在
  111 + validateStudentContactExists(id);
  112 + // 删除
  113 + studentContactMapper.deleteById(id);
  114 + }
  115 +
  116 + @Override
  117 + public InfraStudentContactDO getStudentContact(Long id) {
  118 + return studentContactMapper.selectById(id);
  119 + }
  120 +
  121 + private void validateStudentContactExists(Long id) {
  122 + if (studentContactMapper.selectById(id) == null) {
  123 + throw exception(STUDENT_CONTACT_NOT_EXISTS);
  124 + }
  125 + }
  126 +
  127 + private void deleteStudentContactByStudentId(Long studentId) {
  128 + studentContactMapper.deleteByStudentId(studentId);
  129 + }
  130 +
  131 + // ==================== 子表(学生班主任) ====================
  132 +
  133 + @Override
  134 + public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) {
  135 + return studentTeacherMapper.selectPage(pageReqVO, studentId);
  136 + }
  137 +
  138 + @Override
  139 + public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) {
  140 + // 校验是否已经存在
  141 + if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) {
  142 + throw exception(STUDENT_TEACHER_EXISTS);
  143 + }
  144 + // 插入
  145 + studentTeacherMapper.insert(studentTeacher);
  146 + return studentTeacher.getId();
  147 + }
  148 +
  149 + @Override
  150 + public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) {
  151 + // 校验存在
  152 + validateStudentTeacherExists(studentTeacher.getId());
  153 + // 更新
  154 + studentTeacherMapper.updateById(studentTeacher);
  155 + }
  156 +
  157 + @Override
  158 + public void deleteStudentTeacher(Long id) {
  159 + // 校验存在
  160 + validateStudentTeacherExists(id);
  161 + // 删除
  162 + studentTeacherMapper.deleteById(id);
  163 + }
  164 +
  165 + @Override
  166 + public InfraStudentTeacherDO getStudentTeacher(Long id) {
  167 + return studentTeacherMapper.selectById(id);
  168 + }
  169 +
  170 + private void validateStudentTeacherExists(Long id) {
  171 + if (studentTeacherMapper.selectById(id) == null) {
  172 + throw exception(STUDENT_TEACHER_NOT_EXISTS);
  173 + }
  174 + }
  175 +
  176 + private void deleteStudentTeacherByStudentId(Long studentId) {
  177 + studentTeacherMapper.deleteByStudentId(studentId);
  178 + }
  179 +
  180 +}
0 \ No newline at end of file 181 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentServiceImplTest 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import javax.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  13 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import javax.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link InfraStudentServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(InfraStudentServiceImpl.class)
  37 +public class InfraStudentServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private InfraStudentServiceImpl studentService;
  41 +
  42 + @Resource
  43 + private InfraStudentMapper studentMapper;
  44 +
  45 + @Test
  46 + public void testCreateStudent_success() {
  47 + // 准备参数
  48 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long studentId = studentService.createStudent(createReqVO);
  52 + // 断言
  53 + assertNotNull(studentId);
  54 + // 校验记录的属性是否正确
  55 + InfraStudentDO student = studentMapper.selectById(studentId);
  56 + assertPojoEquals(createReqVO, student, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateStudent_success() {
  61 + // mock 数据
  62 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  63 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
  66 + o.setId(dbStudent.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + studentService.updateStudent(updateReqVO);
  71 + // 校验是否更新正确
  72 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, student);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateStudent_notExists() {
  78 + // 准备参数
  79 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteStudent_success() {
  87 + // mock 数据
  88 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  89 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbStudent.getId();
  92 +
  93 + // 调用
  94 + studentService.deleteStudent(id);
  95 + // 校验数据不存在了
  96 + assertNull(studentMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteStudent_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetStudentPage() {
  111 + // mock 数据
  112 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
  113 + o.setName(null);
  114 + o.setBirthday(null);
  115 + o.setSex(null);
  116 + o.setEnabled(null);
  117 + o.setCreateTime(null);
  118 + });
  119 + studentMapper.insert(dbStudent);
  120 + // 测试 name 不匹配
  121 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
  122 + // 测试 birthday 不匹配
  123 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
  124 + // 测试 sex 不匹配
  125 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
  126 + // 测试 enabled 不匹配
  127 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
  128 + // 测试 createTime 不匹配
  129 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
  130 + // 准备参数
  131 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
  132 + reqVO.setName(null);
  133 + reqVO.setBirthday(null);
  134 + reqVO.setSex(null);
  135 + reqVO.setEnabled(null);
  136 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  137 +
  138 + // 调用
  139 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
  140 + // 断言
  141 + assertEquals(1, pageResult.getTotal());
  142 + assertEquals(1, pageResult.getList().size());
  143 + assertPojoEquals(dbStudent, pageResult.getList().get(0));
  144 + }
  145 +
  146 +}
0 \ No newline at end of file 147 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentTeacherDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生班主任 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student_teacher")
  16 +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentTeacherDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 学生编号
  32 + */
  33 + private Long studentId;
  34 + /**
  35 + * 名字
  36 + */
  37 + private String name;
  38 + /**
  39 + * 简介
  40 + */
  41 + private String description;
  42 + /**
  43 + * 出生日期
  44 + */
  45 + private LocalDateTime birthday;
  46 + /**
  47 + * 性别
  48 + *
  49 + * 枚举 {@link TODO system_user_sex 对应的类}
  50 + */
  51 + private Integer sex;
  52 + /**
  53 + * 是否有效
  54 + *
  55 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  56 + */
  57 + private Boolean enabled;
  58 + /**
  59 + * 头像
  60 + */
  61 + private String avatar;
  62 + /**
  63 + * 附件
  64 + */
  65 + private String video;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String memo;
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/java/InfraStudentTeacherMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +/**
  13 + * 学生班主任 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
  19 +
  20 + default PageResult<InfraStudentTeacherDO> selectPage(PageParam reqVO, Long studentId) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentTeacherDO>()
  22 + .eq(InfraStudentTeacherDO::getStudentId, studentId)
  23 + .orderByDesc(InfraStudentTeacherDO::getId));
  24 + }
  25 +
  26 + default int deleteByStudentId(Long studentId) {
  27 + return delete(InfraStudentTeacherDO::getStudentId, studentId);
  28 + }
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/js/student 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建学生
  4 +export function createStudent(data) {
  5 + return request({
  6 + url: '/infra/student/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新学生
  13 +export function updateStudent(data) {
  14 + return request({
  15 + url: '/infra/student/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除学生
  22 +export function deleteStudent(id) {
  23 + return request({
  24 + url: '/infra/student/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得学生
  30 +export function getStudent(id) {
  31 + return request({
  32 + url: '/infra/student/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得学生分页
  38 +export function getStudentPage(params) {
  39 + return request({
  40 + url: '/infra/student/page',
  41 + method: 'get',
  42 + params
  43 + })
  44 +}
  45 +// 导出学生 Excel
  46 +export function exportStudentExcel(params) {
  47 + return request({
  48 + url: '/infra/student/export-excel',
  49 + method: 'get',
  50 + params,
  51 + responseType: 'blob'
  52 + })
  53 +}
  54 +
  55 +// ==================== 子表(学生联系人) ====================
  56 +
  57 + // 获得学生联系人分页
  58 + export function getStudentContactPage(params) {
  59 + return request({
  60 + url: '/infra/student/student-contact/page',
  61 + method: 'get',
  62 + params
  63 + })
  64 + }
  65 + // 新增学生联系人
  66 + export function createStudentContact(data) {
  67 + return request({
  68 + url: `/infra/student/student-contact/create`,
  69 + method: 'post',
  70 + data
  71 + })
  72 + }
  73 +
  74 + // 修改学生联系人
  75 + export function updateStudentContact(data) {
  76 + return request({
  77 + url: `/infra/student/student-contact/update`,
  78 + method: 'post',
  79 + data
  80 + })
  81 + }
  82 +
  83 + // 删除学生联系人
  84 + export function deleteStudentContact(id) {
  85 + return request({
  86 + url: `/infra/student/student-contact/delete?id=` + id,
  87 + method: 'delete'
  88 + })
  89 + }
  90 +
  91 + // 获得学生联系人
  92 + export function getStudentContact(id) {
  93 + return request({
  94 + url: `/infra/student/student-contact/get?id=` + id,
  95 + method: 'get'
  96 + })
  97 + }
  98 +
  99 +// ==================== 子表(学生班主任) ====================
  100 +
  101 + // 获得学生班主任分页
  102 + export function getStudentTeacherPage(params) {
  103 + return request({
  104 + url: '/infra/student/student-teacher/page',
  105 + method: 'get',
  106 + params
  107 + })
  108 + }
  109 + // 新增学生班主任
  110 + export function createStudentTeacher(data) {
  111 + return request({
  112 + url: `/infra/student/student-teacher/create`,
  113 + method: 'post',
  114 + data
  115 + })
  116 + }
  117 +
  118 + // 修改学生班主任
  119 + export function updateStudentTeacher(data) {
  120 + return request({
  121 + url: `/infra/student/student-teacher/update`,
  122 + method: 'post',
  123 + data
  124 + })
  125 + }
  126 +
  127 + // 删除学生班主任
  128 + export function deleteStudentTeacher(id) {
  129 + return request({
  130 + url: `/infra/student/student-teacher/delete?id=` + id,
  131 + method: 'delete'
  132 + })
  133 + }
  134 +
  135 + // 获得学生班主任
  136 + export function getStudentTeacher(id) {
  137 + return request({
  138 + url: `/infra/student/student-teacher/get?id=` + id,
  139 + method: 'get'
  140 + })
  141 + }
0 \ No newline at end of file 142 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/sql/h2 0 → 100644
  1 +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
  2 +CREATE TABLE IF NOT EXISTS "infra_student" (
  3 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  4 + "name" varchar NOT NULL,
  5 + "description" varchar NOT NULL,
  6 + "birthday" varchar NOT NULL,
  7 + "sex" int NOT NULL,
  8 + "enabled" bit NOT NULL,
  9 + "avatar" varchar NOT NULL,
  10 + "video" varchar NOT NULL,
  11 + "memo" varchar NOT NULL,
  12 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  13 + PRIMARY KEY ("id")
  14 +) COMMENT '学生表';
  15 +
  16 +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
  17 +DELETE FROM "infra_student";
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/sql/sql 0 → 100644
  1 +-- 菜单 SQL
  2 +INSERT INTO system_menu(
  3 + name, permission, type, sort, parent_id,
  4 + path, icon, component, status, component_name
  5 +)
  6 +VALUES (
  7 + '学生管理', '', 2, 0, 888,
  8 + 'student', '', 'infra/demo/index', 0, 'InfraStudent'
  9 +);
  10 +
  11 +-- 按钮父菜单ID
  12 +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
  13 +SELECT @parentId := LAST_INSERT_ID();
  14 +
  15 +-- 按钮 SQL
  16 +INSERT INTO system_menu(
  17 + name, permission, type, sort, parent_id,
  18 + path, icon, component, status
  19 +)
  20 +VALUES (
  21 + '学生查询', 'infra:student:query', 3, 1, @parentId,
  22 + '', '', '', 0
  23 +);
  24 +INSERT INTO system_menu(
  25 + name, permission, type, sort, parent_id,
  26 + path, icon, component, status
  27 +)
  28 +VALUES (
  29 + '学生创建', 'infra:student:create', 3, 2, @parentId,
  30 + '', '', '', 0
  31 +);
  32 +INSERT INTO system_menu(
  33 + name, permission, type, sort, parent_id,
  34 + path, icon, component, status
  35 +)
  36 +VALUES (
  37 + '学生更新', 'infra:student:update', 3, 3, @parentId,
  38 + '', '', '', 0
  39 +);
  40 +INSERT INTO system_menu(
  41 + name, permission, type, sort, parent_id,
  42 + path, icon, component, status
  43 +)
  44 +VALUES (
  45 + '学生删除', 'infra:student:delete', 3, 4, @parentId,
  46 + '', '', '', 0
  47 +);
  48 +INSERT INTO system_menu(
  49 + name, permission, type, sort, parent_id,
  50 + path, icon, component, status
  51 +)
  52 +VALUES (
  53 + '学生导出', 'infra:student:export', 3, 5, @parentId,
  54 + '', '', '', 0
  55 +);
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/vue/StudentContactForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="简介" prop="description">
  10 + <el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
  11 + </el-form-item>
  12 + <el-form-item label="出生日期" prop="birthday">
  13 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  14 + </el-form-item>
  15 + <el-form-item label="性别" prop="sex">
  16 + <el-select v-model="formData.sex" placeholder="请选择性别">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  18 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="是否有效" prop="enabled">
  22 + <el-radio-group v-model="formData.enabled">
  23 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  24 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  25 + </el-radio-group>
  26 + </el-form-item>
  27 + <el-form-item label="头像" prop="avatar">
  28 + <ImageUpload v-model="formData.avatar"/>
  29 + </el-form-item>
  30 + <el-form-item label="附件" prop="video">
  31 + <FileUpload v-model="formData.video"/>
  32 + </el-form-item>
  33 + <el-form-item label="备注" prop="memo">
  34 + <editor v-model="formData.memo" :min-height="192"/>
  35 + </el-form-item>
  36 + </el-form>
  37 + <div slot="footer" class="dialog-footer">
  38 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  39 + <el-button @click="dialogVisible = false">取 消</el-button>
  40 + </div>
  41 + </el-dialog>
  42 + </div>
  43 +</template>
  44 +
  45 +<script>
  46 + import * as StudentApi from '@/api/infra/demo'
  47 + import ImageUpload from '@/components/ImageUpload';
  48 + import FileUpload from '@/components/FileUpload';
  49 + import Editor from '@/components/Editor';
  50 + export default {
  51 + name: "StudentContactForm",
  52 + components: {
  53 + ImageUpload,
  54 + FileUpload,
  55 + Editor,
  56 + },
  57 + data() {
  58 + return {
  59 + // 弹出层标题
  60 + dialogTitle: "",
  61 + // 是否显示弹出层
  62 + dialogVisible: false,
  63 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  64 + formLoading: false,
  65 + // 表单参数
  66 + formData: {
  67 + id: undefined,
  68 + studentId: undefined,
  69 + name: undefined,
  70 + description: undefined,
  71 + birthday: undefined,
  72 + sex: undefined,
  73 + enabled: undefined,
  74 + avatar: undefined,
  75 + video: undefined,
  76 + memo: undefined,
  77 + },
  78 + // 表单校验
  79 + formRules: {
  80 + studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
  81 + name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
  82 + description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
  83 + birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
  84 + sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
  85 + enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
  86 + avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
  87 + memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
  88 + },
  89 + };
  90 + },
  91 + methods: {
  92 + /** 打开弹窗 */
  93 + open(id, studentId) {
  94 + this.dialogVisible = true;
  95 + this.reset();
  96 + const that = this;
  97 + this.formData.studentId = studentId;
  98 + // 修改时,设置数据
  99 + if (id) {
  100 + this.formLoading = true;
  101 + try {
  102 + StudentApi.getStudentContact(id).then(res=>{
  103 + that.formData = res.data;
  104 + that.dialogTitle = "修改学生联系人";
  105 + })
  106 + } finally {
  107 + this.formLoading = false;
  108 + }
  109 + }
  110 + this.dialogTitle = "新增学生联系人";
  111 + },
  112 + /** 提交按钮 */
  113 + submitForm() {
  114 + this.formLoading = true;
  115 + try {
  116 + let data = this.formData;
  117 + this.$refs["formRef"].validate(valid => {
  118 + if (!valid) {
  119 + return;
  120 + }
  121 + // 修改的提交
  122 + if (data.id) {
  123 + StudentApi.updateStudentContact(data).then(response => {
  124 + this.$modal.msgSuccess("修改成功");
  125 + this.dialogVisible = false;
  126 + this.$emit('success');
  127 + });
  128 + return;
  129 + }
  130 + // 添加的提交
  131 + StudentApi.createStudentContact(data).then(response => {
  132 + this.$modal.msgSuccess("新增成功");
  133 + this.dialogVisible = false;
  134 + this.$emit('success');
  135 + });
  136 + });
  137 + }finally {
  138 + this.formLoading = false
  139 + }
  140 + },
  141 + /** 表单重置 */
  142 + reset() {
  143 + this.formData = {
  144 + id: undefined,
  145 + studentId: undefined,
  146 + name: undefined,
  147 + description: undefined,
  148 + birthday: undefined,
  149 + sex: undefined,
  150 + enabled: undefined,
  151 + avatar: undefined,
  152 + video: undefined,
  153 + memo: undefined,
  154 + };
  155 + this.resetForm("formRef");
  156 + },
  157 + }
  158 + };
  159 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/vue/StudentContactList 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 操作工具栏 -->
  4 + <el-row :gutter="10" class="mb8">
  5 + <el-col :span="1.5">
  6 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  7 + v-hasPermi="['infra:student:create']">新增</el-button>
  8 + </el-col>
  9 + </el-row>
  10 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  11 + <el-table-column label="编号" align="center" prop="id" />
  12 + <el-table-column label="名字" align="center" prop="name" />
  13 + <el-table-column label="简介" align="center" prop="description" />
  14 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  15 + <template v-slot="scope">
  16 + <span>{{ parseTime(scope.row.birthday) }}</span>
  17 + </template>
  18 + </el-table-column>
  19 + <el-table-column label="性别" align="center" prop="sex">
  20 + <template v-slot="scope">
  21 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  22 + </template>
  23 + </el-table-column>
  24 + <el-table-column label="是否有效" align="center" prop="enabled">
  25 + <template v-slot="scope">
  26 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  27 + </template>
  28 + </el-table-column>
  29 + <el-table-column label="头像" align="center" prop="avatar" />
  30 + <el-table-column label="附件" align="center" prop="video" />
  31 + <el-table-column label="备注" align="center" prop="memo" />
  32 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  33 + <template v-slot="scope">
  34 + <span>{{ parseTime(scope.row.createTime) }}</span>
  35 + </template>
  36 + </el-table-column>
  37 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  38 + <template v-slot="scope">
  39 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  40 + v-hasPermi="['infra:student:update']">修改</el-button>
  41 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  42 + v-hasPermi="['infra:student:delete']">删除</el-button>
  43 + </template>
  44 + </el-table-column>
  45 + </el-table>
  46 + <!-- 分页组件 -->
  47 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  48 + @pagination="getList"/>
  49 + <!-- 对话框(添加 / 修改) -->
  50 + <StudentContactForm ref="formRef" @success="getList" />
  51 + </div>
  52 +</template>
  53 +
  54 +<script>
  55 + import * as StudentApi from '@/api/infra/demo'
  56 + import StudentContactForm from './StudentContactForm.vue'
  57 + export default {
  58 + name: "StudentContactList",
  59 + components: {
  60 + StudentContactForm
  61 + },
  62 + props:[
  63 + 'studentId'
  64 + ],// 学生编号(主表的关联字段)
  65 + data() {
  66 + return {
  67 + // 遮罩层
  68 + loading: true,
  69 + // 列表的数据
  70 + list: [],
  71 + // 列表的总页数
  72 + total: 0,
  73 + // 查询参数
  74 + queryParams: {
  75 + pageNo: 1,
  76 + pageSize: 10,
  77 + studentId: undefined
  78 + }
  79 + };
  80 + },
  81 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  82 + studentId:{
  83 + handler(val) {
  84 + this.queryParams.studentId = val;
  85 + if (val){
  86 + this.handleQuery();
  87 + }
  88 + },
  89 + immediate: true
  90 + }
  91 + },
  92 + methods: {
  93 + /** 查询列表 */
  94 + getList() {
  95 + try {
  96 + this.loading = true;
  97 + const that = this;
  98 + StudentApi.getStudentContactPage(this.queryParams).then(response => {
  99 + that.list = response.data.list;
  100 + that.total = response.data.total;
  101 + });
  102 + } finally {
  103 + this.loading = false;
  104 + }
  105 + },
  106 + /** 搜索按钮操作 */
  107 + handleQuery() {
  108 + this.queryParams.pageNo = 1;
  109 + this.getList();
  110 + },
  111 + /** 添加/修改操作 */
  112 + openForm(id) {
  113 + if (!this.studentId) {
  114 + that.$modal.msgError('请选择一个学生');
  115 + return;
  116 + }
  117 + this.$refs["formRef"].open(id, this.studentId);
  118 + },
  119 + /** 删除按钮操作 */
  120 + handleDelete(row) {
  121 + const that = this;
  122 + try {
  123 + const id = row.id;
  124 + this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
  125 + return StudentApi.deleteStudentContact(id);
  126 + }).then(() => {
  127 + that.getList();
  128 + that.$modal.msgSuccess("删除成功");
  129 + }).catch(() => {});
  130 + } catch {}
  131 + },
  132 + }
  133 + };
  134 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/vue/StudentForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="简介" prop="description">
  10 + <el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
  11 + </el-form-item>
  12 + <el-form-item label="出生日期" prop="birthday">
  13 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  14 + </el-form-item>
  15 + <el-form-item label="性别" prop="sex">
  16 + <el-select v-model="formData.sex" placeholder="请选择性别">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  18 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="是否有效" prop="enabled">
  22 + <el-radio-group v-model="formData.enabled">
  23 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  24 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  25 + </el-radio-group>
  26 + </el-form-item>
  27 + <el-form-item label="头像">
  28 + <ImageUpload v-model="formData.avatar"/>
  29 + </el-form-item>
  30 + <el-form-item label="附件">
  31 + <FileUpload v-model="formData.video"/>
  32 + </el-form-item>
  33 + <el-form-item label="备注">
  34 + <Editor v-model="formData.memo" :min-height="192"/>
  35 + </el-form-item>
  36 + </el-form>
  37 + <div slot="footer" class="dialog-footer">
  38 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  39 + <el-button @click="dialogVisible = false">取 消</el-button>
  40 + </div>
  41 + </el-dialog>
  42 + </div>
  43 +</template>
  44 +
  45 +<script>
  46 + import * as StudentApi from '@/api/infra/demo'
  47 + import ImageUpload from '@/components/ImageUpload';
  48 + import FileUpload from '@/components/FileUpload';
  49 + import Editor from '@/components/Editor';
  50 + export default {
  51 + name: "StudentForm",
  52 + components: {
  53 + ImageUpload,
  54 + FileUpload,
  55 + Editor,
  56 + },
  57 + data() {
  58 + return {
  59 + // 弹出层标题
  60 + dialogTitle: "",
  61 + // 是否显示弹出层
  62 + dialogVisible: false,
  63 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  64 + formLoading: false,
  65 + // 表单参数
  66 + formData: {
  67 + id: undefined,
  68 + name: undefined,
  69 + description: undefined,
  70 + birthday: undefined,
  71 + sex: undefined,
  72 + enabled: undefined,
  73 + avatar: undefined,
  74 + video: undefined,
  75 + memo: undefined,
  76 + },
  77 + // 表单校验
  78 + formRules: {
  79 + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
  80 + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
  81 + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
  82 + sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
  83 + enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
  84 + avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
  85 + video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
  86 + memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
  87 + },
  88 + };
  89 + },
  90 + methods: {
  91 + /** 打开弹窗 */
  92 + open(id) {
  93 + this.dialogVisible = true;
  94 + this.reset();
  95 + const that = this;
  96 + // 修改时,设置数据
  97 + if (id) {
  98 + this.formLoading = true;
  99 + try {
  100 + StudentApi.getStudent(id).then(res=>{
  101 + that.formData = res.data;
  102 + that.title = "修改学生";
  103 + })
  104 + } finally {
  105 + this.formLoading = false;
  106 + }
  107 + }
  108 + this.title = "新增学生";
  109 + },
  110 + /** 提交按钮 */
  111 + submitForm() {
  112 + this.formLoading = true;
  113 + try {
  114 + const that = this;
  115 + let data = this.formData;
  116 + let validate = false;
  117 + // 校验主表
  118 + this.getRef("formRef").validate(valid => {
  119 + validate = valid;
  120 + });
  121 + // 所有表单校验通过后方可提交
  122 + if (!validate) {
  123 + return;
  124 + }
  125 + // 修改的提交
  126 + if (data.id) {
  127 + StudentApi.updateStudent(data).then(response => {
  128 + that.$modal.msgSuccess("修改成功");
  129 + that.dialogVisible = false;
  130 + that.$emit('success');
  131 + });
  132 + return;
  133 + }
  134 + // 添加的提交
  135 + StudentApi.createStudent(data).then(response => {
  136 + that.$modal.msgSuccess("新增成功");
  137 + that.dialogVisible = false;
  138 + that.$emit('success');
  139 + });
  140 + }finally {
  141 + this.formLoading = false;
  142 + }
  143 + },
  144 + getRef(refName){
  145 + return this.$refs[refName];
  146 + },
  147 + /** 表单重置 */
  148 + reset() {
  149 + this.formData = {
  150 + id: undefined,
  151 + name: undefined,
  152 + description: undefined,
  153 + birthday: undefined,
  154 + sex: undefined,
  155 + enabled: undefined,
  156 + avatar: undefined,
  157 + video: undefined,
  158 + memo: undefined,
  159 + };
  160 + this.resetForm("formRef");
  161 + },
  162 + }
  163 + };
  164 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/vue/StudentTeacherForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="简介" prop="description">
  10 + <el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
  11 + </el-form-item>
  12 + <el-form-item label="出生日期" prop="birthday">
  13 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  14 + </el-form-item>
  15 + <el-form-item label="性别" prop="sex">
  16 + <el-select v-model="formData.sex" placeholder="请选择性别">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  18 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="是否有效" prop="enabled">
  22 + <el-radio-group v-model="formData.enabled">
  23 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  24 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  25 + </el-radio-group>
  26 + </el-form-item>
  27 + <el-form-item label="头像" prop="avatar">
  28 + <ImageUpload v-model="formData.avatar"/>
  29 + </el-form-item>
  30 + <el-form-item label="附件" prop="video">
  31 + <FileUpload v-model="formData.video"/>
  32 + </el-form-item>
  33 + <el-form-item label="备注" prop="memo">
  34 + <editor v-model="formData.memo" :min-height="192"/>
  35 + </el-form-item>
  36 + </el-form>
  37 + <div slot="footer" class="dialog-footer">
  38 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  39 + <el-button @click="dialogVisible = false">取 消</el-button>
  40 + </div>
  41 + </el-dialog>
  42 + </div>
  43 +</template>
  44 +
  45 +<script>
  46 + import * as StudentApi from '@/api/infra/demo'
  47 + import ImageUpload from '@/components/ImageUpload';
  48 + import FileUpload from '@/components/FileUpload';
  49 + import Editor from '@/components/Editor';
  50 + export default {
  51 + name: "StudentTeacherForm",
  52 + components: {
  53 + ImageUpload,
  54 + FileUpload,
  55 + Editor,
  56 + },
  57 + data() {
  58 + return {
  59 + // 弹出层标题
  60 + dialogTitle: "",
  61 + // 是否显示弹出层
  62 + dialogVisible: false,
  63 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  64 + formLoading: false,
  65 + // 表单参数
  66 + formData: {
  67 + id: undefined,
  68 + studentId: undefined,
  69 + name: undefined,
  70 + description: undefined,
  71 + birthday: undefined,
  72 + sex: undefined,
  73 + enabled: undefined,
  74 + avatar: undefined,
  75 + video: undefined,
  76 + memo: undefined,
  77 + },
  78 + // 表单校验
  79 + formRules: {
  80 + studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
  81 + name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
  82 + description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
  83 + birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
  84 + sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
  85 + enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
  86 + avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
  87 + memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
  88 + },
  89 + };
  90 + },
  91 + methods: {
  92 + /** 打开弹窗 */
  93 + open(id, studentId) {
  94 + this.dialogVisible = true;
  95 + this.reset();
  96 + const that = this;
  97 + this.formData.studentId = studentId;
  98 + // 修改时,设置数据
  99 + if (id) {
  100 + this.formLoading = true;
  101 + try {
  102 + StudentApi.getStudentTeacher(id).then(res=>{
  103 + that.formData = res.data;
  104 + that.dialogTitle = "修改学生班主任";
  105 + })
  106 + } finally {
  107 + this.formLoading = false;
  108 + }
  109 + }
  110 + this.dialogTitle = "新增学生班主任";
  111 + },
  112 + /** 提交按钮 */
  113 + submitForm() {
  114 + this.formLoading = true;
  115 + try {
  116 + let data = this.formData;
  117 + this.$refs["formRef"].validate(valid => {
  118 + if (!valid) {
  119 + return;
  120 + }
  121 + // 修改的提交
  122 + if (data.id) {
  123 + StudentApi.updateStudentTeacher(data).then(response => {
  124 + this.$modal.msgSuccess("修改成功");
  125 + this.dialogVisible = false;
  126 + this.$emit('success');
  127 + });
  128 + return;
  129 + }
  130 + // 添加的提交
  131 + StudentApi.createStudentTeacher(data).then(response => {
  132 + this.$modal.msgSuccess("新增成功");
  133 + this.dialogVisible = false;
  134 + this.$emit('success');
  135 + });
  136 + });
  137 + }finally {
  138 + this.formLoading = false
  139 + }
  140 + },
  141 + /** 表单重置 */
  142 + reset() {
  143 + this.formData = {
  144 + id: undefined,
  145 + studentId: undefined,
  146 + name: undefined,
  147 + description: undefined,
  148 + birthday: undefined,
  149 + sex: undefined,
  150 + enabled: undefined,
  151 + avatar: undefined,
  152 + video: undefined,
  153 + memo: undefined,
  154 + };
  155 + this.resetForm("formRef");
  156 + },
  157 + }
  158 + };
  159 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/vue/StudentTeacherList 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 操作工具栏 -->
  4 + <el-row :gutter="10" class="mb8">
  5 + <el-col :span="1.5">
  6 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  7 + v-hasPermi="['infra:student:create']">新增</el-button>
  8 + </el-col>
  9 + </el-row>
  10 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  11 + <el-table-column label="编号" align="center" prop="id" />
  12 + <el-table-column label="名字" align="center" prop="name" />
  13 + <el-table-column label="简介" align="center" prop="description" />
  14 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  15 + <template v-slot="scope">
  16 + <span>{{ parseTime(scope.row.birthday) }}</span>
  17 + </template>
  18 + </el-table-column>
  19 + <el-table-column label="性别" align="center" prop="sex">
  20 + <template v-slot="scope">
  21 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  22 + </template>
  23 + </el-table-column>
  24 + <el-table-column label="是否有效" align="center" prop="enabled">
  25 + <template v-slot="scope">
  26 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  27 + </template>
  28 + </el-table-column>
  29 + <el-table-column label="头像" align="center" prop="avatar" />
  30 + <el-table-column label="附件" align="center" prop="video" />
  31 + <el-table-column label="备注" align="center" prop="memo" />
  32 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  33 + <template v-slot="scope">
  34 + <span>{{ parseTime(scope.row.createTime) }}</span>
  35 + </template>
  36 + </el-table-column>
  37 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  38 + <template v-slot="scope">
  39 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  40 + v-hasPermi="['infra:student:update']">修改</el-button>
  41 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  42 + v-hasPermi="['infra:student:delete']">删除</el-button>
  43 + </template>
  44 + </el-table-column>
  45 + </el-table>
  46 + <!-- 分页组件 -->
  47 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  48 + @pagination="getList"/>
  49 + <!-- 对话框(添加 / 修改) -->
  50 + <StudentTeacherForm ref="formRef" @success="getList" />
  51 + </div>
  52 +</template>
  53 +
  54 +<script>
  55 + import * as StudentApi from '@/api/infra/demo'
  56 + import StudentTeacherForm from './StudentTeacherForm.vue'
  57 + export default {
  58 + name: "StudentTeacherList",
  59 + components: {
  60 + StudentTeacherForm
  61 + },
  62 + props:[
  63 + 'studentId'
  64 + ],// 学生编号(主表的关联字段)
  65 + data() {
  66 + return {
  67 + // 遮罩层
  68 + loading: true,
  69 + // 列表的数据
  70 + list: [],
  71 + // 列表的总页数
  72 + total: 0,
  73 + // 查询参数
  74 + queryParams: {
  75 + pageNo: 1,
  76 + pageSize: 10,
  77 + studentId: undefined
  78 + }
  79 + };
  80 + },
  81 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  82 + studentId:{
  83 + handler(val) {
  84 + this.queryParams.studentId = val;
  85 + if (val){
  86 + this.handleQuery();
  87 + }
  88 + },
  89 + immediate: true
  90 + }
  91 + },
  92 + methods: {
  93 + /** 查询列表 */
  94 + getList() {
  95 + try {
  96 + this.loading = true;
  97 + const that = this;
  98 + StudentApi.getStudentTeacherPage(this.queryParams).then(response => {
  99 + that.list = response.data.list;
  100 + that.total = response.data.total;
  101 + });
  102 + } finally {
  103 + this.loading = false;
  104 + }
  105 + },
  106 + /** 搜索按钮操作 */
  107 + handleQuery() {
  108 + this.queryParams.pageNo = 1;
  109 + this.getList();
  110 + },
  111 + /** 添加/修改操作 */
  112 + openForm(id) {
  113 + if (!this.studentId) {
  114 + that.$modal.msgError('请选择一个学生');
  115 + return;
  116 + }
  117 + this.$refs["formRef"].open(id, this.studentId);
  118 + },
  119 + /** 删除按钮操作 */
  120 + handleDelete(row) {
  121 + const that = this;
  122 + try {
  123 + const id = row.id;
  124 + this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
  125 + return StudentApi.deleteStudentTeacher(id);
  126 + }).then(() => {
  127 + that.getList();
  128 + that.$modal.msgSuccess("删除成功");
  129 + }).catch(() => {});
  130 + } catch {}
  131 + },
  132 + }
  133 + };
  134 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/vue/index 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="出生日期" prop="birthday">
  10 + <el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
  11 + </el-form-item>
  12 + <el-form-item label="性别" prop="sex">
  13 + <el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
  14 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  15 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item label="是否有效" prop="enabled">
  19 + <el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
  20 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  21 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  22 + </el-select>
  23 + </el-form-item>
  24 + <el-form-item label="创建时间" prop="createTime">
  25 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  26 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  27 + </el-form-item>
  28 + <el-form-item>
  29 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  30 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  31 + </el-form-item>
  32 + </el-form>
  33 +
  34 + <!-- 操作工具栏 -->
  35 + <el-row :gutter="10" class="mb8">
  36 + <el-col :span="1.5">
  37 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  38 + v-hasPermi="['infra:student:create']">新增</el-button>
  39 + </el-col>
  40 + <el-col :span="1.5">
  41 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  42 + v-hasPermi="['infra:student:export']">导出</el-button>
  43 + </el-col>
  44 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  45 + </el-row>
  46 +
  47 + <el-table
  48 + v-loading="loading"
  49 + :data="list"
  50 + :stripe="true"
  51 + :highlight-current-row="true"
  52 + :show-overflow-tooltip="true"
  53 + @current-change="handleCurrentChange"
  54 + >
  55 + <el-table-column label="编号" align="center" prop="id">
  56 + <template v-slot="scope">
  57 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
  58 + </template>
  59 + </el-table-column>
  60 + <el-table-column label="名字" align="center" prop="name">
  61 + <template v-slot="scope">
  62 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
  63 + </template>
  64 + </el-table-column>
  65 + <el-table-column label="简介" align="center" prop="description">
  66 + <template v-slot="scope">
  67 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
  68 + </template>
  69 + </el-table-column>
  70 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  71 + <template v-slot="scope">
  72 + <span>{{ parseTime(scope.row.birthday) }}</span>
  73 + </template>
  74 + </el-table-column>
  75 + <el-table-column label="性别" align="center" prop="sex">
  76 + <template v-slot="scope">
  77 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  78 + </template>
  79 + </el-table-column>
  80 + <el-table-column label="是否有效" align="center" prop="enabled">
  81 + <template v-slot="scope">
  82 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  83 + </template>
  84 + </el-table-column>
  85 + <el-table-column label="头像" align="center" prop="avatar">
  86 + <template v-slot="scope">
  87 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
  88 + </template>
  89 + </el-table-column>
  90 + <el-table-column label="附件" align="center" prop="video">
  91 + <template v-slot="scope">
  92 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
  93 + </template>
  94 + </el-table-column>
  95 + <el-table-column label="备注" align="center" prop="memo">
  96 + <template v-slot="scope">
  97 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
  98 + </template>
  99 + </el-table-column>
  100 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  101 + <template v-slot="scope">
  102 + <span>{{ parseTime(scope.row.createTime) }}</span>
  103 + </template>
  104 + </el-table-column>
  105 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  106 + <template v-slot="scope">
  107 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  108 + v-hasPermi="['infra:student:update']">修改</el-button>
  109 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  110 + v-hasPermi="['infra:student:delete']">删除</el-button>
  111 + </template>
  112 + </el-table-column>
  113 + </el-table>
  114 + <!-- 分页组件 -->
  115 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  116 + @pagination="getList"/>
  117 + <!-- 对话框(添加 / 修改) -->
  118 + <StudentForm ref="formRef" @success="getList" />
  119 + <!-- 子表的列表 -->
  120 + <el-tabs v-model="subTabsName">
  121 + <el-tab-pane label="学生联系人" name="studentContact">
  122 + <StudentContactList v-if="currentRow.id" :student-id="currentRow.id" />
  123 + </el-tab-pane>
  124 + <el-tab-pane label="学生班主任" name="studentTeacher">
  125 + <StudentTeacherList v-if="currentRow.id" :student-id="currentRow.id" />
  126 + </el-tab-pane>
  127 + </el-tabs>
  128 + </div>
  129 +</template>
  130 +
  131 +<script>
  132 +import * as StudentApi from '@/api/infra/demo';
  133 +import StudentForm from './StudentForm.vue';
  134 + import StudentContactList from './components/StudentContactList.vue'
  135 + import StudentTeacherList from './components/StudentTeacherList.vue'
  136 +export default {
  137 + name: "Student",
  138 + components: {
  139 + StudentForm,
  140 + StudentContactList,
  141 + StudentTeacherList,
  142 + },
  143 + data() {
  144 + return {
  145 + // 遮罩层
  146 + loading: true,
  147 + // 导出遮罩层
  148 + exportLoading: false,
  149 + // 显示搜索条件
  150 + showSearch: true,
  151 + // 总条数
  152 + total: 0,
  153 + // 学生列表
  154 + list: [],
  155 + // 是否展开,默认全部展开
  156 + isExpandAll: true,
  157 + // 重新渲染表格状态
  158 + refreshTable: true,
  159 + // 选中行
  160 + currentRow: {},
  161 + // 查询参数
  162 + queryParams: {
  163 + pageNo: 1,
  164 + pageSize: 10,
  165 + name: null,
  166 + birthday: null,
  167 + sex: null,
  168 + enabled: null,
  169 + createTime: [],
  170 + },
  171 + /** 子表的列表 */
  172 + subTabsName: 'studentContact'
  173 + };
  174 + },
  175 + created() {
  176 + this.getList();
  177 + },
  178 + methods: {
  179 + /** 查询列表 */
  180 + getList() {
  181 + try {
  182 + this.loading = true;
  183 + StudentApi.getStudentPage(this.queryParams).then(response => {
  184 + this.list = response.data.list;
  185 + this.total = response.data.total;
  186 + });
  187 + } finally {
  188 + this.loading = false;
  189 + }
  190 + },
  191 + /** 搜索按钮操作 */
  192 + handleQuery() {
  193 + this.queryParams.pageNo = 1;
  194 + this.getList();
  195 + },
  196 + /** 重置按钮操作 */
  197 + resetQuery() {
  198 + this.resetForm("queryForm");
  199 + this.handleQuery();
  200 + },
  201 + /** 添加/修改操作 */
  202 + openForm(id) {
  203 + this.$refs["formRef"].open(id);
  204 + },
  205 + /** 删除按钮操作 */
  206 + handleDelete(row) {
  207 + const that = this;
  208 + try {
  209 + const id = row.id;
  210 + this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
  211 + return StudentApi.deleteStudent(id);
  212 + }).then(() => {
  213 + that.getList();
  214 + that.$modal.msgSuccess("删除成功");
  215 + }).catch(() => {});
  216 + } catch {}
  217 + },
  218 + /** 导出按钮操作 */
  219 + handleExport() {
  220 + const that = this;
  221 + try {
  222 + this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
  223 + that.exportLoading = true;
  224 + return StudentApi.exportStudentExcel(params);
  225 + }).then(response => {
  226 + that.$download.excel(response, '学生.xls');
  227 + });
  228 + } catch {
  229 + } finally {
  230 + that.exportLoading = false;
  231 + }
  232 + },
  233 + /** 选中行操作 */
  234 + handleCurrentChange(row) {
  235 + this.currentRow = row;
  236 + /** 子表的列表 */
  237 + this.subTabsName = 'studentContact';
  238 + },
  239 + }
  240 +};
  241 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_erp/xml/InfraStudentMapper 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/assert.json 0 → 100644
  1 +[ {
  2 + "contentPath" : "java/InfraStudentPageReqVO",
  3 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
  4 +}, {
  5 + "contentPath" : "java/InfraStudentRespVO",
  6 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
  7 +}, {
  8 + "contentPath" : "java/InfraStudentSaveReqVO",
  9 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
  10 +}, {
  11 + "contentPath" : "java/InfraStudentController",
  12 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
  13 +}, {
  14 + "contentPath" : "java/InfraStudentDO",
  15 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
  16 +}, {
  17 + "contentPath" : "java/InfraStudentContactDO",
  18 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
  19 +}, {
  20 + "contentPath" : "java/InfraStudentTeacherDO",
  21 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
  22 +}, {
  23 + "contentPath" : "java/InfraStudentMapper",
  24 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
  25 +}, {
  26 + "contentPath" : "java/InfraStudentContactMapper",
  27 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
  28 +}, {
  29 + "contentPath" : "java/InfraStudentTeacherMapper",
  30 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
  31 +}, {
  32 + "contentPath" : "xml/InfraStudentMapper",
  33 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
  34 +}, {
  35 + "contentPath" : "java/InfraStudentServiceImpl",
  36 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
  37 +}, {
  38 + "contentPath" : "java/InfraStudentService",
  39 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
  40 +}, {
  41 + "contentPath" : "java/InfraStudentServiceImplTest",
  42 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
  43 +}, {
  44 + "contentPath" : "java/ErrorCodeConstants_手动操作",
  45 + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
  46 +}, {
  47 + "contentPath" : "sql/sql",
  48 + "filePath" : "sql/sql.sql"
  49 +}, {
  50 + "contentPath" : "sql/h2",
  51 + "filePath" : "sql/h2.sql"
  52 +}, {
  53 + "contentPath" : "vue/index",
  54 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
  55 +}, {
  56 + "contentPath" : "js/student",
  57 + "filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
  58 +}, {
  59 + "contentPath" : "vue/StudentForm",
  60 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
  61 +}, {
  62 + "contentPath" : "vue/StudentContactForm",
  63 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
  64 +}, {
  65 + "contentPath" : "vue/StudentTeacherForm",
  66 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
  67 +}, {
  68 + "contentPath" : "vue/StudentContactList",
  69 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue"
  70 +}, {
  71 + "contentPath" : "vue/StudentTeacherList",
  72 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue"
  73 +} ]
0 \ No newline at end of file 74 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/ErrorCodeConstants_手动操作 0 → 100644
  1 +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
  2 +// ========== 学生 TODO 补充编号 ==========
  3 +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
0 \ No newline at end of file 4 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentContactDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生联系人 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student_contact")
  16 +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentContactDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 学生编号
  32 + */
  33 + private Long studentId;
  34 + /**
  35 + * 名字
  36 + */
  37 + private String name;
  38 + /**
  39 + * 简介
  40 + */
  41 + private String description;
  42 + /**
  43 + * 出生日期
  44 + */
  45 + private LocalDateTime birthday;
  46 + /**
  47 + * 性别
  48 + *
  49 + * 枚举 {@link TODO system_user_sex 对应的类}
  50 + */
  51 + private Integer sex;
  52 + /**
  53 + * 是否有效
  54 + *
  55 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  56 + */
  57 + private Boolean enabled;
  58 + /**
  59 + * 头像
  60 + */
  61 + private String avatar;
  62 + /**
  63 + * 附件
  64 + */
  65 + private String video;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String memo;
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentContactMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +/**
  13 + * 学生联系人 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
  19 +
  20 + default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {
  21 + return selectList(InfraStudentContactDO::getStudentId, studentId);
  22 + }
  23 +
  24 + default int deleteByStudentId(Long studentId) {
  25 + return delete(InfraStudentContactDO::getStudentId, studentId);
  26 + }
  27 +
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentController 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import javax.validation.constraints.*;
  12 +import javax.validation.*;
  13 +import javax.servlet.http.*;
  14 +import java.util.*;
  15 +import java.io.IOException;
  16 +
  17 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  18 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  19 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  20 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  24 +
  25 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
  27 +
  28 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  29 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  30 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  31 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  32 +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
  33 +
  34 +@Tag(name = "管理后台 - 学生")
  35 +@RestController
  36 +@RequestMapping("/infra/student")
  37 +@Validated
  38 +public class InfraStudentController {
  39 +
  40 + @Resource
  41 + private InfraStudentService studentService;
  42 +
  43 + @PostMapping("/create")
  44 + @Operation(summary = "创建学生")
  45 + @PreAuthorize("@ss.hasPermission('infra:student:create')")
  46 + public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
  47 + return success(studentService.createStudent(createReqVO));
  48 + }
  49 +
  50 + @PutMapping("/update")
  51 + @Operation(summary = "更新学生")
  52 + @PreAuthorize("@ss.hasPermission('infra:student:update')")
  53 + public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
  54 + studentService.updateStudent(updateReqVO);
  55 + return success(true);
  56 + }
  57 +
  58 + @DeleteMapping("/delete")
  59 + @Operation(summary = "删除学生")
  60 + @Parameter(name = "id", description = "编号", required = true)
  61 + @PreAuthorize("@ss.hasPermission('infra:student:delete')")
  62 + public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
  63 + studentService.deleteStudent(id);
  64 + return success(true);
  65 + }
  66 +
  67 + @GetMapping("/get")
  68 + @Operation(summary = "获得学生")
  69 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  70 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  71 + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
  72 + InfraStudentDO student = studentService.getStudent(id);
  73 + return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/page")
  77 + @Operation(summary = "获得学生分页")
  78 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  79 + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
  80 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
  81 + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
  82 + }
  83 +
  84 + @GetMapping("/export-excel")
  85 + @Operation(summary = "导出学生 Excel")
  86 + @PreAuthorize("@ss.hasPermission('infra:student:export')")
  87 + @OperateLog(type = EXPORT)
  88 + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
  89 + HttpServletResponse response) throws IOException {
  90 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  91 + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
  92 + // 导出 Excel
  93 + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
  94 + BeanUtils.toBean(list, InfraStudentRespVO.class));
  95 + }
  96 +
  97 + // ==================== 子表(学生联系人) ====================
  98 +
  99 + @GetMapping("/student-contact/list-by-student-id")
  100 + @Operation(summary = "获得学生联系人列表")
  101 + @Parameter(name = "studentId", description = "学生编号")
  102 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  103 + public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
  104 + return success(studentService.getStudentContactListByStudentId(studentId));
  105 + }
  106 +
  107 + // ==================== 子表(学生班主任) ====================
  108 +
  109 + @GetMapping("/student-teacher/get-by-student-id")
  110 + @Operation(summary = "获得学生班主任")
  111 + @Parameter(name = "studentId", description = "学生编号")
  112 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  113 + public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
  114 + return success(studentService.getStudentTeacherByStudentId(studentId));
  115 + }
  116 +
  117 +}
0 \ No newline at end of file 118 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student")
  16 +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 名字
  32 + */
  33 + private String name;
  34 + /**
  35 + * 简介
  36 + */
  37 + private String description;
  38 + /**
  39 + * 出生日期
  40 + */
  41 + private LocalDateTime birthday;
  42 + /**
  43 + * 性别
  44 + *
  45 + * 枚举 {@link TODO system_user_sex 对应的类}
  46 + */
  47 + private Integer sex;
  48 + /**
  49 + * 是否有效
  50 + *
  51 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  52 + */
  53 + private Boolean enabled;
  54 + /**
  55 + * 头像
  56 + */
  57 + private String avatar;
  58 + /**
  59 + * 附件
  60 + */
  61 + private String video;
  62 + /**
  63 + * 备注
  64 + */
  65 + private String memo;
  66 +
  67 +}
0 \ No newline at end of file 68 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  11 +
  12 +/**
  13 + * 学生 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
  19 +
  20 + default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
  22 + .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
  23 + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
  24 + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
  25 + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
  26 + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(InfraStudentDO::getId));
  28 + }
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentPageReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - 学生分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class InfraStudentPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "名字", example = "芋头")
  19 + private String name;
  20 +
  21 + @Schema(description = "出生日期")
  22 + private LocalDateTime birthday;
  23 +
  24 + @Schema(description = "性别", example = "1")
  25 + private Integer sex;
  26 +
  27 + @Schema(description = "是否有效", example = "true")
  28 + private Boolean enabled;
  29 +
  30 + @Schema(description = "创建时间")
  31 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  32 + private LocalDateTime[] createTime;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentRespVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import java.util.*;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +import com.alibaba.excel.annotation.*;
  10 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  11 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  12 +
  13 +@Schema(description = "管理后台 - 学生 Response VO")
  14 +@Data
  15 +@ExcelIgnoreUnannotated
  16 +public class InfraStudentRespVO {
  17 +
  18 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @ExcelProperty("编号")
  20 + private Long id;
  21 +
  22 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  23 + @ExcelProperty("名字")
  24 + private String name;
  25 +
  26 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  27 + @ExcelProperty("简介")
  28 + private String description;
  29 +
  30 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @ExcelProperty("出生日期")
  32 + private LocalDateTime birthday;
  33 +
  34 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + @ExcelProperty(value = "性别", converter = DictConvert.class)
  36 + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  37 + private Integer sex;
  38 +
  39 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  40 + @ExcelProperty(value = "是否有效", converter = DictConvert.class)
  41 + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  42 + private Boolean enabled;
  43 +
  44 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  45 + @ExcelProperty("头像")
  46 + private String avatar;
  47 +
  48 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  49 + @ExcelProperty("附件")
  50 + private String video;
  51 +
  52 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  53 + @ExcelProperty("备注")
  54 + private String memo;
  55 +
  56 + @Schema(description = "创建时间")
  57 + @ExcelProperty("创建时间")
  58 + private LocalDateTime createTime;
  59 +
  60 +}
0 \ No newline at end of file 61 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentSaveReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import javax.validation.constraints.*;
  7 +import java.util.*;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +import java.time.LocalDateTime;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  12 +
  13 +@Schema(description = "管理后台 - 学生新增/修改 Request VO")
  14 +@Data
  15 +public class InfraStudentSaveReqVO {
  16 +
  17 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + private Long id;
  19 +
  20 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  21 + @NotEmpty(message = "名字不能为空")
  22 + private String name;
  23 +
  24 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  25 + @NotEmpty(message = "简介不能为空")
  26 + private String description;
  27 +
  28 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + @NotNull(message = "出生日期不能为空")
  30 + private LocalDateTime birthday;
  31 +
  32 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + @NotNull(message = "性别不能为空")
  34 + private Integer sex;
  35 +
  36 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  37 + @NotNull(message = "是否有效不能为空")
  38 + private Boolean enabled;
  39 +
  40 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  41 + @NotEmpty(message = "头像不能为空")
  42 + private String avatar;
  43 +
  44 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  45 + @NotEmpty(message = "附件不能为空")
  46 + private String video;
  47 +
  48 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  49 + @NotEmpty(message = "备注不能为空")
  50 + private String memo;
  51 +
  52 + @Schema(description = "学生联系人列表")
  53 + private List<InfraStudentContactDO> studentContacts;
  54 +
  55 + @Schema(description = "学生班主任")
  56 + private InfraStudentTeacherDO studentTeacher;
  57 +
  58 +}
0 \ No newline at end of file 59 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentService 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import java.util.*;
  4 +import javax.validation.*;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  6 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  7 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  9 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  10 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  11 +
  12 +/**
  13 + * 学生 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface InfraStudentService {
  18 +
  19 + /**
  20 + * 创建学生
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新学生
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除学生
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deleteStudent(Long id);
  40 +
  41 + /**
  42 + * 获得学生
  43 + *
  44 + * @param id 编号
  45 + * @return 学生
  46 + */
  47 + InfraStudentDO getStudent(Long id);
  48 +
  49 + /**
  50 + * 获得学生分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return 学生分页
  54 + */
  55 + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
  56 +
  57 + // ==================== 子表(学生联系人) ====================
  58 +
  59 + /**
  60 + * 获得学生联系人列表
  61 + *
  62 + * @param studentId 学生编号
  63 + * @return 学生联系人列表
  64 + */
  65 + List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);
  66 +
  67 + // ==================== 子表(学生班主任) ====================
  68 +
  69 + /**
  70 + * 获得学生班主任
  71 + *
  72 + * @param studentId 学生编号
  73 + * @return 学生班主任
  74 + */
  75 + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
  76 +
  77 +}
0 \ No newline at end of file 78 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentServiceImpl 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.transaction.annotation.Transactional;
  7 +
  8 +import java.util.*;
  9 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  13 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  15 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  16 +
  17 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  18 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
  19 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +
  24 +/**
  25 + * 学生 Service 实现类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Service
  30 +@Validated
  31 +public class InfraStudentServiceImpl implements InfraStudentService {
  32 +
  33 + @Resource
  34 + private InfraStudentMapper studentMapper;
  35 + @Resource
  36 + private InfraStudentContactMapper studentContactMapper;
  37 + @Resource
  38 + private InfraStudentTeacherMapper studentTeacherMapper;
  39 +
  40 + @Override
  41 + @Transactional(rollbackFor = Exception.class)
  42 + public Long createStudent(InfraStudentSaveReqVO createReqVO) {
  43 + // 插入
  44 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
  45 + studentMapper.insert(student);
  46 +
  47 + // 插入子表
  48 + createStudentContactList(student.getId(), createReqVO.getStudentContacts());
  49 + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
  50 + // 返回
  51 + return student.getId();
  52 + }
  53 +
  54 + @Override
  55 + @Transactional(rollbackFor = Exception.class)
  56 + public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
  57 + // 校验存在
  58 + validateStudentExists(updateReqVO.getId());
  59 + // 更新
  60 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
  61 + studentMapper.updateById(updateObj);
  62 +
  63 + // 更新子表
  64 + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
  65 + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
  66 + }
  67 +
  68 + @Override
  69 + @Transactional(rollbackFor = Exception.class)
  70 + public void deleteStudent(Long id) {
  71 + // 校验存在
  72 + validateStudentExists(id);
  73 + // 删除
  74 + studentMapper.deleteById(id);
  75 +
  76 + // 删除子表
  77 + deleteStudentContactByStudentId(id);
  78 + deleteStudentTeacherByStudentId(id);
  79 + }
  80 +
  81 + private void validateStudentExists(Long id) {
  82 + if (studentMapper.selectById(id) == null) {
  83 + throw exception(STUDENT_NOT_EXISTS);
  84 + }
  85 + }
  86 +
  87 + @Override
  88 + public InfraStudentDO getStudent(Long id) {
  89 + return studentMapper.selectById(id);
  90 + }
  91 +
  92 + @Override
  93 + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
  94 + return studentMapper.selectPage(pageReqVO);
  95 + }
  96 +
  97 + // ==================== 子表(学生联系人) ====================
  98 +
  99 + @Override
  100 + public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {
  101 + return studentContactMapper.selectListByStudentId(studentId);
  102 + }
  103 +
  104 + private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
  105 + list.forEach(o -> o.setStudentId(studentId));
  106 + studentContactMapper.insertBatch(list);
  107 + }
  108 +
  109 + private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
  110 + deleteStudentContactByStudentId(studentId);
  111 + list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
  112 + createStudentContactList(studentId, list);
  113 + }
  114 +
  115 + private void deleteStudentContactByStudentId(Long studentId) {
  116 + studentContactMapper.deleteByStudentId(studentId);
  117 + }
  118 +
  119 + // ==================== 子表(学生班主任) ====================
  120 +
  121 + @Override
  122 + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
  123 + return studentTeacherMapper.selectByStudentId(studentId);
  124 + }
  125 +
  126 + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
  127 + if (studentTeacher == null) {
  128 + return;
  129 + }
  130 + studentTeacher.setStudentId(studentId);
  131 + studentTeacherMapper.insert(studentTeacher);
  132 + }
  133 +
  134 + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
  135 + if (studentTeacher == null) {
  136 + return;
  137 + }
  138 + studentTeacher.setStudentId(studentId);
  139 + studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
  140 + studentTeacherMapper.insertOrUpdate(studentTeacher);
  141 + }
  142 +
  143 + private void deleteStudentTeacherByStudentId(Long studentId) {
  144 + studentTeacherMapper.deleteByStudentId(studentId);
  145 + }
  146 +
  147 +}
0 \ No newline at end of file 148 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentServiceImplTest 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import javax.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  13 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import javax.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link InfraStudentServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(InfraStudentServiceImpl.class)
  37 +public class InfraStudentServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private InfraStudentServiceImpl studentService;
  41 +
  42 + @Resource
  43 + private InfraStudentMapper studentMapper;
  44 +
  45 + @Test
  46 + public void testCreateStudent_success() {
  47 + // 准备参数
  48 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long studentId = studentService.createStudent(createReqVO);
  52 + // 断言
  53 + assertNotNull(studentId);
  54 + // 校验记录的属性是否正确
  55 + InfraStudentDO student = studentMapper.selectById(studentId);
  56 + assertPojoEquals(createReqVO, student, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateStudent_success() {
  61 + // mock 数据
  62 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  63 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
  66 + o.setId(dbStudent.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + studentService.updateStudent(updateReqVO);
  71 + // 校验是否更新正确
  72 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, student);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateStudent_notExists() {
  78 + // 准备参数
  79 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteStudent_success() {
  87 + // mock 数据
  88 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  89 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbStudent.getId();
  92 +
  93 + // 调用
  94 + studentService.deleteStudent(id);
  95 + // 校验数据不存在了
  96 + assertNull(studentMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteStudent_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetStudentPage() {
  111 + // mock 数据
  112 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
  113 + o.setName(null);
  114 + o.setBirthday(null);
  115 + o.setSex(null);
  116 + o.setEnabled(null);
  117 + o.setCreateTime(null);
  118 + });
  119 + studentMapper.insert(dbStudent);
  120 + // 测试 name 不匹配
  121 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
  122 + // 测试 birthday 不匹配
  123 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
  124 + // 测试 sex 不匹配
  125 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
  126 + // 测试 enabled 不匹配
  127 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
  128 + // 测试 createTime 不匹配
  129 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
  130 + // 准备参数
  131 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
  132 + reqVO.setName(null);
  133 + reqVO.setBirthday(null);
  134 + reqVO.setSex(null);
  135 + reqVO.setEnabled(null);
  136 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  137 +
  138 + // 调用
  139 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
  140 + // 断言
  141 + assertEquals(1, pageResult.getTotal());
  142 + assertEquals(1, pageResult.getList().size());
  143 + assertPojoEquals(dbStudent, pageResult.getList().get(0));
  144 + }
  145 +
  146 +}
0 \ No newline at end of file 147 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentTeacherDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生班主任 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student_teacher")
  16 +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentTeacherDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 学生编号
  32 + */
  33 + private Long studentId;
  34 + /**
  35 + * 名字
  36 + */
  37 + private String name;
  38 + /**
  39 + * 简介
  40 + */
  41 + private String description;
  42 + /**
  43 + * 出生日期
  44 + */
  45 + private LocalDateTime birthday;
  46 + /**
  47 + * 性别
  48 + *
  49 + * 枚举 {@link TODO system_user_sex 对应的类}
  50 + */
  51 + private Integer sex;
  52 + /**
  53 + * 是否有效
  54 + *
  55 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  56 + */
  57 + private Boolean enabled;
  58 + /**
  59 + * 头像
  60 + */
  61 + private String avatar;
  62 + /**
  63 + * 附件
  64 + */
  65 + private String video;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String memo;
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/java/InfraStudentTeacherMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +/**
  13 + * 学生班主任 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
  19 +
  20 + default InfraStudentTeacherDO selectByStudentId(Long studentId) {
  21 + return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
  22 + }
  23 +
  24 + default int deleteByStudentId(Long studentId) {
  25 + return delete(InfraStudentTeacherDO::getStudentId, studentId);
  26 + }
  27 +
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/js/student 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建学生
  4 +export function createStudent(data) {
  5 + return request({
  6 + url: '/infra/student/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新学生
  13 +export function updateStudent(data) {
  14 + return request({
  15 + url: '/infra/student/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除学生
  22 +export function deleteStudent(id) {
  23 + return request({
  24 + url: '/infra/student/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得学生
  30 +export function getStudent(id) {
  31 + return request({
  32 + url: '/infra/student/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得学生分页
  38 +export function getStudentPage(params) {
  39 + return request({
  40 + url: '/infra/student/page',
  41 + method: 'get',
  42 + params
  43 + })
  44 +}
  45 +// 导出学生 Excel
  46 +export function exportStudentExcel(params) {
  47 + return request({
  48 + url: '/infra/student/export-excel',
  49 + method: 'get',
  50 + params,
  51 + responseType: 'blob'
  52 + })
  53 +}
  54 +
  55 +// ==================== 子表(学生联系人) ====================
  56 +
  57 + // 获得学生联系人列表
  58 + export function getStudentContactListByStudentId(studentId) {
  59 + return request({
  60 + url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,
  61 + method: 'get'
  62 + })
  63 + }
  64 +
  65 +// ==================== 子表(学生班主任) ====================
  66 +
  67 + // 获得学生班主任
  68 + export function getStudentTeacherByStudentId(studentId) {
  69 + return request({
  70 + url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,
  71 + method: 'get'
  72 + })
  73 + }
  74 +
0 \ No newline at end of file 75 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/sql/h2 0 → 100644
  1 +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
  2 +CREATE TABLE IF NOT EXISTS "infra_student" (
  3 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  4 + "name" varchar NOT NULL,
  5 + "description" varchar NOT NULL,
  6 + "birthday" varchar NOT NULL,
  7 + "sex" int NOT NULL,
  8 + "enabled" bit NOT NULL,
  9 + "avatar" varchar NOT NULL,
  10 + "video" varchar NOT NULL,
  11 + "memo" varchar NOT NULL,
  12 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  13 + PRIMARY KEY ("id")
  14 +) COMMENT '学生表';
  15 +
  16 +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
  17 +DELETE FROM "infra_student";
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/sql/sql 0 → 100644
  1 +-- 菜单 SQL
  2 +INSERT INTO system_menu(
  3 + name, permission, type, sort, parent_id,
  4 + path, icon, component, status, component_name
  5 +)
  6 +VALUES (
  7 + '学生管理', '', 2, 0, 888,
  8 + 'student', '', 'infra/demo/index', 0, 'InfraStudent'
  9 +);
  10 +
  11 +-- 按钮父菜单ID
  12 +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
  13 +SELECT @parentId := LAST_INSERT_ID();
  14 +
  15 +-- 按钮 SQL
  16 +INSERT INTO system_menu(
  17 + name, permission, type, sort, parent_id,
  18 + path, icon, component, status
  19 +)
  20 +VALUES (
  21 + '学生查询', 'infra:student:query', 3, 1, @parentId,
  22 + '', '', '', 0
  23 +);
  24 +INSERT INTO system_menu(
  25 + name, permission, type, sort, parent_id,
  26 + path, icon, component, status
  27 +)
  28 +VALUES (
  29 + '学生创建', 'infra:student:create', 3, 2, @parentId,
  30 + '', '', '', 0
  31 +);
  32 +INSERT INTO system_menu(
  33 + name, permission, type, sort, parent_id,
  34 + path, icon, component, status
  35 +)
  36 +VALUES (
  37 + '学生更新', 'infra:student:update', 3, 3, @parentId,
  38 + '', '', '', 0
  39 +);
  40 +INSERT INTO system_menu(
  41 + name, permission, type, sort, parent_id,
  42 + path, icon, component, status
  43 +)
  44 +VALUES (
  45 + '学生删除', 'infra:student:delete', 3, 4, @parentId,
  46 + '', '', '', 0
  47 +);
  48 +INSERT INTO system_menu(
  49 + name, permission, type, sort, parent_id,
  50 + path, icon, component, status
  51 +)
  52 +VALUES (
  53 + '学生导出', 'infra:student:export', 3, 5, @parentId,
  54 + '', '', '', 0
  55 +);
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/vue/StudentContactForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-form
  4 + ref="formRef"
  5 + :model="formData"
  6 + :rules="formRules"
  7 + v-loading="formLoading"
  8 + label-width="0px"
  9 + :inline-message="true"
  10 + >
  11 + <el-table :data="formData" class="-mt-10px">
  12 + <el-table-column label="序号" type="index" width="100" />
  13 + <el-table-column label="名字" min-width="150">
  14 + <template v-slot="{ row, $index }">
  15 + <el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
  16 + <el-input v-model="row.name" placeholder="请输入名字" />
  17 + </el-form-item>
  18 + </template>
  19 + </el-table-column>
  20 + <el-table-column label="简介" min-width="200">
  21 + <template v-slot="{ row, $index }">
  22 + <el-form-item :prop="`${$index}.description`" :rules="formRules.description" class="mb-0px!">
  23 + <el-input v-model="row.description" type="textarea" placeholder="请输入简介" />
  24 + </el-form-item>
  25 + </template>
  26 + </el-table-column>
  27 + <el-table-column label="出生日期" min-width="150">
  28 + <template v-slot="{ row, $index }">
  29 + <el-form-item :prop="`${$index}.birthday`" :rules="formRules.birthday" class="mb-0px!">
  30 + <el-date-picker clearable v-model="row.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  31 + </el-form-item>
  32 + </template>
  33 + </el-table-column>
  34 + <el-table-column label="性别" min-width="150">
  35 + <template v-slot="{ row, $index }">
  36 + <el-form-item :prop="`${$index}.sex`" :rules="formRules.sex" class="mb-0px!">
  37 + <el-select v-model="row.sex" placeholder="请选择性别">
  38 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  39 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  40 + </el-select>
  41 + </el-form-item>
  42 + </template>
  43 + </el-table-column>
  44 + <el-table-column label="是否有效" min-width="150">
  45 + <template v-slot="{ row, $index }">
  46 + <el-form-item :prop="`${$index}.enabled`" :rules="formRules.enabled" class="mb-0px!">
  47 + <el-radio-group v-model="row.enabled">
  48 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  49 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  50 + </el-radio-group>
  51 + </el-form-item>
  52 + </template>
  53 + </el-table-column>
  54 + <el-table-column label="头像" min-width="200">
  55 + <template v-slot="{ row, $index }">
  56 + <el-form-item :prop="`${$index}.avatar`" :rules="formRules.avatar" class="mb-0px!">
  57 + <ImageUpload v-model="row.avatar"/>
  58 + </el-form-item>
  59 + </template>
  60 + </el-table-column>
  61 + <el-table-column label="附件" min-width="200">
  62 + <template v-slot="{ row, $index }">
  63 + <el-form-item :prop="`${$index}.video`" :rules="formRules.video" class="mb-0px!">
  64 + <FileUpload v-model="row.video"/>
  65 + </el-form-item>
  66 + </template>
  67 + </el-table-column>
  68 + <el-table-column label="备注" min-width="400">
  69 + <template v-slot="{ row, $index }">
  70 + <el-form-item :prop="`${$index}.memo`" :rules="formRules.memo" class="mb-0px!">
  71 + <Editor v-model="row.memo" :min-height="192"/>
  72 + </el-form-item>
  73 + </template>
  74 + </el-table-column>
  75 + <el-table-column align="center" fixed="right" label="操作" width="60">
  76 + <template v-slot="{ $index }">
  77 + <el-link @click="handleDelete($index)">—</el-link>
  78 + </template>
  79 + </el-table-column>
  80 + </el-table>
  81 + </el-form>
  82 + <el-row justify="center" class="mt-3">
  83 + <el-button @click="handleAdd" round>+ 添加学生联系人</el-button>
  84 + </el-row>
  85 + </div>
  86 +</template>
  87 +
  88 +<script>
  89 + import * as StudentApi from '@/api/infra/demo'
  90 + import ImageUpload from '@/components/ImageUpload';
  91 + import FileUpload from '@/components/FileUpload';
  92 + import Editor from '@/components/Editor';
  93 + export default {
  94 + name: "StudentContactForm",
  95 + components: {
  96 + ImageUpload,
  97 + FileUpload,
  98 + Editor,
  99 + },
  100 + props:[
  101 + 'studentId'
  102 + ],// 学生编号(主表的关联字段)
  103 + data() {
  104 + return {
  105 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  106 + formLoading: false,
  107 + // 表单参数
  108 + formData: [],
  109 + // 表单校验
  110 + formRules: {
  111 + studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
  112 + name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
  113 + description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
  114 + birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
  115 + sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
  116 + enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
  117 + avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
  118 + memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
  119 + },
  120 + };
  121 + },
  122 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  123 + studentId:{
  124 + handler(val) {
  125 + // 1. 重置表单
  126 + this.formData = []
  127 + // 2. val 非空,则加载数据
  128 + if (!val) {
  129 + return;
  130 + }
  131 + try {
  132 + this.formLoading = true;
  133 + const that = this;
  134 + StudentApi.getStudentContactListByStudentId(val).then(res=>{
  135 + that.formData = res.data;
  136 + })
  137 + } finally {
  138 + this.formLoading = false;
  139 + }
  140 + },
  141 + immediate: true
  142 + }
  143 + },
  144 + methods: {
  145 + /** 新增按钮操作 */
  146 + handleAdd() {
  147 + const row = {
  148 + id: undefined,
  149 + studentId: undefined,
  150 + name: undefined,
  151 + description: undefined,
  152 + birthday: undefined,
  153 + sex: undefined,
  154 + enabled: undefined,
  155 + avatar: undefined,
  156 + video: undefined,
  157 + memo: undefined,
  158 + }
  159 + row.studentId = this.studentId
  160 + this.formData.push(row)
  161 + },
  162 + /** 删除按钮操作 */
  163 + handleDelete(index) {
  164 + this.formData.splice(index, 1)
  165 + },
  166 + /** 表单校验 */
  167 + validate(){
  168 + return this.$refs["formRef"].validate()
  169 + },
  170 + /** 表单值 */
  171 + getData(){
  172 + return this.formData
  173 + }
  174 + }
  175 + };
  176 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/vue/StudentContactList 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  4 + <el-table-column label="编号" align="center" prop="id" />
  5 + <el-table-column label="名字" align="center" prop="name" />
  6 + <el-table-column label="简介" align="center" prop="description" />
  7 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  8 + <template v-slot="scope">
  9 + <span>{{ parseTime(scope.row.birthday) }}</span>
  10 + </template>
  11 + </el-table-column>
  12 + <el-table-column label="性别" align="center" prop="sex">
  13 + <template v-slot="scope">
  14 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  15 + </template>
  16 + </el-table-column>
  17 + <el-table-column label="是否有效" align="center" prop="enabled">
  18 + <template v-slot="scope">
  19 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  20 + </template>
  21 + </el-table-column>
  22 + <el-table-column label="头像" align="center" prop="avatar" />
  23 + <el-table-column label="附件" align="center" prop="video" />
  24 + <el-table-column label="备注" align="center" prop="memo" />
  25 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  26 + <template v-slot="scope">
  27 + <span>{{ parseTime(scope.row.createTime) }}</span>
  28 + </template>
  29 + </el-table-column>
  30 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  31 + <template v-slot="scope">
  32 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  33 + v-hasPermi="['infra:student:update']">修改</el-button>
  34 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  35 + v-hasPermi="['infra:student:delete']">删除</el-button>
  36 + </template>
  37 + </el-table-column>
  38 + </el-table>
  39 + </div>
  40 +</template>
  41 +
  42 +<script>
  43 + import * as StudentApi from '@/api/infra/demo'
  44 + export default {
  45 + name: "StudentContactList",
  46 + props:[
  47 + 'studentId'
  48 + ],// 学生编号(主表的关联字段)
  49 + data() {
  50 + return {
  51 + // 遮罩层
  52 + loading: true,
  53 + // 列表的数据
  54 + list: [],
  55 + };
  56 + },
  57 + created() {
  58 + this.getList();
  59 + },
  60 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  61 + studentId:{
  62 + handler(val) {
  63 + this.queryParams.studentId = val;
  64 + if (val){
  65 + this.handleQuery();
  66 + }
  67 + },
  68 + immediate: true
  69 + }
  70 + },
  71 + methods: {
  72 + /** 查询列表 */
  73 + getList() {
  74 + try {
  75 + this.loading = true;
  76 + const that = this;
  77 + StudentApi.getStudentContactListByStudentId(this.studentId).then(response=>{
  78 + that.list = response.data;
  79 + })
  80 + } finally {
  81 + this.loading = false;
  82 + }
  83 + },
  84 + /** 搜索按钮操作 */
  85 + handleQuery() {
  86 + this.queryParams.pageNo = 1;
  87 + this.getList();
  88 + },
  89 + }
  90 + };
  91 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/vue/StudentForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="简介" prop="description">
  10 + <el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
  11 + </el-form-item>
  12 + <el-form-item label="出生日期" prop="birthday">
  13 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  14 + </el-form-item>
  15 + <el-form-item label="性别" prop="sex">
  16 + <el-select v-model="formData.sex" placeholder="请选择性别">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  18 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="是否有效" prop="enabled">
  22 + <el-radio-group v-model="formData.enabled">
  23 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  24 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  25 + </el-radio-group>
  26 + </el-form-item>
  27 + <el-form-item label="头像">
  28 + <ImageUpload v-model="formData.avatar"/>
  29 + </el-form-item>
  30 + <el-form-item label="附件">
  31 + <FileUpload v-model="formData.video"/>
  32 + </el-form-item>
  33 + <el-form-item label="备注">
  34 + <Editor v-model="formData.memo" :min-height="192"/>
  35 + </el-form-item>
  36 + </el-form>
  37 + <!-- 子表的表单 -->
  38 + <el-tabs v-model="subTabsName">
  39 + <el-tab-pane label="学生联系人" name="studentContact">
  40 + <StudentContactForm ref="studentContactFormRef" :student-id="formData.id" />
  41 + </el-tab-pane>
  42 + <el-tab-pane label="学生班主任" name="studentTeacher">
  43 + <StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData.id" />
  44 + </el-tab-pane>
  45 + </el-tabs>
  46 + <div slot="footer" class="dialog-footer">
  47 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  48 + <el-button @click="dialogVisible = false">取 消</el-button>
  49 + </div>
  50 + </el-dialog>
  51 + </div>
  52 +</template>
  53 +
  54 +<script>
  55 + import * as StudentApi from '@/api/infra/demo'
  56 + import ImageUpload from '@/components/ImageUpload';
  57 + import FileUpload from '@/components/FileUpload';
  58 + import Editor from '@/components/Editor';
  59 + import StudentContactForm from './components/StudentContactForm.vue'
  60 + import StudentTeacherForm from './components/StudentTeacherForm.vue'
  61 + export default {
  62 + name: "StudentForm",
  63 + components: {
  64 + ImageUpload,
  65 + FileUpload,
  66 + Editor,
  67 + StudentContactForm,
  68 + StudentTeacherForm,
  69 + },
  70 + data() {
  71 + return {
  72 + // 弹出层标题
  73 + dialogTitle: "",
  74 + // 是否显示弹出层
  75 + dialogVisible: false,
  76 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  77 + formLoading: false,
  78 + // 表单参数
  79 + formData: {
  80 + id: undefined,
  81 + name: undefined,
  82 + description: undefined,
  83 + birthday: undefined,
  84 + sex: undefined,
  85 + enabled: undefined,
  86 + avatar: undefined,
  87 + video: undefined,
  88 + memo: undefined,
  89 + },
  90 + // 表单校验
  91 + formRules: {
  92 + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
  93 + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
  94 + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
  95 + sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
  96 + enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
  97 + avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
  98 + video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
  99 + memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
  100 + },
  101 + /** 子表的表单 */
  102 + subTabsName: 'studentContact'
  103 + };
  104 + },
  105 + methods: {
  106 + /** 打开弹窗 */
  107 + open(id) {
  108 + this.dialogVisible = true;
  109 + this.reset();
  110 + const that = this;
  111 + // 修改时,设置数据
  112 + if (id) {
  113 + this.formLoading = true;
  114 + try {
  115 + StudentApi.getStudent(id).then(res=>{
  116 + that.formData = res.data;
  117 + that.title = "修改学生";
  118 + })
  119 + } finally {
  120 + this.formLoading = false;
  121 + }
  122 + }
  123 + this.title = "新增学生";
  124 + },
  125 + /** 提交按钮 */
  126 + submitForm() {
  127 + this.formLoading = true;
  128 + try {
  129 + const that = this;
  130 + let data = this.formData;
  131 + let validate = false;
  132 + // 校验主表
  133 + this.getRef("formRef").validate(valid => {
  134 + validate = valid;
  135 + });
  136 + // 校验子表
  137 + this.validateSubFrom01().then(() => {
  138 + // 全部校验通过-拼接子表的数据
  139 + // 拼接子表的数据
  140 + data.studentContacts = that.getRef('studentContactFormRef').getData();
  141 + data.studentTeacher = that.getRef('studentTeacherFormRef').getData();
  142 + }).catch((err) => {
  143 + validate = false;
  144 + that.subTabsName = err.replace("FormRef", ""); // 定位到没有校验通过的子表单
  145 + })
  146 + // 所有表单校验通过后方可提交
  147 + if (!validate) {
  148 + return;
  149 + }
  150 + // 修改的提交
  151 + if (data.id) {
  152 + StudentApi.updateStudent(data).then(response => {
  153 + that.$modal.msgSuccess("修改成功");
  154 + that.dialogVisible = false;
  155 + that.$emit('success');
  156 + });
  157 + return;
  158 + }
  159 + // 添加的提交
  160 + StudentApi.createStudent(data).then(response => {
  161 + that.$modal.msgSuccess("新增成功");
  162 + that.dialogVisible = false;
  163 + that.$emit('success');
  164 + });
  165 + }finally {
  166 + this.formLoading = false;
  167 + }
  168 + },
  169 + getRef(refName){
  170 + return this.$refs[refName];
  171 + },
  172 + /** 校验子表单 */
  173 + validateSubFrom(item) {
  174 + return new Promise((resolve, reject) => {
  175 + this.getRef(item).validate()
  176 + .then(() => {
  177 + resolve();
  178 + })
  179 + .catch(() => {
  180 + reject(item);
  181 + })
  182 + })
  183 + },
  184 + /** 校验所有子表单 */
  185 + validateSubFrom01() {
  186 + // 需要校验的表单 ref
  187 + const validFormRefArr = [
  188 + "studentContactFormRef",
  189 + "studentTeacherFormRef",
  190 + ];
  191 + const validArr = []; // 校验
  192 + for (const item of validFormRefArr) {
  193 + validArr.push(this.validateSubFrom(item));
  194 + }
  195 + return new Promise((resolve, reject) => {
  196 + // 校验所有
  197 + Promise.all(validArr).then(() => {
  198 + resolve();
  199 + }).catch((err) => {
  200 + reject(err);
  201 + })
  202 + })
  203 + },
  204 + /** 表单重置 */
  205 + reset() {
  206 + this.formData = {
  207 + id: undefined,
  208 + name: undefined,
  209 + description: undefined,
  210 + birthday: undefined,
  211 + sex: undefined,
  212 + enabled: undefined,
  213 + avatar: undefined,
  214 + video: undefined,
  215 + memo: undefined,
  216 + };
  217 + this.resetForm("formRef");
  218 + },
  219 + }
  220 + };
  221 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/vue/StudentTeacherForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-form
  4 + ref="formRef"
  5 + :model="formData"
  6 + :rules="formRules"
  7 + label-width="100px"
  8 + v-loading="formLoading"
  9 + >
  10 + <el-form-item label="名字" prop="name">
  11 + <el-input v-model="formData.name" placeholder="请输入名字" />
  12 + </el-form-item>
  13 + <el-form-item label="简介" prop="description">
  14 + <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
  15 + </el-form-item>
  16 + <el-form-item label="出生日期" prop="birthday">
  17 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  18 + </el-form-item>
  19 + <el-form-item label="性别" prop="sex">
  20 + <el-select v-model="formData.sex" placeholder="请选择性别">
  21 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  22 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  23 + </el-select>
  24 + </el-form-item>
  25 + <el-form-item label="是否有效" prop="enabled">
  26 + <el-radio-group v-model="formData.enabled">
  27 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  28 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  29 + </el-radio-group>
  30 + </el-form-item>
  31 + <el-form-item label="头像">
  32 + <ImageUpload v-model="formData.avatar"/>
  33 + </el-form-item>
  34 + <el-form-item label="附件">
  35 + <FileUpload v-model="formData.video"/>
  36 + </el-form-item>
  37 + <el-form-item label="备注">
  38 + <Editor v-model="formData.memo" :min-height="192"/>
  39 + </el-form-item>
  40 + </el-form>
  41 + </div>
  42 +</template>
  43 +
  44 +<script>
  45 + import * as StudentApi from '@/api/infra/demo'
  46 + import ImageUpload from '@/components/ImageUpload';
  47 + import FileUpload from '@/components/FileUpload';
  48 + import Editor from '@/components/Editor';
  49 + export default {
  50 + name: "StudentTeacherForm",
  51 + components: {
  52 + ImageUpload,
  53 + FileUpload,
  54 + Editor,
  55 + },
  56 + props:[
  57 + 'studentId'
  58 + ],// 学生编号(主表的关联字段)
  59 + data() {
  60 + return {
  61 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  62 + formLoading: false,
  63 + // 表单参数
  64 + formData: [],
  65 + // 表单校验
  66 + formRules: {
  67 + studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
  68 + name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
  69 + description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
  70 + birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
  71 + sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
  72 + enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
  73 + avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
  74 + memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
  75 + },
  76 + };
  77 + },
  78 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  79 + studentId:{
  80 + handler(val) {
  81 + // 1. 重置表单
  82 + this.formData = {
  83 + id: undefined,
  84 + studentId: undefined,
  85 + name: undefined,
  86 + description: undefined,
  87 + birthday: undefined,
  88 + sex: undefined,
  89 + enabled: undefined,
  90 + avatar: undefined,
  91 + video: undefined,
  92 + memo: undefined,
  93 + }
  94 + // 2. val 非空,则加载数据
  95 + if (!val) {
  96 + return;
  97 + }
  98 + try {
  99 + this.formLoading = true;
  100 + const that = this;
  101 + StudentApi.getStudentTeacherByStudentId(val).then(res=>{
  102 + const data = res.data;
  103 + if (!data) {
  104 + return
  105 + }
  106 + that.formData = data;
  107 + })
  108 + } finally {
  109 + this.formLoading = false;
  110 + }
  111 + },
  112 + immediate: true
  113 + }
  114 + },
  115 + methods: {
  116 + /** 表单校验 */
  117 + validate(){
  118 + return this.$refs["formRef"].validate()
  119 + },
  120 + /** 表单值 */
  121 + getData(){
  122 + return this.formData
  123 + }
  124 + }
  125 + };
  126 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/vue/StudentTeacherList 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  4 + <el-table-column label="编号" align="center" prop="id" />
  5 + <el-table-column label="名字" align="center" prop="name" />
  6 + <el-table-column label="简介" align="center" prop="description" />
  7 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  8 + <template v-slot="scope">
  9 + <span>{{ parseTime(scope.row.birthday) }}</span>
  10 + </template>
  11 + </el-table-column>
  12 + <el-table-column label="性别" align="center" prop="sex">
  13 + <template v-slot="scope">
  14 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  15 + </template>
  16 + </el-table-column>
  17 + <el-table-column label="是否有效" align="center" prop="enabled">
  18 + <template v-slot="scope">
  19 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  20 + </template>
  21 + </el-table-column>
  22 + <el-table-column label="头像" align="center" prop="avatar" />
  23 + <el-table-column label="附件" align="center" prop="video" />
  24 + <el-table-column label="备注" align="center" prop="memo" />
  25 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  26 + <template v-slot="scope">
  27 + <span>{{ parseTime(scope.row.createTime) }}</span>
  28 + </template>
  29 + </el-table-column>
  30 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  31 + <template v-slot="scope">
  32 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  33 + v-hasPermi="['infra:student:update']">修改</el-button>
  34 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  35 + v-hasPermi="['infra:student:delete']">删除</el-button>
  36 + </template>
  37 + </el-table-column>
  38 + </el-table>
  39 + </div>
  40 +</template>
  41 +
  42 +<script>
  43 + import * as StudentApi from '@/api/infra/demo'
  44 + export default {
  45 + name: "StudentTeacherList",
  46 + props:[
  47 + 'studentId'
  48 + ],// 学生编号(主表的关联字段)
  49 + data() {
  50 + return {
  51 + // 遮罩层
  52 + loading: true,
  53 + // 列表的数据
  54 + list: [],
  55 + };
  56 + },
  57 + created() {
  58 + this.getList();
  59 + },
  60 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  61 + studentId:{
  62 + handler(val) {
  63 + this.queryParams.studentId = val;
  64 + if (val){
  65 + this.handleQuery();
  66 + }
  67 + },
  68 + immediate: true
  69 + }
  70 + },
  71 + methods: {
  72 + /** 查询列表 */
  73 + getList() {
  74 + try {
  75 + this.loading = true;
  76 + const that = this;
  77 + StudentApi.getStudentTeacherByStudentId(this.studentId).then(response=>{
  78 + const data = response.data;
  79 + if (!data) {
  80 + return
  81 + }
  82 + that.list.push(data)
  83 + })
  84 + } finally {
  85 + this.loading = false;
  86 + }
  87 + },
  88 + /** 搜索按钮操作 */
  89 + handleQuery() {
  90 + this.queryParams.pageNo = 1;
  91 + this.getList();
  92 + },
  93 + }
  94 + };
  95 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/vue/index 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="出生日期" prop="birthday">
  10 + <el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
  11 + </el-form-item>
  12 + <el-form-item label="性别" prop="sex">
  13 + <el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
  14 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  15 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item label="是否有效" prop="enabled">
  19 + <el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
  20 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  21 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  22 + </el-select>
  23 + </el-form-item>
  24 + <el-form-item label="创建时间" prop="createTime">
  25 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  26 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  27 + </el-form-item>
  28 + <el-form-item>
  29 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  30 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  31 + </el-form-item>
  32 + </el-form>
  33 +
  34 + <!-- 操作工具栏 -->
  35 + <el-row :gutter="10" class="mb8">
  36 + <el-col :span="1.5">
  37 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  38 + v-hasPermi="['infra:student:create']">新增</el-button>
  39 + </el-col>
  40 + <el-col :span="1.5">
  41 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  42 + v-hasPermi="['infra:student:export']">导出</el-button>
  43 + </el-col>
  44 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  45 + </el-row>
  46 +
  47 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  48 + <!-- 子表的列表 -->
  49 + <el-table-column type="expand">
  50 + <template #default="scope">
  51 + <el-tabs value="studentContact">
  52 + <el-tab-pane label="学生联系人" name="studentContact">
  53 + <StudentContactList :student-id="scope.row.id" />
  54 + </el-tab-pane>
  55 + <el-tab-pane label="学生班主任" name="studentTeacher">
  56 + <StudentTeacherList :student-id="scope.row.id" />
  57 + </el-tab-pane>
  58 + </el-tabs>
  59 + </template>
  60 + </el-table-column>
  61 + <el-table-column label="编号" align="center" prop="id">
  62 + <template v-slot="scope">
  63 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
  64 + </template>
  65 + </el-table-column>
  66 + <el-table-column label="名字" align="center" prop="name">
  67 + <template v-slot="scope">
  68 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
  69 + </template>
  70 + </el-table-column>
  71 + <el-table-column label="简介" align="center" prop="description">
  72 + <template v-slot="scope">
  73 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
  74 + </template>
  75 + </el-table-column>
  76 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  77 + <template v-slot="scope">
  78 + <span>{{ parseTime(scope.row.birthday) }}</span>
  79 + </template>
  80 + </el-table-column>
  81 + <el-table-column label="性别" align="center" prop="sex">
  82 + <template v-slot="scope">
  83 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  84 + </template>
  85 + </el-table-column>
  86 + <el-table-column label="是否有效" align="center" prop="enabled">
  87 + <template v-slot="scope">
  88 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  89 + </template>
  90 + </el-table-column>
  91 + <el-table-column label="头像" align="center" prop="avatar">
  92 + <template v-slot="scope">
  93 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
  94 + </template>
  95 + </el-table-column>
  96 + <el-table-column label="附件" align="center" prop="video">
  97 + <template v-slot="scope">
  98 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
  99 + </template>
  100 + </el-table-column>
  101 + <el-table-column label="备注" align="center" prop="memo">
  102 + <template v-slot="scope">
  103 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
  104 + </template>
  105 + </el-table-column>
  106 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  107 + <template v-slot="scope">
  108 + <span>{{ parseTime(scope.row.createTime) }}</span>
  109 + </template>
  110 + </el-table-column>
  111 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  112 + <template v-slot="scope">
  113 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  114 + v-hasPermi="['infra:student:update']">修改</el-button>
  115 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  116 + v-hasPermi="['infra:student:delete']">删除</el-button>
  117 + </template>
  118 + </el-table-column>
  119 + </el-table>
  120 + <!-- 分页组件 -->
  121 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  122 + @pagination="getList"/>
  123 + <!-- 对话框(添加 / 修改) -->
  124 + <StudentForm ref="formRef" @success="getList" />
  125 + </div>
  126 +</template>
  127 +
  128 +<script>
  129 +import * as StudentApi from '@/api/infra/demo';
  130 +import StudentForm from './StudentForm.vue';
  131 + import StudentContactList from './components/StudentContactList.vue'
  132 + import StudentTeacherList from './components/StudentTeacherList.vue'
  133 +export default {
  134 + name: "Student",
  135 + components: {
  136 + StudentForm,
  137 + StudentContactList,
  138 + StudentTeacherList,
  139 + },
  140 + data() {
  141 + return {
  142 + // 遮罩层
  143 + loading: true,
  144 + // 导出遮罩层
  145 + exportLoading: false,
  146 + // 显示搜索条件
  147 + showSearch: true,
  148 + // 总条数
  149 + total: 0,
  150 + // 学生列表
  151 + list: [],
  152 + // 是否展开,默认全部展开
  153 + isExpandAll: true,
  154 + // 重新渲染表格状态
  155 + refreshTable: true,
  156 + // 选中行
  157 + currentRow: {},
  158 + // 查询参数
  159 + queryParams: {
  160 + pageNo: 1,
  161 + pageSize: 10,
  162 + name: null,
  163 + birthday: null,
  164 + sex: null,
  165 + enabled: null,
  166 + createTime: [],
  167 + },
  168 + };
  169 + },
  170 + created() {
  171 + this.getList();
  172 + },
  173 + methods: {
  174 + /** 查询列表 */
  175 + getList() {
  176 + try {
  177 + this.loading = true;
  178 + StudentApi.getStudentPage(this.queryParams).then(response => {
  179 + this.list = response.data.list;
  180 + this.total = response.data.total;
  181 + });
  182 + } finally {
  183 + this.loading = false;
  184 + }
  185 + },
  186 + /** 搜索按钮操作 */
  187 + handleQuery() {
  188 + this.queryParams.pageNo = 1;
  189 + this.getList();
  190 + },
  191 + /** 重置按钮操作 */
  192 + resetQuery() {
  193 + this.resetForm("queryForm");
  194 + this.handleQuery();
  195 + },
  196 + /** 添加/修改操作 */
  197 + openForm(id) {
  198 + this.$refs["formRef"].open(id);
  199 + },
  200 + /** 删除按钮操作 */
  201 + handleDelete(row) {
  202 + const that = this;
  203 + try {
  204 + const id = row.id;
  205 + this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
  206 + return StudentApi.deleteStudent(id);
  207 + }).then(() => {
  208 + that.getList();
  209 + that.$modal.msgSuccess("删除成功");
  210 + }).catch(() => {});
  211 + } catch {}
  212 + },
  213 + /** 导出按钮操作 */
  214 + handleExport() {
  215 + const that = this;
  216 + try {
  217 + this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
  218 + that.exportLoading = true;
  219 + return StudentApi.exportStudentExcel(params);
  220 + }).then(response => {
  221 + that.$download.excel(response, '学生.xls');
  222 + });
  223 + } catch {
  224 + } finally {
  225 + that.exportLoading = false;
  226 + }
  227 + },
  228 + }
  229 +};
  230 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_inner/xml/InfraStudentMapper 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/assert.json 0 → 100644
  1 +[ {
  2 + "contentPath" : "java/InfraStudentPageReqVO",
  3 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
  4 +}, {
  5 + "contentPath" : "java/InfraStudentRespVO",
  6 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
  7 +}, {
  8 + "contentPath" : "java/InfraStudentSaveReqVO",
  9 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
  10 +}, {
  11 + "contentPath" : "java/InfraStudentController",
  12 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
  13 +}, {
  14 + "contentPath" : "java/InfraStudentDO",
  15 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
  16 +}, {
  17 + "contentPath" : "java/InfraStudentContactDO",
  18 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
  19 +}, {
  20 + "contentPath" : "java/InfraStudentTeacherDO",
  21 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
  22 +}, {
  23 + "contentPath" : "java/InfraStudentMapper",
  24 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
  25 +}, {
  26 + "contentPath" : "java/InfraStudentContactMapper",
  27 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
  28 +}, {
  29 + "contentPath" : "java/InfraStudentTeacherMapper",
  30 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
  31 +}, {
  32 + "contentPath" : "xml/InfraStudentMapper",
  33 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
  34 +}, {
  35 + "contentPath" : "java/InfraStudentServiceImpl",
  36 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
  37 +}, {
  38 + "contentPath" : "java/InfraStudentService",
  39 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
  40 +}, {
  41 + "contentPath" : "java/InfraStudentServiceImplTest",
  42 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
  43 +}, {
  44 + "contentPath" : "java/ErrorCodeConstants_手动操作",
  45 + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
  46 +}, {
  47 + "contentPath" : "sql/sql",
  48 + "filePath" : "sql/sql.sql"
  49 +}, {
  50 + "contentPath" : "sql/h2",
  51 + "filePath" : "sql/h2.sql"
  52 +}, {
  53 + "contentPath" : "vue/index",
  54 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
  55 +}, {
  56 + "contentPath" : "js/student",
  57 + "filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
  58 +}, {
  59 + "contentPath" : "vue/StudentForm",
  60 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
  61 +}, {
  62 + "contentPath" : "vue/StudentContactForm",
  63 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
  64 +}, {
  65 + "contentPath" : "vue/StudentTeacherForm",
  66 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
  67 +} ]
0 \ No newline at end of file 68 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/ErrorCodeConstants_手动操作 0 → 100644
  1 +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
  2 +// ========== 学生 TODO 补充编号 ==========
  3 +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
0 \ No newline at end of file 4 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentContactDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生联系人 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student_contact")
  16 +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentContactDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 学生编号
  32 + */
  33 + private Long studentId;
  34 + /**
  35 + * 名字
  36 + */
  37 + private String name;
  38 + /**
  39 + * 简介
  40 + */
  41 + private String description;
  42 + /**
  43 + * 出生日期
  44 + */
  45 + private LocalDateTime birthday;
  46 + /**
  47 + * 性别
  48 + *
  49 + * 枚举 {@link TODO system_user_sex 对应的类}
  50 + */
  51 + private Integer sex;
  52 + /**
  53 + * 是否有效
  54 + *
  55 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  56 + */
  57 + private Boolean enabled;
  58 + /**
  59 + * 头像
  60 + */
  61 + private String avatar;
  62 + /**
  63 + * 附件
  64 + */
  65 + private String video;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String memo;
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentContactMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +/**
  13 + * 学生联系人 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
  19 +
  20 + default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {
  21 + return selectList(InfraStudentContactDO::getStudentId, studentId);
  22 + }
  23 +
  24 + default int deleteByStudentId(Long studentId) {
  25 + return delete(InfraStudentContactDO::getStudentId, studentId);
  26 + }
  27 +
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentController 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import javax.validation.constraints.*;
  12 +import javax.validation.*;
  13 +import javax.servlet.http.*;
  14 +import java.util.*;
  15 +import java.io.IOException;
  16 +
  17 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  18 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  19 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  20 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  24 +
  25 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
  27 +
  28 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  29 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  30 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  31 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  32 +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
  33 +
  34 +@Tag(name = "管理后台 - 学生")
  35 +@RestController
  36 +@RequestMapping("/infra/student")
  37 +@Validated
  38 +public class InfraStudentController {
  39 +
  40 + @Resource
  41 + private InfraStudentService studentService;
  42 +
  43 + @PostMapping("/create")
  44 + @Operation(summary = "创建学生")
  45 + @PreAuthorize("@ss.hasPermission('infra:student:create')")
  46 + public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
  47 + return success(studentService.createStudent(createReqVO));
  48 + }
  49 +
  50 + @PutMapping("/update")
  51 + @Operation(summary = "更新学生")
  52 + @PreAuthorize("@ss.hasPermission('infra:student:update')")
  53 + public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
  54 + studentService.updateStudent(updateReqVO);
  55 + return success(true);
  56 + }
  57 +
  58 + @DeleteMapping("/delete")
  59 + @Operation(summary = "删除学生")
  60 + @Parameter(name = "id", description = "编号", required = true)
  61 + @PreAuthorize("@ss.hasPermission('infra:student:delete')")
  62 + public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
  63 + studentService.deleteStudent(id);
  64 + return success(true);
  65 + }
  66 +
  67 + @GetMapping("/get")
  68 + @Operation(summary = "获得学生")
  69 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  70 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  71 + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
  72 + InfraStudentDO student = studentService.getStudent(id);
  73 + return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
  74 + }
  75 +
  76 + @GetMapping("/page")
  77 + @Operation(summary = "获得学生分页")
  78 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  79 + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
  80 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
  81 + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
  82 + }
  83 +
  84 + @GetMapping("/export-excel")
  85 + @Operation(summary = "导出学生 Excel")
  86 + @PreAuthorize("@ss.hasPermission('infra:student:export')")
  87 + @OperateLog(type = EXPORT)
  88 + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
  89 + HttpServletResponse response) throws IOException {
  90 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  91 + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
  92 + // 导出 Excel
  93 + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
  94 + BeanUtils.toBean(list, InfraStudentRespVO.class));
  95 + }
  96 +
  97 + // ==================== 子表(学生联系人) ====================
  98 +
  99 + @GetMapping("/student-contact/list-by-student-id")
  100 + @Operation(summary = "获得学生联系人列表")
  101 + @Parameter(name = "studentId", description = "学生编号")
  102 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  103 + public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
  104 + return success(studentService.getStudentContactListByStudentId(studentId));
  105 + }
  106 +
  107 + // ==================== 子表(学生班主任) ====================
  108 +
  109 + @GetMapping("/student-teacher/get-by-student-id")
  110 + @Operation(summary = "获得学生班主任")
  111 + @Parameter(name = "studentId", description = "学生编号")
  112 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  113 + public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
  114 + return success(studentService.getStudentTeacherByStudentId(studentId));
  115 + }
  116 +
  117 +}
0 \ No newline at end of file 118 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student")
  16 +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 名字
  32 + */
  33 + private String name;
  34 + /**
  35 + * 简介
  36 + */
  37 + private String description;
  38 + /**
  39 + * 出生日期
  40 + */
  41 + private LocalDateTime birthday;
  42 + /**
  43 + * 性别
  44 + *
  45 + * 枚举 {@link TODO system_user_sex 对应的类}
  46 + */
  47 + private Integer sex;
  48 + /**
  49 + * 是否有效
  50 + *
  51 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  52 + */
  53 + private Boolean enabled;
  54 + /**
  55 + * 头像
  56 + */
  57 + private String avatar;
  58 + /**
  59 + * 附件
  60 + */
  61 + private String video;
  62 + /**
  63 + * 备注
  64 + */
  65 + private String memo;
  66 +
  67 +}
0 \ No newline at end of file 68 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  11 +
  12 +/**
  13 + * 学生 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
  19 +
  20 + default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
  22 + .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
  23 + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
  24 + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
  25 + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
  26 + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(InfraStudentDO::getId));
  28 + }
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentPageReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - 学生分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class InfraStudentPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "名字", example = "芋头")
  19 + private String name;
  20 +
  21 + @Schema(description = "出生日期")
  22 + private LocalDateTime birthday;
  23 +
  24 + @Schema(description = "性别", example = "1")
  25 + private Integer sex;
  26 +
  27 + @Schema(description = "是否有效", example = "true")
  28 + private Boolean enabled;
  29 +
  30 + @Schema(description = "创建时间")
  31 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  32 + private LocalDateTime[] createTime;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentRespVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import java.util.*;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +import com.alibaba.excel.annotation.*;
  10 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  11 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  12 +
  13 +@Schema(description = "管理后台 - 学生 Response VO")
  14 +@Data
  15 +@ExcelIgnoreUnannotated
  16 +public class InfraStudentRespVO {
  17 +
  18 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @ExcelProperty("编号")
  20 + private Long id;
  21 +
  22 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  23 + @ExcelProperty("名字")
  24 + private String name;
  25 +
  26 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  27 + @ExcelProperty("简介")
  28 + private String description;
  29 +
  30 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @ExcelProperty("出生日期")
  32 + private LocalDateTime birthday;
  33 +
  34 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + @ExcelProperty(value = "性别", converter = DictConvert.class)
  36 + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  37 + private Integer sex;
  38 +
  39 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  40 + @ExcelProperty(value = "是否有效", converter = DictConvert.class)
  41 + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  42 + private Boolean enabled;
  43 +
  44 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  45 + @ExcelProperty("头像")
  46 + private String avatar;
  47 +
  48 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  49 + @ExcelProperty("附件")
  50 + private String video;
  51 +
  52 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  53 + @ExcelProperty("备注")
  54 + private String memo;
  55 +
  56 + @Schema(description = "创建时间")
  57 + @ExcelProperty("创建时间")
  58 + private LocalDateTime createTime;
  59 +
  60 +}
0 \ No newline at end of file 61 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentSaveReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import javax.validation.constraints.*;
  7 +import java.util.*;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +import java.time.LocalDateTime;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  12 +
  13 +@Schema(description = "管理后台 - 学生新增/修改 Request VO")
  14 +@Data
  15 +public class InfraStudentSaveReqVO {
  16 +
  17 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + private Long id;
  19 +
  20 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  21 + @NotEmpty(message = "名字不能为空")
  22 + private String name;
  23 +
  24 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  25 + @NotEmpty(message = "简介不能为空")
  26 + private String description;
  27 +
  28 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + @NotNull(message = "出生日期不能为空")
  30 + private LocalDateTime birthday;
  31 +
  32 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  33 + @NotNull(message = "性别不能为空")
  34 + private Integer sex;
  35 +
  36 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  37 + @NotNull(message = "是否有效不能为空")
  38 + private Boolean enabled;
  39 +
  40 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  41 + @NotEmpty(message = "头像不能为空")
  42 + private String avatar;
  43 +
  44 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  45 + @NotEmpty(message = "附件不能为空")
  46 + private String video;
  47 +
  48 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  49 + @NotEmpty(message = "备注不能为空")
  50 + private String memo;
  51 +
  52 + @Schema(description = "学生联系人列表")
  53 + private List<InfraStudentContactDO> studentContacts;
  54 +
  55 + @Schema(description = "学生班主任")
  56 + private InfraStudentTeacherDO studentTeacher;
  57 +
  58 +}
0 \ No newline at end of file 59 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentService 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import java.util.*;
  4 +import javax.validation.*;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  6 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  7 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  9 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  10 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  11 +
  12 +/**
  13 + * 学生 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface InfraStudentService {
  18 +
  19 + /**
  20 + * 创建学生
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新学生
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除学生
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deleteStudent(Long id);
  40 +
  41 + /**
  42 + * 获得学生
  43 + *
  44 + * @param id 编号
  45 + * @return 学生
  46 + */
  47 + InfraStudentDO getStudent(Long id);
  48 +
  49 + /**
  50 + * 获得学生分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return 学生分页
  54 + */
  55 + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
  56 +
  57 + // ==================== 子表(学生联系人) ====================
  58 +
  59 + /**
  60 + * 获得学生联系人列表
  61 + *
  62 + * @param studentId 学生编号
  63 + * @return 学生联系人列表
  64 + */
  65 + List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);
  66 +
  67 + // ==================== 子表(学生班主任) ====================
  68 +
  69 + /**
  70 + * 获得学生班主任
  71 + *
  72 + * @param studentId 学生编号
  73 + * @return 学生班主任
  74 + */
  75 + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
  76 +
  77 +}
0 \ No newline at end of file 78 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentServiceImpl 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.transaction.annotation.Transactional;
  7 +
  8 +import java.util.*;
  9 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  11 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  13 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  15 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  16 +
  17 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  18 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
  19 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +
  24 +/**
  25 + * 学生 Service 实现类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Service
  30 +@Validated
  31 +public class InfraStudentServiceImpl implements InfraStudentService {
  32 +
  33 + @Resource
  34 + private InfraStudentMapper studentMapper;
  35 + @Resource
  36 + private InfraStudentContactMapper studentContactMapper;
  37 + @Resource
  38 + private InfraStudentTeacherMapper studentTeacherMapper;
  39 +
  40 + @Override
  41 + @Transactional(rollbackFor = Exception.class)
  42 + public Long createStudent(InfraStudentSaveReqVO createReqVO) {
  43 + // 插入
  44 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
  45 + studentMapper.insert(student);
  46 +
  47 + // 插入子表
  48 + createStudentContactList(student.getId(), createReqVO.getStudentContacts());
  49 + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
  50 + // 返回
  51 + return student.getId();
  52 + }
  53 +
  54 + @Override
  55 + @Transactional(rollbackFor = Exception.class)
  56 + public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
  57 + // 校验存在
  58 + validateStudentExists(updateReqVO.getId());
  59 + // 更新
  60 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
  61 + studentMapper.updateById(updateObj);
  62 +
  63 + // 更新子表
  64 + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
  65 + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
  66 + }
  67 +
  68 + @Override
  69 + @Transactional(rollbackFor = Exception.class)
  70 + public void deleteStudent(Long id) {
  71 + // 校验存在
  72 + validateStudentExists(id);
  73 + // 删除
  74 + studentMapper.deleteById(id);
  75 +
  76 + // 删除子表
  77 + deleteStudentContactByStudentId(id);
  78 + deleteStudentTeacherByStudentId(id);
  79 + }
  80 +
  81 + private void validateStudentExists(Long id) {
  82 + if (studentMapper.selectById(id) == null) {
  83 + throw exception(STUDENT_NOT_EXISTS);
  84 + }
  85 + }
  86 +
  87 + @Override
  88 + public InfraStudentDO getStudent(Long id) {
  89 + return studentMapper.selectById(id);
  90 + }
  91 +
  92 + @Override
  93 + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
  94 + return studentMapper.selectPage(pageReqVO);
  95 + }
  96 +
  97 + // ==================== 子表(学生联系人) ====================
  98 +
  99 + @Override
  100 + public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {
  101 + return studentContactMapper.selectListByStudentId(studentId);
  102 + }
  103 +
  104 + private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
  105 + list.forEach(o -> o.setStudentId(studentId));
  106 + studentContactMapper.insertBatch(list);
  107 + }
  108 +
  109 + private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
  110 + deleteStudentContactByStudentId(studentId);
  111 + list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
  112 + createStudentContactList(studentId, list);
  113 + }
  114 +
  115 + private void deleteStudentContactByStudentId(Long studentId) {
  116 + studentContactMapper.deleteByStudentId(studentId);
  117 + }
  118 +
  119 + // ==================== 子表(学生班主任) ====================
  120 +
  121 + @Override
  122 + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
  123 + return studentTeacherMapper.selectByStudentId(studentId);
  124 + }
  125 +
  126 + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
  127 + if (studentTeacher == null) {
  128 + return;
  129 + }
  130 + studentTeacher.setStudentId(studentId);
  131 + studentTeacherMapper.insert(studentTeacher);
  132 + }
  133 +
  134 + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
  135 + if (studentTeacher == null) {
  136 + return;
  137 + }
  138 + studentTeacher.setStudentId(studentId);
  139 + studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
  140 + studentTeacherMapper.insertOrUpdate(studentTeacher);
  141 + }
  142 +
  143 + private void deleteStudentTeacherByStudentId(Long studentId) {
  144 + studentTeacherMapper.deleteByStudentId(studentId);
  145 + }
  146 +
  147 +}
0 \ No newline at end of file 148 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentServiceImplTest 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import javax.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  13 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import javax.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link InfraStudentServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(InfraStudentServiceImpl.class)
  37 +public class InfraStudentServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private InfraStudentServiceImpl studentService;
  41 +
  42 + @Resource
  43 + private InfraStudentMapper studentMapper;
  44 +
  45 + @Test
  46 + public void testCreateStudent_success() {
  47 + // 准备参数
  48 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long studentId = studentService.createStudent(createReqVO);
  52 + // 断言
  53 + assertNotNull(studentId);
  54 + // 校验记录的属性是否正确
  55 + InfraStudentDO student = studentMapper.selectById(studentId);
  56 + assertPojoEquals(createReqVO, student, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateStudent_success() {
  61 + // mock 数据
  62 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  63 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
  66 + o.setId(dbStudent.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + studentService.updateStudent(updateReqVO);
  71 + // 校验是否更新正确
  72 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, student);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateStudent_notExists() {
  78 + // 准备参数
  79 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteStudent_success() {
  87 + // mock 数据
  88 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  89 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbStudent.getId();
  92 +
  93 + // 调用
  94 + studentService.deleteStudent(id);
  95 + // 校验数据不存在了
  96 + assertNull(studentMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteStudent_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetStudentPage() {
  111 + // mock 数据
  112 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
  113 + o.setName(null);
  114 + o.setBirthday(null);
  115 + o.setSex(null);
  116 + o.setEnabled(null);
  117 + o.setCreateTime(null);
  118 + });
  119 + studentMapper.insert(dbStudent);
  120 + // 测试 name 不匹配
  121 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
  122 + // 测试 birthday 不匹配
  123 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
  124 + // 测试 sex 不匹配
  125 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
  126 + // 测试 enabled 不匹配
  127 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
  128 + // 测试 createTime 不匹配
  129 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
  130 + // 准备参数
  131 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
  132 + reqVO.setName(null);
  133 + reqVO.setBirthday(null);
  134 + reqVO.setSex(null);
  135 + reqVO.setEnabled(null);
  136 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  137 +
  138 + // 调用
  139 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
  140 + // 断言
  141 + assertEquals(1, pageResult.getTotal());
  142 + assertEquals(1, pageResult.getList().size());
  143 + assertPojoEquals(dbStudent, pageResult.getList().get(0));
  144 + }
  145 +
  146 +}
0 \ No newline at end of file 147 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentTeacherDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生班主任 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student_teacher")
  16 +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentTeacherDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 学生编号
  32 + */
  33 + private Long studentId;
  34 + /**
  35 + * 名字
  36 + */
  37 + private String name;
  38 + /**
  39 + * 简介
  40 + */
  41 + private String description;
  42 + /**
  43 + * 出生日期
  44 + */
  45 + private LocalDateTime birthday;
  46 + /**
  47 + * 性别
  48 + *
  49 + * 枚举 {@link TODO system_user_sex 对应的类}
  50 + */
  51 + private Integer sex;
  52 + /**
  53 + * 是否有效
  54 + *
  55 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  56 + */
  57 + private Boolean enabled;
  58 + /**
  59 + * 头像
  60 + */
  61 + private String avatar;
  62 + /**
  63 + * 附件
  64 + */
  65 + private String video;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String memo;
  70 +
  71 +}
0 \ No newline at end of file 72 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/java/InfraStudentTeacherMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +/**
  13 + * 学生班主任 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
  19 +
  20 + default InfraStudentTeacherDO selectByStudentId(Long studentId) {
  21 + return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
  22 + }
  23 +
  24 + default int deleteByStudentId(Long studentId) {
  25 + return delete(InfraStudentTeacherDO::getStudentId, studentId);
  26 + }
  27 +
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/js/student 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建学生
  4 +export function createStudent(data) {
  5 + return request({
  6 + url: '/infra/student/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新学生
  13 +export function updateStudent(data) {
  14 + return request({
  15 + url: '/infra/student/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除学生
  22 +export function deleteStudent(id) {
  23 + return request({
  24 + url: '/infra/student/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得学生
  30 +export function getStudent(id) {
  31 + return request({
  32 + url: '/infra/student/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得学生分页
  38 +export function getStudentPage(params) {
  39 + return request({
  40 + url: '/infra/student/page',
  41 + method: 'get',
  42 + params
  43 + })
  44 +}
  45 +// 导出学生 Excel
  46 +export function exportStudentExcel(params) {
  47 + return request({
  48 + url: '/infra/student/export-excel',
  49 + method: 'get',
  50 + params,
  51 + responseType: 'blob'
  52 + })
  53 +}
  54 +
  55 +// ==================== 子表(学生联系人) ====================
  56 +
  57 + // 获得学生联系人列表
  58 + export function getStudentContactListByStudentId(studentId) {
  59 + return request({
  60 + url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,
  61 + method: 'get'
  62 + })
  63 + }
  64 +
  65 +// ==================== 子表(学生班主任) ====================
  66 +
  67 + // 获得学生班主任
  68 + export function getStudentTeacherByStudentId(studentId) {
  69 + return request({
  70 + url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,
  71 + method: 'get'
  72 + })
  73 + }
  74 +
0 \ No newline at end of file 75 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/sql/h2 0 → 100644
  1 +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
  2 +CREATE TABLE IF NOT EXISTS "infra_student" (
  3 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  4 + "name" varchar NOT NULL,
  5 + "description" varchar NOT NULL,
  6 + "birthday" varchar NOT NULL,
  7 + "sex" int NOT NULL,
  8 + "enabled" bit NOT NULL,
  9 + "avatar" varchar NOT NULL,
  10 + "video" varchar NOT NULL,
  11 + "memo" varchar NOT NULL,
  12 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  13 + PRIMARY KEY ("id")
  14 +) COMMENT '学生表';
  15 +
  16 +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
  17 +DELETE FROM "infra_student";
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/sql/sql 0 → 100644
  1 +-- 菜单 SQL
  2 +INSERT INTO system_menu(
  3 + name, permission, type, sort, parent_id,
  4 + path, icon, component, status, component_name
  5 +)
  6 +VALUES (
  7 + '学生管理', '', 2, 0, 888,
  8 + 'student', '', 'infra/demo/index', 0, 'InfraStudent'
  9 +);
  10 +
  11 +-- 按钮父菜单ID
  12 +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
  13 +SELECT @parentId := LAST_INSERT_ID();
  14 +
  15 +-- 按钮 SQL
  16 +INSERT INTO system_menu(
  17 + name, permission, type, sort, parent_id,
  18 + path, icon, component, status
  19 +)
  20 +VALUES (
  21 + '学生查询', 'infra:student:query', 3, 1, @parentId,
  22 + '', '', '', 0
  23 +);
  24 +INSERT INTO system_menu(
  25 + name, permission, type, sort, parent_id,
  26 + path, icon, component, status
  27 +)
  28 +VALUES (
  29 + '学生创建', 'infra:student:create', 3, 2, @parentId,
  30 + '', '', '', 0
  31 +);
  32 +INSERT INTO system_menu(
  33 + name, permission, type, sort, parent_id,
  34 + path, icon, component, status
  35 +)
  36 +VALUES (
  37 + '学生更新', 'infra:student:update', 3, 3, @parentId,
  38 + '', '', '', 0
  39 +);
  40 +INSERT INTO system_menu(
  41 + name, permission, type, sort, parent_id,
  42 + path, icon, component, status
  43 +)
  44 +VALUES (
  45 + '学生删除', 'infra:student:delete', 3, 4, @parentId,
  46 + '', '', '', 0
  47 +);
  48 +INSERT INTO system_menu(
  49 + name, permission, type, sort, parent_id,
  50 + path, icon, component, status
  51 +)
  52 +VALUES (
  53 + '学生导出', 'infra:student:export', 3, 5, @parentId,
  54 + '', '', '', 0
  55 +);
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/vue/StudentContactForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-form
  4 + ref="formRef"
  5 + :model="formData"
  6 + :rules="formRules"
  7 + v-loading="formLoading"
  8 + label-width="0px"
  9 + :inline-message="true"
  10 + >
  11 + <el-table :data="formData" class="-mt-10px">
  12 + <el-table-column label="序号" type="index" width="100" />
  13 + <el-table-column label="名字" min-width="150">
  14 + <template v-slot="{ row, $index }">
  15 + <el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
  16 + <el-input v-model="row.name" placeholder="请输入名字" />
  17 + </el-form-item>
  18 + </template>
  19 + </el-table-column>
  20 + <el-table-column label="简介" min-width="200">
  21 + <template v-slot="{ row, $index }">
  22 + <el-form-item :prop="`${$index}.description`" :rules="formRules.description" class="mb-0px!">
  23 + <el-input v-model="row.description" type="textarea" placeholder="请输入简介" />
  24 + </el-form-item>
  25 + </template>
  26 + </el-table-column>
  27 + <el-table-column label="出生日期" min-width="150">
  28 + <template v-slot="{ row, $index }">
  29 + <el-form-item :prop="`${$index}.birthday`" :rules="formRules.birthday" class="mb-0px!">
  30 + <el-date-picker clearable v-model="row.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  31 + </el-form-item>
  32 + </template>
  33 + </el-table-column>
  34 + <el-table-column label="性别" min-width="150">
  35 + <template v-slot="{ row, $index }">
  36 + <el-form-item :prop="`${$index}.sex`" :rules="formRules.sex" class="mb-0px!">
  37 + <el-select v-model="row.sex" placeholder="请选择性别">
  38 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  39 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  40 + </el-select>
  41 + </el-form-item>
  42 + </template>
  43 + </el-table-column>
  44 + <el-table-column label="是否有效" min-width="150">
  45 + <template v-slot="{ row, $index }">
  46 + <el-form-item :prop="`${$index}.enabled`" :rules="formRules.enabled" class="mb-0px!">
  47 + <el-radio-group v-model="row.enabled">
  48 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  49 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  50 + </el-radio-group>
  51 + </el-form-item>
  52 + </template>
  53 + </el-table-column>
  54 + <el-table-column label="头像" min-width="200">
  55 + <template v-slot="{ row, $index }">
  56 + <el-form-item :prop="`${$index}.avatar`" :rules="formRules.avatar" class="mb-0px!">
  57 + <ImageUpload v-model="row.avatar"/>
  58 + </el-form-item>
  59 + </template>
  60 + </el-table-column>
  61 + <el-table-column label="附件" min-width="200">
  62 + <template v-slot="{ row, $index }">
  63 + <el-form-item :prop="`${$index}.video`" :rules="formRules.video" class="mb-0px!">
  64 + <FileUpload v-model="row.video"/>
  65 + </el-form-item>
  66 + </template>
  67 + </el-table-column>
  68 + <el-table-column label="备注" min-width="400">
  69 + <template v-slot="{ row, $index }">
  70 + <el-form-item :prop="`${$index}.memo`" :rules="formRules.memo" class="mb-0px!">
  71 + <Editor v-model="row.memo" :min-height="192"/>
  72 + </el-form-item>
  73 + </template>
  74 + </el-table-column>
  75 + <el-table-column align="center" fixed="right" label="操作" width="60">
  76 + <template v-slot="{ $index }">
  77 + <el-link @click="handleDelete($index)">—</el-link>
  78 + </template>
  79 + </el-table-column>
  80 + </el-table>
  81 + </el-form>
  82 + <el-row justify="center" class="mt-3">
  83 + <el-button @click="handleAdd" round>+ 添加学生联系人</el-button>
  84 + </el-row>
  85 + </div>
  86 +</template>
  87 +
  88 +<script>
  89 + import * as StudentApi from '@/api/infra/demo'
  90 + import ImageUpload from '@/components/ImageUpload';
  91 + import FileUpload from '@/components/FileUpload';
  92 + import Editor from '@/components/Editor';
  93 + export default {
  94 + name: "StudentContactForm",
  95 + components: {
  96 + ImageUpload,
  97 + FileUpload,
  98 + Editor,
  99 + },
  100 + props:[
  101 + 'studentId'
  102 + ],// 学生编号(主表的关联字段)
  103 + data() {
  104 + return {
  105 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  106 + formLoading: false,
  107 + // 表单参数
  108 + formData: [],
  109 + // 表单校验
  110 + formRules: {
  111 + studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
  112 + name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
  113 + description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
  114 + birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
  115 + sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
  116 + enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
  117 + avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
  118 + memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
  119 + },
  120 + };
  121 + },
  122 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  123 + studentId:{
  124 + handler(val) {
  125 + // 1. 重置表单
  126 + this.formData = []
  127 + // 2. val 非空,则加载数据
  128 + if (!val) {
  129 + return;
  130 + }
  131 + try {
  132 + this.formLoading = true;
  133 + const that = this;
  134 + StudentApi.getStudentContactListByStudentId(val).then(res=>{
  135 + that.formData = res.data;
  136 + })
  137 + } finally {
  138 + this.formLoading = false;
  139 + }
  140 + },
  141 + immediate: true
  142 + }
  143 + },
  144 + methods: {
  145 + /** 新增按钮操作 */
  146 + handleAdd() {
  147 + const row = {
  148 + id: undefined,
  149 + studentId: undefined,
  150 + name: undefined,
  151 + description: undefined,
  152 + birthday: undefined,
  153 + sex: undefined,
  154 + enabled: undefined,
  155 + avatar: undefined,
  156 + video: undefined,
  157 + memo: undefined,
  158 + }
  159 + row.studentId = this.studentId
  160 + this.formData.push(row)
  161 + },
  162 + /** 删除按钮操作 */
  163 + handleDelete(index) {
  164 + this.formData.splice(index, 1)
  165 + },
  166 + /** 表单校验 */
  167 + validate(){
  168 + return this.$refs["formRef"].validate()
  169 + },
  170 + /** 表单值 */
  171 + getData(){
  172 + return this.formData
  173 + }
  174 + }
  175 + };
  176 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/vue/StudentForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="简介" prop="description">
  10 + <el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
  11 + </el-form-item>
  12 + <el-form-item label="出生日期" prop="birthday">
  13 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  14 + </el-form-item>
  15 + <el-form-item label="性别" prop="sex">
  16 + <el-select v-model="formData.sex" placeholder="请选择性别">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  18 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="是否有效" prop="enabled">
  22 + <el-radio-group v-model="formData.enabled">
  23 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  24 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  25 + </el-radio-group>
  26 + </el-form-item>
  27 + <el-form-item label="头像">
  28 + <ImageUpload v-model="formData.avatar"/>
  29 + </el-form-item>
  30 + <el-form-item label="附件">
  31 + <FileUpload v-model="formData.video"/>
  32 + </el-form-item>
  33 + <el-form-item label="备注">
  34 + <Editor v-model="formData.memo" :min-height="192"/>
  35 + </el-form-item>
  36 + </el-form>
  37 + <!-- 子表的表单 -->
  38 + <el-tabs v-model="subTabsName">
  39 + <el-tab-pane label="学生联系人" name="studentContact">
  40 + <StudentContactForm ref="studentContactFormRef" :student-id="formData.id" />
  41 + </el-tab-pane>
  42 + <el-tab-pane label="学生班主任" name="studentTeacher">
  43 + <StudentTeacherForm ref="studentTeacherFormRef" :student-id="formData.id" />
  44 + </el-tab-pane>
  45 + </el-tabs>
  46 + <div slot="footer" class="dialog-footer">
  47 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  48 + <el-button @click="dialogVisible = false">取 消</el-button>
  49 + </div>
  50 + </el-dialog>
  51 + </div>
  52 +</template>
  53 +
  54 +<script>
  55 + import * as StudentApi from '@/api/infra/demo'
  56 + import ImageUpload from '@/components/ImageUpload';
  57 + import FileUpload from '@/components/FileUpload';
  58 + import Editor from '@/components/Editor';
  59 + import StudentContactForm from './components/StudentContactForm.vue'
  60 + import StudentTeacherForm from './components/StudentTeacherForm.vue'
  61 + export default {
  62 + name: "StudentForm",
  63 + components: {
  64 + ImageUpload,
  65 + FileUpload,
  66 + Editor,
  67 + StudentContactForm,
  68 + StudentTeacherForm,
  69 + },
  70 + data() {
  71 + return {
  72 + // 弹出层标题
  73 + dialogTitle: "",
  74 + // 是否显示弹出层
  75 + dialogVisible: false,
  76 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  77 + formLoading: false,
  78 + // 表单参数
  79 + formData: {
  80 + id: undefined,
  81 + name: undefined,
  82 + description: undefined,
  83 + birthday: undefined,
  84 + sex: undefined,
  85 + enabled: undefined,
  86 + avatar: undefined,
  87 + video: undefined,
  88 + memo: undefined,
  89 + },
  90 + // 表单校验
  91 + formRules: {
  92 + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
  93 + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
  94 + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
  95 + sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
  96 + enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
  97 + avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
  98 + video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
  99 + memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
  100 + },
  101 + /** 子表的表单 */
  102 + subTabsName: 'studentContact'
  103 + };
  104 + },
  105 + methods: {
  106 + /** 打开弹窗 */
  107 + open(id) {
  108 + this.dialogVisible = true;
  109 + this.reset();
  110 + const that = this;
  111 + // 修改时,设置数据
  112 + if (id) {
  113 + this.formLoading = true;
  114 + try {
  115 + StudentApi.getStudent(id).then(res=>{
  116 + that.formData = res.data;
  117 + that.title = "修改学生";
  118 + })
  119 + } finally {
  120 + this.formLoading = false;
  121 + }
  122 + }
  123 + this.title = "新增学生";
  124 + },
  125 + /** 提交按钮 */
  126 + submitForm() {
  127 + this.formLoading = true;
  128 + try {
  129 + const that = this;
  130 + let data = this.formData;
  131 + let validate = false;
  132 + // 校验主表
  133 + this.getRef("formRef").validate(valid => {
  134 + validate = valid;
  135 + });
  136 + // 校验子表
  137 + this.validateSubFrom01().then(() => {
  138 + // 全部校验通过-拼接子表的数据
  139 + // 拼接子表的数据
  140 + data.studentContacts = that.getRef('studentContactFormRef').getData();
  141 + data.studentTeacher = that.getRef('studentTeacherFormRef').getData();
  142 + }).catch((err) => {
  143 + validate = false;
  144 + that.subTabsName = err.replace("FormRef", ""); // 定位到没有校验通过的子表单
  145 + })
  146 + // 所有表单校验通过后方可提交
  147 + if (!validate) {
  148 + return;
  149 + }
  150 + // 修改的提交
  151 + if (data.id) {
  152 + StudentApi.updateStudent(data).then(response => {
  153 + that.$modal.msgSuccess("修改成功");
  154 + that.dialogVisible = false;
  155 + that.$emit('success');
  156 + });
  157 + return;
  158 + }
  159 + // 添加的提交
  160 + StudentApi.createStudent(data).then(response => {
  161 + that.$modal.msgSuccess("新增成功");
  162 + that.dialogVisible = false;
  163 + that.$emit('success');
  164 + });
  165 + }finally {
  166 + this.formLoading = false;
  167 + }
  168 + },
  169 + getRef(refName){
  170 + return this.$refs[refName];
  171 + },
  172 + /** 校验子表单 */
  173 + validateSubFrom(item) {
  174 + return new Promise((resolve, reject) => {
  175 + this.getRef(item).validate()
  176 + .then(() => {
  177 + resolve();
  178 + })
  179 + .catch(() => {
  180 + reject(item);
  181 + })
  182 + })
  183 + },
  184 + /** 校验所有子表单 */
  185 + validateSubFrom01() {
  186 + // 需要校验的表单 ref
  187 + const validFormRefArr = [
  188 + "studentContactFormRef",
  189 + "studentTeacherFormRef",
  190 + ];
  191 + const validArr = []; // 校验
  192 + for (const item of validFormRefArr) {
  193 + validArr.push(this.validateSubFrom(item));
  194 + }
  195 + return new Promise((resolve, reject) => {
  196 + // 校验所有
  197 + Promise.all(validArr).then(() => {
  198 + resolve();
  199 + }).catch((err) => {
  200 + reject(err);
  201 + })
  202 + })
  203 + },
  204 + /** 表单重置 */
  205 + reset() {
  206 + this.formData = {
  207 + id: undefined,
  208 + name: undefined,
  209 + description: undefined,
  210 + birthday: undefined,
  211 + sex: undefined,
  212 + enabled: undefined,
  213 + avatar: undefined,
  214 + video: undefined,
  215 + memo: undefined,
  216 + };
  217 + this.resetForm("formRef");
  218 + },
  219 + }
  220 + };
  221 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/vue/StudentTeacherForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-form
  4 + ref="formRef"
  5 + :model="formData"
  6 + :rules="formRules"
  7 + label-width="100px"
  8 + v-loading="formLoading"
  9 + >
  10 + <el-form-item label="名字" prop="name">
  11 + <el-input v-model="formData.name" placeholder="请输入名字" />
  12 + </el-form-item>
  13 + <el-form-item label="简介" prop="description">
  14 + <el-input v-model="formData.description" type="textarea" placeholder="请输入简介" />
  15 + </el-form-item>
  16 + <el-form-item label="出生日期" prop="birthday">
  17 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  18 + </el-form-item>
  19 + <el-form-item label="性别" prop="sex">
  20 + <el-select v-model="formData.sex" placeholder="请选择性别">
  21 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  22 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  23 + </el-select>
  24 + </el-form-item>
  25 + <el-form-item label="是否有效" prop="enabled">
  26 + <el-radio-group v-model="formData.enabled">
  27 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  28 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  29 + </el-radio-group>
  30 + </el-form-item>
  31 + <el-form-item label="头像">
  32 + <ImageUpload v-model="formData.avatar"/>
  33 + </el-form-item>
  34 + <el-form-item label="附件">
  35 + <FileUpload v-model="formData.video"/>
  36 + </el-form-item>
  37 + <el-form-item label="备注">
  38 + <Editor v-model="formData.memo" :min-height="192"/>
  39 + </el-form-item>
  40 + </el-form>
  41 + </div>
  42 +</template>
  43 +
  44 +<script>
  45 + import * as StudentApi from '@/api/infra/demo'
  46 + import ImageUpload from '@/components/ImageUpload';
  47 + import FileUpload from '@/components/FileUpload';
  48 + import Editor from '@/components/Editor';
  49 + export default {
  50 + name: "StudentTeacherForm",
  51 + components: {
  52 + ImageUpload,
  53 + FileUpload,
  54 + Editor,
  55 + },
  56 + props:[
  57 + 'studentId'
  58 + ],// 学生编号(主表的关联字段)
  59 + data() {
  60 + return {
  61 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  62 + formLoading: false,
  63 + // 表单参数
  64 + formData: [],
  65 + // 表单校验
  66 + formRules: {
  67 + studentId: [{ required: true, message: "学生编号不能为空", trigger: "blur" }],
  68 + name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
  69 + description: [{ required: true, message: "简介不能为空", trigger: "blur" }],
  70 + birthday: [{ required: true, message: "出生日期不能为空", trigger: "blur" }],
  71 + sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
  72 + enabled: [{ required: true, message: "是否有效不能为空", trigger: "blur" }],
  73 + avatar: [{ required: true, message: "头像不能为空", trigger: "blur" }],
  74 + memo: [{ required: true, message: "备注不能为空", trigger: "blur" }],
  75 + },
  76 + };
  77 + },
  78 + watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
  79 + studentId:{
  80 + handler(val) {
  81 + // 1. 重置表单
  82 + this.formData = {
  83 + id: undefined,
  84 + studentId: undefined,
  85 + name: undefined,
  86 + description: undefined,
  87 + birthday: undefined,
  88 + sex: undefined,
  89 + enabled: undefined,
  90 + avatar: undefined,
  91 + video: undefined,
  92 + memo: undefined,
  93 + }
  94 + // 2. val 非空,则加载数据
  95 + if (!val) {
  96 + return;
  97 + }
  98 + try {
  99 + this.formLoading = true;
  100 + const that = this;
  101 + StudentApi.getStudentTeacherByStudentId(val).then(res=>{
  102 + const data = res.data;
  103 + if (!data) {
  104 + return
  105 + }
  106 + that.formData = data;
  107 + })
  108 + } finally {
  109 + this.formLoading = false;
  110 + }
  111 + },
  112 + immediate: true
  113 + }
  114 + },
  115 + methods: {
  116 + /** 表单校验 */
  117 + validate(){
  118 + return this.$refs["formRef"].validate()
  119 + },
  120 + /** 表单值 */
  121 + getData(){
  122 + return this.formData
  123 + }
  124 + }
  125 + };
  126 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/vue/index 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="出生日期" prop="birthday">
  10 + <el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
  11 + </el-form-item>
  12 + <el-form-item label="性别" prop="sex">
  13 + <el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
  14 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  15 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item label="是否有效" prop="enabled">
  19 + <el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
  20 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  21 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  22 + </el-select>
  23 + </el-form-item>
  24 + <el-form-item label="创建时间" prop="createTime">
  25 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  26 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  27 + </el-form-item>
  28 + <el-form-item>
  29 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  30 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  31 + </el-form-item>
  32 + </el-form>
  33 +
  34 + <!-- 操作工具栏 -->
  35 + <el-row :gutter="10" class="mb8">
  36 + <el-col :span="1.5">
  37 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  38 + v-hasPermi="['infra:student:create']">新增</el-button>
  39 + </el-col>
  40 + <el-col :span="1.5">
  41 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  42 + v-hasPermi="['infra:student:export']">导出</el-button>
  43 + </el-col>
  44 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  45 + </el-row>
  46 +
  47 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  48 + <el-table-column label="编号" align="center" prop="id">
  49 + <template v-slot="scope">
  50 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column label="名字" align="center" prop="name">
  54 + <template v-slot="scope">
  55 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column label="简介" align="center" prop="description">
  59 + <template v-slot="scope">
  60 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
  61 + </template>
  62 + </el-table-column>
  63 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  64 + <template v-slot="scope">
  65 + <span>{{ parseTime(scope.row.birthday) }}</span>
  66 + </template>
  67 + </el-table-column>
  68 + <el-table-column label="性别" align="center" prop="sex">
  69 + <template v-slot="scope">
  70 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  71 + </template>
  72 + </el-table-column>
  73 + <el-table-column label="是否有效" align="center" prop="enabled">
  74 + <template v-slot="scope">
  75 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  76 + </template>
  77 + </el-table-column>
  78 + <el-table-column label="头像" align="center" prop="avatar">
  79 + <template v-slot="scope">
  80 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
  81 + </template>
  82 + </el-table-column>
  83 + <el-table-column label="附件" align="center" prop="video">
  84 + <template v-slot="scope">
  85 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
  86 + </template>
  87 + </el-table-column>
  88 + <el-table-column label="备注" align="center" prop="memo">
  89 + <template v-slot="scope">
  90 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
  91 + </template>
  92 + </el-table-column>
  93 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  94 + <template v-slot="scope">
  95 + <span>{{ parseTime(scope.row.createTime) }}</span>
  96 + </template>
  97 + </el-table-column>
  98 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  99 + <template v-slot="scope">
  100 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  101 + v-hasPermi="['infra:student:update']">修改</el-button>
  102 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  103 + v-hasPermi="['infra:student:delete']">删除</el-button>
  104 + </template>
  105 + </el-table-column>
  106 + </el-table>
  107 + <!-- 分页组件 -->
  108 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  109 + @pagination="getList"/>
  110 + <!-- 对话框(添加 / 修改) -->
  111 + <StudentForm ref="formRef" @success="getList" />
  112 + </div>
  113 +</template>
  114 +
  115 +<script>
  116 +import * as StudentApi from '@/api/infra/demo';
  117 +import StudentForm from './StudentForm.vue';
  118 +export default {
  119 + name: "Student",
  120 + components: {
  121 + StudentForm,
  122 + },
  123 + data() {
  124 + return {
  125 + // 遮罩层
  126 + loading: true,
  127 + // 导出遮罩层
  128 + exportLoading: false,
  129 + // 显示搜索条件
  130 + showSearch: true,
  131 + // 总条数
  132 + total: 0,
  133 + // 学生列表
  134 + list: [],
  135 + // 是否展开,默认全部展开
  136 + isExpandAll: true,
  137 + // 重新渲染表格状态
  138 + refreshTable: true,
  139 + // 选中行
  140 + currentRow: {},
  141 + // 查询参数
  142 + queryParams: {
  143 + pageNo: 1,
  144 + pageSize: 10,
  145 + name: null,
  146 + birthday: null,
  147 + sex: null,
  148 + enabled: null,
  149 + createTime: [],
  150 + },
  151 + };
  152 + },
  153 + created() {
  154 + this.getList();
  155 + },
  156 + methods: {
  157 + /** 查询列表 */
  158 + getList() {
  159 + try {
  160 + this.loading = true;
  161 + StudentApi.getStudentPage(this.queryParams).then(response => {
  162 + this.list = response.data.list;
  163 + this.total = response.data.total;
  164 + });
  165 + } finally {
  166 + this.loading = false;
  167 + }
  168 + },
  169 + /** 搜索按钮操作 */
  170 + handleQuery() {
  171 + this.queryParams.pageNo = 1;
  172 + this.getList();
  173 + },
  174 + /** 重置按钮操作 */
  175 + resetQuery() {
  176 + this.resetForm("queryForm");
  177 + this.handleQuery();
  178 + },
  179 + /** 添加/修改操作 */
  180 + openForm(id) {
  181 + this.$refs["formRef"].open(id);
  182 + },
  183 + /** 删除按钮操作 */
  184 + handleDelete(row) {
  185 + const that = this;
  186 + try {
  187 + const id = row.id;
  188 + this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
  189 + return StudentApi.deleteStudent(id);
  190 + }).then(() => {
  191 + that.getList();
  192 + that.$modal.msgSuccess("删除成功");
  193 + }).catch(() => {});
  194 + } catch {}
  195 + },
  196 + /** 导出按钮操作 */
  197 + handleExport() {
  198 + const that = this;
  199 + try {
  200 + this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
  201 + that.exportLoading = true;
  202 + return StudentApi.exportStudentExcel(params);
  203 + }).then(response => {
  204 + that.$download.excel(response, '学生.xls');
  205 + });
  206 + } catch {
  207 + } finally {
  208 + that.exportLoading = false;
  209 + }
  210 + },
  211 + }
  212 +};
  213 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_master_normal/xml/InfraStudentMapper 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/assert.json 0 → 100644
  1 +[ {
  2 + "contentPath" : "java/InfraStudentPageReqVO",
  3 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
  4 +}, {
  5 + "contentPath" : "java/InfraStudentRespVO",
  6 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
  7 +}, {
  8 + "contentPath" : "java/InfraStudentSaveReqVO",
  9 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
  10 +}, {
  11 + "contentPath" : "java/InfraStudentController",
  12 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
  13 +}, {
  14 + "contentPath" : "java/InfraStudentDO",
  15 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
  16 +}, {
  17 + "contentPath" : "java/InfraStudentMapper",
  18 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
  19 +}, {
  20 + "contentPath" : "xml/InfraStudentMapper",
  21 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
  22 +}, {
  23 + "contentPath" : "java/InfraStudentServiceImpl",
  24 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
  25 +}, {
  26 + "contentPath" : "java/InfraStudentService",
  27 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
  28 +}, {
  29 + "contentPath" : "java/InfraStudentServiceImplTest",
  30 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
  31 +}, {
  32 + "contentPath" : "java/ErrorCodeConstants_手动操作",
  33 + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
  34 +}, {
  35 + "contentPath" : "sql/sql",
  36 + "filePath" : "sql/sql.sql"
  37 +}, {
  38 + "contentPath" : "sql/h2",
  39 + "filePath" : "sql/h2.sql"
  40 +}, {
  41 + "contentPath" : "vue/index",
  42 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
  43 +}, {
  44 + "contentPath" : "js/student",
  45 + "filePath" : "yudao-ui-admin-vue2/src/api/infra/student.js"
  46 +}, {
  47 + "contentPath" : "vue/StudentForm",
  48 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
  49 +} ]
0 \ No newline at end of file 50 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/ErrorCodeConstants_手动操作 0 → 100644
  1 +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
  2 +// ========== 学生 TODO 补充编号 ==========
  3 +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
0 \ No newline at end of file 4 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentController 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import javax.validation.constraints.*;
  12 +import javax.validation.*;
  13 +import javax.servlet.http.*;
  14 +import java.util.*;
  15 +import java.io.IOException;
  16 +
  17 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  18 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  19 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  20 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  24 +
  25 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
  27 +
  28 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  29 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  30 +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
  31 +
  32 +@Tag(name = "管理后台 - 学生")
  33 +@RestController
  34 +@RequestMapping("/infra/student")
  35 +@Validated
  36 +public class InfraStudentController {
  37 +
  38 + @Resource
  39 + private InfraStudentService studentService;
  40 +
  41 + @PostMapping("/create")
  42 + @Operation(summary = "创建学生")
  43 + @PreAuthorize("@ss.hasPermission('infra:student:create')")
  44 + public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
  45 + return success(studentService.createStudent(createReqVO));
  46 + }
  47 +
  48 + @PutMapping("/update")
  49 + @Operation(summary = "更新学生")
  50 + @PreAuthorize("@ss.hasPermission('infra:student:update')")
  51 + public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
  52 + studentService.updateStudent(updateReqVO);
  53 + return success(true);
  54 + }
  55 +
  56 + @DeleteMapping("/delete")
  57 + @Operation(summary = "删除学生")
  58 + @Parameter(name = "id", description = "编号", required = true)
  59 + @PreAuthorize("@ss.hasPermission('infra:student:delete')")
  60 + public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
  61 + studentService.deleteStudent(id);
  62 + return success(true);
  63 + }
  64 +
  65 + @GetMapping("/get")
  66 + @Operation(summary = "获得学生")
  67 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  68 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  69 + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
  70 + InfraStudentDO student = studentService.getStudent(id);
  71 + return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
  72 + }
  73 +
  74 + @GetMapping("/page")
  75 + @Operation(summary = "获得学生分页")
  76 + @PreAuthorize("@ss.hasPermission('infra:student:query')")
  77 + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
  78 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
  79 + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
  80 + }
  81 +
  82 + @GetMapping("/export-excel")
  83 + @Operation(summary = "导出学生 Excel")
  84 + @PreAuthorize("@ss.hasPermission('infra:student:export')")
  85 + @OperateLog(type = EXPORT)
  86 + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
  87 + HttpServletResponse response) throws IOException {
  88 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  89 + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
  90 + // 导出 Excel
  91 + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
  92 + BeanUtils.toBean(list, InfraStudentRespVO.class));
  93 + }
  94 +
  95 +}
0 \ No newline at end of file 96 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * 学生 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("infra_student")
  16 +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class InfraStudentDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 名字
  32 + */
  33 + private String name;
  34 + /**
  35 + * 简介
  36 + */
  37 + private String description;
  38 + /**
  39 + * 出生日期
  40 + */
  41 + private LocalDateTime birthday;
  42 + /**
  43 + * 性别
  44 + *
  45 + * 枚举 {@link TODO system_user_sex 对应的类}
  46 + */
  47 + private Integer sex;
  48 + /**
  49 + * 是否有效
  50 + *
  51 + * 枚举 {@link TODO infra_boolean_string 对应的类}
  52 + */
  53 + private Boolean enabled;
  54 + /**
  55 + * 头像
  56 + */
  57 + private String avatar;
  58 + /**
  59 + * 附件
  60 + */
  61 + private String video;
  62 + /**
  63 + * 备注
  64 + */
  65 + private String memo;
  66 +
  67 +}
0 \ No newline at end of file 68 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  11 +
  12 +/**
  13 + * 学生 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
  19 +
  20 + default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
  22 + .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
  23 + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
  24 + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
  25 + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
  26 + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(InfraStudentDO::getId));
  28 + }
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentPageReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - 学生分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class InfraStudentPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "名字", example = "芋头")
  19 + private String name;
  20 +
  21 + @Schema(description = "出生日期")
  22 + private LocalDateTime birthday;
  23 +
  24 + @Schema(description = "性别", example = "1")
  25 + private Integer sex;
  26 +
  27 + @Schema(description = "是否有效", example = "true")
  28 + private Boolean enabled;
  29 +
  30 + @Schema(description = "创建时间")
  31 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  32 + private LocalDateTime[] createTime;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentRespVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import java.util.*;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +import com.alibaba.excel.annotation.*;
  10 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  11 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  12 +
  13 +@Schema(description = "管理后台 - 学生 Response VO")
  14 +@Data
  15 +@ExcelIgnoreUnannotated
  16 +public class InfraStudentRespVO {
  17 +
  18 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  19 + @ExcelProperty("编号")
  20 + private Long id;
  21 +
  22 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  23 + @ExcelProperty("名字")
  24 + private String name;
  25 +
  26 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  27 + @ExcelProperty("简介")
  28 + private String description;
  29 +
  30 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @ExcelProperty("出生日期")
  32 + private LocalDateTime birthday;
  33 +
  34 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + @ExcelProperty(value = "性别", converter = DictConvert.class)
  36 + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  37 + private Integer sex;
  38 +
  39 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  40 + @ExcelProperty(value = "是否有效", converter = DictConvert.class)
  41 + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
  42 + private Boolean enabled;
  43 +
  44 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  45 + @ExcelProperty("头像")
  46 + private String avatar;
  47 +
  48 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  49 + @ExcelProperty("附件")
  50 + private String video;
  51 +
  52 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  53 + @ExcelProperty("备注")
  54 + private String memo;
  55 +
  56 + @Schema(description = "创建时间")
  57 + @ExcelProperty("创建时间")
  58 + private LocalDateTime createTime;
  59 +
  60 +}
0 \ No newline at end of file 61 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentSaveReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import javax.validation.constraints.*;
  7 +import java.util.*;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - 学生新增/修改 Request VO")
  12 +@Data
  13 +public class InfraStudentSaveReqVO {
  14 +
  15 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long id;
  17 +
  18 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  19 + @NotEmpty(message = "名字不能为空")
  20 + private String name;
  21 +
  22 + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
  23 + @NotEmpty(message = "简介不能为空")
  24 + private String description;
  25 +
  26 + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
  27 + @NotNull(message = "出生日期不能为空")
  28 + private LocalDateTime birthday;
  29 +
  30 + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  31 + @NotNull(message = "性别不能为空")
  32 + private Integer sex;
  33 +
  34 + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  35 + @NotNull(message = "是否有效不能为空")
  36 + private Boolean enabled;
  37 +
  38 + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  39 + @NotEmpty(message = "头像不能为空")
  40 + private String avatar;
  41 +
  42 + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
  43 + @NotEmpty(message = "附件不能为空")
  44 + private String video;
  45 +
  46 + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
  47 + @NotEmpty(message = "备注不能为空")
  48 + private String memo;
  49 +
  50 +}
0 \ No newline at end of file 51 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentService 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import java.util.*;
  4 +import javax.validation.*;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  6 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  9 +
  10 +/**
  11 + * 学生 Service 接口
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +public interface InfraStudentService {
  16 +
  17 + /**
  18 + * 创建学生
  19 + *
  20 + * @param createReqVO 创建信息
  21 + * @return 编号
  22 + */
  23 + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
  24 +
  25 + /**
  26 + * 更新学生
  27 + *
  28 + * @param updateReqVO 更新信息
  29 + */
  30 + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
  31 +
  32 + /**
  33 + * 删除学生
  34 + *
  35 + * @param id 编号
  36 + */
  37 + void deleteStudent(Long id);
  38 +
  39 + /**
  40 + * 获得学生
  41 + *
  42 + * @param id 编号
  43 + * @return 学生
  44 + */
  45 + InfraStudentDO getStudent(Long id);
  46 +
  47 + /**
  48 + * 获得学生分页
  49 + *
  50 + * @param pageReqVO 分页查询
  51 + * @return 学生分页
  52 + */
  53 + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
  54 +
  55 +}
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentServiceImpl 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.transaction.annotation.Transactional;
  7 +
  8 +import java.util.*;
  9 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  11 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  12 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  13 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  14 +
  15 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  18 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  19 +
  20 +/**
  21 + * 学生 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +@Validated
  27 +public class InfraStudentServiceImpl implements InfraStudentService {
  28 +
  29 + @Resource
  30 + private InfraStudentMapper studentMapper;
  31 +
  32 + @Override
  33 + public Long createStudent(InfraStudentSaveReqVO createReqVO) {
  34 + // 插入
  35 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
  36 + studentMapper.insert(student);
  37 + // 返回
  38 + return student.getId();
  39 + }
  40 +
  41 + @Override
  42 + public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
  43 + // 校验存在
  44 + validateStudentExists(updateReqVO.getId());
  45 + // 更新
  46 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
  47 + studentMapper.updateById(updateObj);
  48 + }
  49 +
  50 + @Override
  51 + public void deleteStudent(Long id) {
  52 + // 校验存在
  53 + validateStudentExists(id);
  54 + // 删除
  55 + studentMapper.deleteById(id);
  56 + }
  57 +
  58 + private void validateStudentExists(Long id) {
  59 + if (studentMapper.selectById(id) == null) {
  60 + throw exception(STUDENT_NOT_EXISTS);
  61 + }
  62 + }
  63 +
  64 + @Override
  65 + public InfraStudentDO getStudent(Long id) {
  66 + return studentMapper.selectById(id);
  67 + }
  68 +
  69 + @Override
  70 + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
  71 + return studentMapper.selectPage(pageReqVO);
  72 + }
  73 +
  74 +}
0 \ No newline at end of file 75 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/java/InfraStudentServiceImplTest 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import javax.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
  13 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import javax.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link InfraStudentServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(InfraStudentServiceImpl.class)
  37 +public class InfraStudentServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private InfraStudentServiceImpl studentService;
  41 +
  42 + @Resource
  43 + private InfraStudentMapper studentMapper;
  44 +
  45 + @Test
  46 + public void testCreateStudent_success() {
  47 + // 准备参数
  48 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long studentId = studentService.createStudent(createReqVO);
  52 + // 断言
  53 + assertNotNull(studentId);
  54 + // 校验记录的属性是否正确
  55 + InfraStudentDO student = studentMapper.selectById(studentId);
  56 + assertPojoEquals(createReqVO, student, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateStudent_success() {
  61 + // mock 数据
  62 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  63 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
  66 + o.setId(dbStudent.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + studentService.updateStudent(updateReqVO);
  71 + // 校验是否更新正确
  72 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, student);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateStudent_notExists() {
  78 + // 准备参数
  79 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteStudent_success() {
  87 + // mock 数据
  88 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
  89 + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbStudent.getId();
  92 +
  93 + // 调用
  94 + studentService.deleteStudent(id);
  95 + // 校验数据不存在了
  96 + assertNull(studentMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteStudent_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetStudentPage() {
  111 + // mock 数据
  112 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
  113 + o.setName(null);
  114 + o.setBirthday(null);
  115 + o.setSex(null);
  116 + o.setEnabled(null);
  117 + o.setCreateTime(null);
  118 + });
  119 + studentMapper.insert(dbStudent);
  120 + // 测试 name 不匹配
  121 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
  122 + // 测试 birthday 不匹配
  123 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
  124 + // 测试 sex 不匹配
  125 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
  126 + // 测试 enabled 不匹配
  127 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
  128 + // 测试 createTime 不匹配
  129 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
  130 + // 准备参数
  131 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
  132 + reqVO.setName(null);
  133 + reqVO.setBirthday(null);
  134 + reqVO.setSex(null);
  135 + reqVO.setEnabled(null);
  136 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  137 +
  138 + // 调用
  139 + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
  140 + // 断言
  141 + assertEquals(1, pageResult.getTotal());
  142 + assertEquals(1, pageResult.getList().size());
  143 + assertPojoEquals(dbStudent, pageResult.getList().get(0));
  144 + }
  145 +
  146 +}
0 \ No newline at end of file 147 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/js/student 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建学生
  4 +export function createStudent(data) {
  5 + return request({
  6 + url: '/infra/student/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新学生
  13 +export function updateStudent(data) {
  14 + return request({
  15 + url: '/infra/student/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除学生
  22 +export function deleteStudent(id) {
  23 + return request({
  24 + url: '/infra/student/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得学生
  30 +export function getStudent(id) {
  31 + return request({
  32 + url: '/infra/student/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得学生分页
  38 +export function getStudentPage(params) {
  39 + return request({
  40 + url: '/infra/student/page',
  41 + method: 'get',
  42 + params
  43 + })
  44 +}
  45 +// 导出学生 Excel
  46 +export function exportStudentExcel(params) {
  47 + return request({
  48 + url: '/infra/student/export-excel',
  49 + method: 'get',
  50 + params,
  51 + responseType: 'blob'
  52 + })
  53 +}
0 \ No newline at end of file 54 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/sql/h2 0 → 100644
  1 +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
  2 +CREATE TABLE IF NOT EXISTS "infra_student" (
  3 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  4 + "name" varchar NOT NULL,
  5 + "description" varchar NOT NULL,
  6 + "birthday" varchar NOT NULL,
  7 + "sex" int NOT NULL,
  8 + "enabled" bit NOT NULL,
  9 + "avatar" varchar NOT NULL,
  10 + "video" varchar NOT NULL,
  11 + "memo" varchar NOT NULL,
  12 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  13 + PRIMARY KEY ("id")
  14 +) COMMENT '学生表';
  15 +
  16 +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
  17 +DELETE FROM "infra_student";
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/sql/sql 0 → 100644
  1 +-- 菜单 SQL
  2 +INSERT INTO system_menu(
  3 + name, permission, type, sort, parent_id,
  4 + path, icon, component, status, component_name
  5 +)
  6 +VALUES (
  7 + '学生管理', '', 2, 0, 888,
  8 + 'student', '', 'infra/demo/index', 0, 'InfraStudent'
  9 +);
  10 +
  11 +-- 按钮父菜单ID
  12 +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
  13 +SELECT @parentId := LAST_INSERT_ID();
  14 +
  15 +-- 按钮 SQL
  16 +INSERT INTO system_menu(
  17 + name, permission, type, sort, parent_id,
  18 + path, icon, component, status
  19 +)
  20 +VALUES (
  21 + '学生查询', 'infra:student:query', 3, 1, @parentId,
  22 + '', '', '', 0
  23 +);
  24 +INSERT INTO system_menu(
  25 + name, permission, type, sort, parent_id,
  26 + path, icon, component, status
  27 +)
  28 +VALUES (
  29 + '学生创建', 'infra:student:create', 3, 2, @parentId,
  30 + '', '', '', 0
  31 +);
  32 +INSERT INTO system_menu(
  33 + name, permission, type, sort, parent_id,
  34 + path, icon, component, status
  35 +)
  36 +VALUES (
  37 + '学生更新', 'infra:student:update', 3, 3, @parentId,
  38 + '', '', '', 0
  39 +);
  40 +INSERT INTO system_menu(
  41 + name, permission, type, sort, parent_id,
  42 + path, icon, component, status
  43 +)
  44 +VALUES (
  45 + '学生删除', 'infra:student:delete', 3, 4, @parentId,
  46 + '', '', '', 0
  47 +);
  48 +INSERT INTO system_menu(
  49 + name, permission, type, sort, parent_id,
  50 + path, icon, component, status
  51 +)
  52 +VALUES (
  53 + '学生导出', 'infra:student:export', 3, 5, @parentId,
  54 + '', '', '', 0
  55 +);
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/vue/StudentForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="简介" prop="description">
  10 + <el-input v-model="formData.description" type="textarea" placeholder="请输入内容" />
  11 + </el-form-item>
  12 + <el-form-item label="出生日期" prop="birthday">
  13 + <el-date-picker clearable v-model="formData.birthday" type="date" value-format="timestamp" placeholder="选择出生日期" />
  14 + </el-form-item>
  15 + <el-form-item label="性别" prop="sex">
  16 + <el-select v-model="formData.sex" placeholder="请选择性别">
  17 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  18 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
  19 + </el-select>
  20 + </el-form-item>
  21 + <el-form-item label="是否有效" prop="enabled">
  22 + <el-radio-group v-model="formData.enabled">
  23 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  24 + :key="dict.value" :label="dict.value">{{dict.label}}</el-radio>
  25 + </el-radio-group>
  26 + </el-form-item>
  27 + <el-form-item label="头像">
  28 + <ImageUpload v-model="formData.avatar"/>
  29 + </el-form-item>
  30 + <el-form-item label="附件">
  31 + <FileUpload v-model="formData.video"/>
  32 + </el-form-item>
  33 + <el-form-item label="备注">
  34 + <Editor v-model="formData.memo" :min-height="192"/>
  35 + </el-form-item>
  36 + </el-form>
  37 + <div slot="footer" class="dialog-footer">
  38 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  39 + <el-button @click="dialogVisible = false">取 消</el-button>
  40 + </div>
  41 + </el-dialog>
  42 + </div>
  43 +</template>
  44 +
  45 +<script>
  46 + import * as StudentApi from '@/api/infra/demo'
  47 + import ImageUpload from '@/components/ImageUpload';
  48 + import FileUpload from '@/components/FileUpload';
  49 + import Editor from '@/components/Editor';
  50 + export default {
  51 + name: "StudentForm",
  52 + components: {
  53 + ImageUpload,
  54 + FileUpload,
  55 + Editor,
  56 + },
  57 + data() {
  58 + return {
  59 + // 弹出层标题
  60 + dialogTitle: "",
  61 + // 是否显示弹出层
  62 + dialogVisible: false,
  63 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  64 + formLoading: false,
  65 + // 表单参数
  66 + formData: {
  67 + id: undefined,
  68 + name: undefined,
  69 + description: undefined,
  70 + birthday: undefined,
  71 + sex: undefined,
  72 + enabled: undefined,
  73 + avatar: undefined,
  74 + video: undefined,
  75 + memo: undefined,
  76 + },
  77 + // 表单校验
  78 + formRules: {
  79 + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
  80 + description: [{ required: true, message: '简介不能为空', trigger: 'blur' }],
  81 + birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
  82 + sex: [{ required: true, message: '性别不能为空', trigger: 'change' }],
  83 + enabled: [{ required: true, message: '是否有效不能为空', trigger: 'blur' }],
  84 + avatar: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
  85 + video: [{ required: true, message: '附件不能为空', trigger: 'blur' }],
  86 + memo: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
  87 + },
  88 + };
  89 + },
  90 + methods: {
  91 + /** 打开弹窗 */
  92 + open(id) {
  93 + this.dialogVisible = true;
  94 + this.reset();
  95 + const that = this;
  96 + // 修改时,设置数据
  97 + if (id) {
  98 + this.formLoading = true;
  99 + try {
  100 + StudentApi.getStudent(id).then(res=>{
  101 + that.formData = res.data;
  102 + that.title = "修改学生";
  103 + })
  104 + } finally {
  105 + this.formLoading = false;
  106 + }
  107 + }
  108 + this.title = "新增学生";
  109 + },
  110 + /** 提交按钮 */
  111 + submitForm() {
  112 + this.formLoading = true;
  113 + try {
  114 + const that = this;
  115 + let data = this.formData;
  116 + let validate = false;
  117 + // 校验主表
  118 + this.getRef("formRef").validate(valid => {
  119 + validate = valid;
  120 + });
  121 + // 所有表单校验通过后方可提交
  122 + if (!validate) {
  123 + return;
  124 + }
  125 + // 修改的提交
  126 + if (data.id) {
  127 + StudentApi.updateStudent(data).then(response => {
  128 + that.$modal.msgSuccess("修改成功");
  129 + that.dialogVisible = false;
  130 + that.$emit('success');
  131 + });
  132 + return;
  133 + }
  134 + // 添加的提交
  135 + StudentApi.createStudent(data).then(response => {
  136 + that.$modal.msgSuccess("新增成功");
  137 + that.dialogVisible = false;
  138 + that.$emit('success');
  139 + });
  140 + }finally {
  141 + this.formLoading = false;
  142 + }
  143 + },
  144 + getRef(refName){
  145 + return this.$refs[refName];
  146 + },
  147 + /** 表单重置 */
  148 + reset() {
  149 + this.formData = {
  150 + id: undefined,
  151 + name: undefined,
  152 + description: undefined,
  153 + birthday: undefined,
  154 + sex: undefined,
  155 + enabled: undefined,
  156 + avatar: undefined,
  157 + video: undefined,
  158 + memo: undefined,
  159 + };
  160 + this.resetForm("formRef");
  161 + },
  162 + }
  163 + };
  164 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/vue/index 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="出生日期" prop="birthday">
  10 + <el-date-picker clearable v-model="queryParams.birthday" type="date" value-format="yyyy-MM-dd" placeholder="选择出生日期" />
  11 + </el-form-item>
  12 + <el-form-item label="性别" prop="sex">
  13 + <el-select v-model="queryParams.sex" placeholder="请选择性别" clearable size="small">
  14 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_USER_SEX)"
  15 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item label="是否有效" prop="enabled">
  19 + <el-select v-model="queryParams.enabled" placeholder="请选择是否有效" clearable size="small">
  20 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  21 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  22 + </el-select>
  23 + </el-form-item>
  24 + <el-form-item label="创建时间" prop="createTime">
  25 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  26 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  27 + </el-form-item>
  28 + <el-form-item>
  29 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  30 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  31 + </el-form-item>
  32 + </el-form>
  33 +
  34 + <!-- 操作工具栏 -->
  35 + <el-row :gutter="10" class="mb8">
  36 + <el-col :span="1.5">
  37 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  38 + v-hasPermi="['infra:student:create']">新增</el-button>
  39 + </el-col>
  40 + <el-col :span="1.5">
  41 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  42 + v-hasPermi="['infra:student:export']">导出</el-button>
  43 + </el-col>
  44 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  45 + </el-row>
  46 +
  47 + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  48 + <el-table-column label="编号" align="center" prop="id">
  49 + <template v-slot="scope">
  50 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column label="名字" align="center" prop="name">
  54 + <template v-slot="scope">
  55 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column label="简介" align="center" prop="description">
  59 + <template v-slot="scope">
  60 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.description" />
  61 + </template>
  62 + </el-table-column>
  63 + <el-table-column label="出生日期" align="center" prop="birthday" width="180">
  64 + <template v-slot="scope">
  65 + <span>{{ parseTime(scope.row.birthday) }}</span>
  66 + </template>
  67 + </el-table-column>
  68 + <el-table-column label="性别" align="center" prop="sex">
  69 + <template v-slot="scope">
  70 + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
  71 + </template>
  72 + </el-table-column>
  73 + <el-table-column label="是否有效" align="center" prop="enabled">
  74 + <template v-slot="scope">
  75 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.enabled" />
  76 + </template>
  77 + </el-table-column>
  78 + <el-table-column label="头像" align="center" prop="avatar">
  79 + <template v-slot="scope">
  80 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.avatar" />
  81 + </template>
  82 + </el-table-column>
  83 + <el-table-column label="附件" align="center" prop="video">
  84 + <template v-slot="scope">
  85 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.video" />
  86 + </template>
  87 + </el-table-column>
  88 + <el-table-column label="备注" align="center" prop="memo">
  89 + <template v-slot="scope">
  90 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.memo" />
  91 + </template>
  92 + </el-table-column>
  93 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  94 + <template v-slot="scope">
  95 + <span>{{ parseTime(scope.row.createTime) }}</span>
  96 + </template>
  97 + </el-table-column>
  98 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  99 + <template v-slot="scope">
  100 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  101 + v-hasPermi="['infra:student:update']">修改</el-button>
  102 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  103 + v-hasPermi="['infra:student:delete']">删除</el-button>
  104 + </template>
  105 + </el-table-column>
  106 + </el-table>
  107 + <!-- 分页组件 -->
  108 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  109 + @pagination="getList"/>
  110 + <!-- 对话框(添加 / 修改) -->
  111 + <StudentForm ref="formRef" @success="getList" />
  112 + </div>
  113 +</template>
  114 +
  115 +<script>
  116 +import * as StudentApi from '@/api/infra/demo';
  117 +import StudentForm from './StudentForm.vue';
  118 +export default {
  119 + name: "Student",
  120 + components: {
  121 + StudentForm,
  122 + },
  123 + data() {
  124 + return {
  125 + // 遮罩层
  126 + loading: true,
  127 + // 导出遮罩层
  128 + exportLoading: false,
  129 + // 显示搜索条件
  130 + showSearch: true,
  131 + // 总条数
  132 + total: 0,
  133 + // 学生列表
  134 + list: [],
  135 + // 是否展开,默认全部展开
  136 + isExpandAll: true,
  137 + // 重新渲染表格状态
  138 + refreshTable: true,
  139 + // 选中行
  140 + currentRow: {},
  141 + // 查询参数
  142 + queryParams: {
  143 + pageNo: 1,
  144 + pageSize: 10,
  145 + name: null,
  146 + birthday: null,
  147 + sex: null,
  148 + enabled: null,
  149 + createTime: [],
  150 + },
  151 + };
  152 + },
  153 + created() {
  154 + this.getList();
  155 + },
  156 + methods: {
  157 + /** 查询列表 */
  158 + getList() {
  159 + try {
  160 + this.loading = true;
  161 + StudentApi.getStudentPage(this.queryParams).then(response => {
  162 + this.list = response.data.list;
  163 + this.total = response.data.total;
  164 + });
  165 + } finally {
  166 + this.loading = false;
  167 + }
  168 + },
  169 + /** 搜索按钮操作 */
  170 + handleQuery() {
  171 + this.queryParams.pageNo = 1;
  172 + this.getList();
  173 + },
  174 + /** 重置按钮操作 */
  175 + resetQuery() {
  176 + this.resetForm("queryForm");
  177 + this.handleQuery();
  178 + },
  179 + /** 添加/修改操作 */
  180 + openForm(id) {
  181 + this.$refs["formRef"].open(id);
  182 + },
  183 + /** 删除按钮操作 */
  184 + handleDelete(row) {
  185 + const that = this;
  186 + try {
  187 + const id = row.id;
  188 + this.$modal.confirm('是否确认删除学生编号为"' + id + '"的数据项?').then(()=>{
  189 + return StudentApi.deleteStudent(id);
  190 + }).then(() => {
  191 + that.getList();
  192 + that.$modal.msgSuccess("删除成功");
  193 + }).catch(() => {});
  194 + } catch {}
  195 + },
  196 + /** 导出按钮操作 */
  197 + handleExport() {
  198 + const that = this;
  199 + try {
  200 + this.$modal.confirm('是否确认导出所有学生数据项?').then(() => {
  201 + that.exportLoading = true;
  202 + return StudentApi.exportStudentExcel(params);
  203 + }).then(response => {
  204 + that.$download.excel(response, '学生.xls');
  205 + });
  206 + } catch {
  207 + } finally {
  208 + that.exportLoading = false;
  209 + }
  210 + },
  211 + }
  212 +};
  213 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one/xml/InfraStudentMapper 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/assert.json 0 → 100644
  1 +[ {
  2 + "contentPath" : "java/InfraCategoryListReqVO",
  3 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java"
  4 +}, {
  5 + "contentPath" : "java/InfraCategoryRespVO",
  6 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java"
  7 +}, {
  8 + "contentPath" : "java/InfraCategorySaveReqVO",
  9 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java"
  10 +}, {
  11 + "contentPath" : "java/InfraCategoryController",
  12 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java"
  13 +}, {
  14 + "contentPath" : "java/InfraCategoryDO",
  15 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java"
  16 +}, {
  17 + "contentPath" : "java/InfraCategoryMapper",
  18 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java"
  19 +}, {
  20 + "contentPath" : "xml/InfraCategoryMapper",
  21 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml"
  22 +}, {
  23 + "contentPath" : "java/InfraCategoryServiceImpl",
  24 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java"
  25 +}, {
  26 + "contentPath" : "java/InfraCategoryService",
  27 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java"
  28 +}, {
  29 + "contentPath" : "java/InfraCategoryServiceImplTest",
  30 + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java"
  31 +}, {
  32 + "contentPath" : "java/ErrorCodeConstants_手动操作",
  33 + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
  34 +}, {
  35 + "contentPath" : "sql/sql",
  36 + "filePath" : "sql/sql.sql"
  37 +}, {
  38 + "contentPath" : "sql/h2",
  39 + "filePath" : "sql/h2.sql"
  40 +}, {
  41 + "contentPath" : "vue/index",
  42 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
  43 +}, {
  44 + "contentPath" : "js/category",
  45 + "filePath" : "yudao-ui-admin-vue2/src/api/infra/category.js"
  46 +}, {
  47 + "contentPath" : "vue/CategoryForm",
  48 + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/CategoryForm.vue"
  49 +} ]
0 \ No newline at end of file 50 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/ErrorCodeConstants_手动操作 0 → 100644
  1 +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
  2 +// ========== 分类 TODO 补充编号 ==========
  3 +ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在");
  4 +ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除");
  5 +ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在");
  6 +ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类");
  7 +ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类");
  8 +ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory");
0 \ No newline at end of file 9 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryController 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import javax.validation.constraints.*;
  12 +import javax.validation.*;
  13 +import javax.servlet.http.*;
  14 +import java.util.*;
  15 +import java.io.IOException;
  16 +
  17 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  18 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  19 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  20 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  24 +
  25 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  26 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
  27 +
  28 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  29 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
  30 +import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService;
  31 +
  32 +@Tag(name = "管理后台 - 分类")
  33 +@RestController
  34 +@RequestMapping("/infra/category")
  35 +@Validated
  36 +public class InfraCategoryController {
  37 +
  38 + @Resource
  39 + private InfraCategoryService categoryService;
  40 +
  41 + @PostMapping("/create")
  42 + @Operation(summary = "创建分类")
  43 + @PreAuthorize("@ss.hasPermission('infra:category:create')")
  44 + public CommonResult<Long> createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) {
  45 + return success(categoryService.createCategory(createReqVO));
  46 + }
  47 +
  48 + @PutMapping("/update")
  49 + @Operation(summary = "更新分类")
  50 + @PreAuthorize("@ss.hasPermission('infra:category:update')")
  51 + public CommonResult<Boolean> updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) {
  52 + categoryService.updateCategory(updateReqVO);
  53 + return success(true);
  54 + }
  55 +
  56 + @DeleteMapping("/delete")
  57 + @Operation(summary = "删除分类")
  58 + @Parameter(name = "id", description = "编号", required = true)
  59 + @PreAuthorize("@ss.hasPermission('infra:category:delete')")
  60 + public CommonResult<Boolean> deleteCategory(@RequestParam("id") Long id) {
  61 + categoryService.deleteCategory(id);
  62 + return success(true);
  63 + }
  64 +
  65 + @GetMapping("/get")
  66 + @Operation(summary = "获得分类")
  67 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  68 + @PreAuthorize("@ss.hasPermission('infra:category:query')")
  69 + public CommonResult<InfraCategoryRespVO> getCategory(@RequestParam("id") Long id) {
  70 + InfraCategoryDO category = categoryService.getCategory(id);
  71 + return success(BeanUtils.toBean(category, InfraCategoryRespVO.class));
  72 + }
  73 +
  74 + @GetMapping("/list")
  75 + @Operation(summary = "获得分类列表")
  76 + @PreAuthorize("@ss.hasPermission('infra:category:query')")
  77 + public CommonResult<List<InfraCategoryRespVO>> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) {
  78 + List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);
  79 + return success(BeanUtils.toBean(list, InfraCategoryRespVO.class));
  80 + }
  81 +
  82 + @GetMapping("/export-excel")
  83 + @Operation(summary = "导出分类 Excel")
  84 + @PreAuthorize("@ss.hasPermission('infra:category:export')")
  85 + @OperateLog(type = EXPORT)
  86 + public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO,
  87 + HttpServletResponse response) throws IOException {
  88 + List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO);
  89 + // 导出 Excel
  90 + ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class,
  91 + BeanUtils.toBean(list, InfraCategoryRespVO.class));
  92 + }
  93 +
  94 +}
0 \ No newline at end of file 95 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryDO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import com.baomidou.mybatisplus.annotation.*;
  6 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  7 +
  8 +/**
  9 + * 分类 DO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@TableName("infra_category")
  14 +@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +@Builder
  19 +@NoArgsConstructor
  20 +@AllArgsConstructor
  21 +public class InfraCategoryDO extends BaseDO {
  22 +
  23 + public static final Long PARENT_ID_ROOT = 0L;
  24 +
  25 + /**
  26 + * 编号
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 名字
  32 + */
  33 + private String name;
  34 + /**
  35 + * 父编号
  36 + */
  37 + private Long parentId;
  38 +
  39 +}
0 \ No newline at end of file 40 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryListReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +
  8 +@Schema(description = "管理后台 - 分类列表 Request VO")
  9 +@Data
  10 +public class InfraCategoryListReqVO {
  11 +
  12 + @Schema(description = "名字", example = "芋头")
  13 + private String name;
  14 +
  15 +}
0 \ No newline at end of file 16 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryMapper 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.demo;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  11 +
  12 +/**
  13 + * 分类 Mapper
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface InfraCategoryMapper extends BaseMapperX<InfraCategoryDO> {
  19 +
  20 + default List<InfraCategoryDO> selectList(InfraCategoryListReqVO reqVO) {
  21 + return selectList(new LambdaQueryWrapperX<InfraCategoryDO>()
  22 + .likeIfPresent(InfraCategoryDO::getName, reqVO.getName())
  23 + .orderByDesc(InfraCategoryDO::getId));
  24 + }
  25 +
  26 + default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) {
  27 + return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name);
  28 + }
  29 +
  30 + default Long selectCountByParentId(Long parentId) {
  31 + return selectCount(InfraCategoryDO::getParentId, parentId);
  32 + }
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryRespVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import java.util.*;
  7 +import com.alibaba.excel.annotation.*;
  8 +
  9 +@Schema(description = "管理后台 - 分类 Response VO")
  10 +@Data
  11 +@ExcelIgnoreUnannotated
  12 +public class InfraCategoryRespVO {
  13 +
  14 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + @ExcelProperty("编号")
  16 + private Long id;
  17 +
  18 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  19 + @ExcelProperty("名字")
  20 + private String name;
  21 +
  22 + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  23 + @ExcelProperty("父编号")
  24 + private Long parentId;
  25 +
  26 +}
0 \ No newline at end of file 27 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategorySaveReqVO 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import javax.validation.constraints.*;
  7 +import java.util.*;
  8 +
  9 +@Schema(description = "管理后台 - 分类新增/修改 Request VO")
  10 +@Data
  11 +public class InfraCategorySaveReqVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long id;
  15 +
  16 + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
  17 + @NotEmpty(message = "名字不能为空")
  18 + private String name;
  19 +
  20 + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  21 + @NotNull(message = "父编号不能为空")
  22 + private Long parentId;
  23 +
  24 +}
0 \ No newline at end of file 25 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryService 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import java.util.*;
  4 +import javax.validation.*;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  6 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  9 +
  10 +/**
  11 + * 分类 Service 接口
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +public interface InfraCategoryService {
  16 +
  17 + /**
  18 + * 创建分类
  19 + *
  20 + * @param createReqVO 创建信息
  21 + * @return 编号
  22 + */
  23 + Long createCategory(@Valid InfraCategorySaveReqVO createReqVO);
  24 +
  25 + /**
  26 + * 更新分类
  27 + *
  28 + * @param updateReqVO 更新信息
  29 + */
  30 + void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO);
  31 +
  32 + /**
  33 + * 删除分类
  34 + *
  35 + * @param id 编号
  36 + */
  37 + void deleteCategory(Long id);
  38 +
  39 + /**
  40 + * 获得分类
  41 + *
  42 + * @param id 编号
  43 + * @return 分类
  44 + */
  45 + InfraCategoryDO getCategory(Long id);
  46 +
  47 + /**
  48 + * 获得分类列表
  49 + *
  50 + * @param listReqVO 查询条件
  51 + * @return 分类列表
  52 + */
  53 + List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO);
  54 +
  55 +}
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryServiceImpl 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import javax.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.transaction.annotation.Transactional;
  7 +
  8 +import java.util.*;
  9 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
  11 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  12 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  13 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  14 +
  15 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  18 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  19 +
  20 +/**
  21 + * 分类 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +@Validated
  27 +public class InfraCategoryServiceImpl implements InfraCategoryService {
  28 +
  29 + @Resource
  30 + private InfraCategoryMapper categoryMapper;
  31 +
  32 + @Override
  33 + public Long createCategory(InfraCategorySaveReqVO createReqVO) {
  34 + // 校验父编号的有效性
  35 + validateParentCategory(null, createReqVO.getParentId());
  36 + // 校验名字的唯一性
  37 + validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
  38 +
  39 + // 插入
  40 + InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class);
  41 + categoryMapper.insert(category);
  42 + // 返回
  43 + return category.getId();
  44 + }
  45 +
  46 + @Override
  47 + public void updateCategory(InfraCategorySaveReqVO updateReqVO) {
  48 + // 校验存在
  49 + validateCategoryExists(updateReqVO.getId());
  50 + // 校验父编号的有效性
  51 + validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());
  52 + // 校验名字的唯一性
  53 + validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
  54 +
  55 + // 更新
  56 + InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class);
  57 + categoryMapper.updateById(updateObj);
  58 + }
  59 +
  60 + @Override
  61 + public void deleteCategory(Long id) {
  62 + // 校验存在
  63 + validateCategoryExists(id);
  64 + // 校验是否有子分类
  65 + if (categoryMapper.selectCountByParentId(id) > 0) {
  66 + throw exception(CATEGORY_EXITS_CHILDREN);
  67 + }
  68 + // 删除
  69 + categoryMapper.deleteById(id);
  70 + }
  71 +
  72 + private void validateCategoryExists(Long id) {
  73 + if (categoryMapper.selectById(id) == null) {
  74 + throw exception(CATEGORY_NOT_EXISTS);
  75 + }
  76 + }
  77 +
  78 + private void validateParentCategory(Long id, Long parentId) {
  79 + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
  80 + return;
  81 + }
  82 + // 1. 不能设置自己为父分类
  83 + if (Objects.equals(id, parentId)) {
  84 + throw exception(CATEGORY_PARENT_ERROR);
  85 + }
  86 + // 2. 父分类不存在
  87 + CategoryDO parentCategory = categoryMapper.selectById(parentId);
  88 + if (parentCategory == null) {
  89 + throw exception(CATEGORY_PARENT_NOT_EXITS);
  90 + }
  91 + // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路
  92 + if (id == null) { // id 为空,说明新增,不需要考虑环路
  93 + return;
  94 + }
  95 + for (int i = 0; i < Short.MAX_VALUE; i++) {
  96 + // 3.1 校验环路
  97 + parentId = parentCategory.getParentId();
  98 + if (Objects.equals(id, parentId)) {
  99 + throw exception(CATEGORY_PARENT_IS_CHILD);
  100 + }
  101 + // 3.2 继续递归下一级父分类
  102 + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
  103 + break;
  104 + }
  105 + parentCategory = categoryMapper.selectById(parentId);
  106 + if (parentCategory == null) {
  107 + break;
  108 + }
  109 + }
  110 + }
  111 +
  112 + private void validateCategoryNameUnique(Long id, Long parentId, String name) {
  113 + CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);
  114 + if (category == null) {
  115 + return;
  116 + }
  117 + // 如果 id 为空,说明不用比较是否为相同 id 的分类
  118 + if (id == null) {
  119 + throw exception(CATEGORY_NAME_DUPLICATE);
  120 + }
  121 + if (!Objects.equals(category.getId(), id)) {
  122 + throw exception(CATEGORY_NAME_DUPLICATE);
  123 + }
  124 + }
  125 +
  126 + @Override
  127 + public InfraCategoryDO getCategory(Long id) {
  128 + return categoryMapper.selectById(id);
  129 + }
  130 +
  131 + @Override
  132 + public List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO) {
  133 + return categoryMapper.selectList(listReqVO);
  134 + }
  135 +
  136 +}
0 \ No newline at end of file 137 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/java/InfraCategoryServiceImplTest 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.service.demo;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import javax.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
  12 +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
  13 +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import javax.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link InfraCategoryServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(InfraCategoryServiceImpl.class)
  37 +public class InfraCategoryServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private InfraCategoryServiceImpl categoryService;
  41 +
  42 + @Resource
  43 + private InfraCategoryMapper categoryMapper;
  44 +
  45 + @Test
  46 + public void testCreateCategory_success() {
  47 + // 准备参数
  48 + InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long categoryId = categoryService.createCategory(createReqVO);
  52 + // 断言
  53 + assertNotNull(categoryId);
  54 + // 校验记录的属性是否正确
  55 + InfraCategoryDO category = categoryMapper.selectById(categoryId);
  56 + assertPojoEquals(createReqVO, category, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdateCategory_success() {
  61 + // mock 数据
  62 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);
  63 + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> {
  66 + o.setId(dbCategory.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + categoryService.updateCategory(updateReqVO);
  71 + // 校验是否更新正确
  72 + InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, category);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdateCategory_notExists() {
  78 + // 准备参数
  79 + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeleteCategory_success() {
  87 + // mock 数据
  88 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);
  89 + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbCategory.getId();
  92 +
  93 + // 调用
  94 + categoryService.deleteCategory(id);
  95 + // 校验数据不存在了
  96 + assertNull(categoryMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeleteCategory_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetCategoryList() {
  111 + // mock 数据
  112 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到
  113 + o.setName(null);
  114 + });
  115 + categoryMapper.insert(dbCategory);
  116 + // 测试 name 不匹配
  117 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
  118 + // 准备参数
  119 + InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO();
  120 + reqVO.setName(null);
  121 +
  122 + // 调用
  123 + List<InfraCategoryDO> list = categoryService.getCategoryList(reqVO);
  124 + // 断言
  125 + assertEquals(1, list.size());
  126 + assertPojoEquals(dbCategory, list.get(0));
  127 + }
  128 +
  129 +}
0 \ No newline at end of file 130 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/js/category 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建分类
  4 +export function createCategory(data) {
  5 + return request({
  6 + url: '/infra/category/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新分类
  13 +export function updateCategory(data) {
  14 + return request({
  15 + url: '/infra/category/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除分类
  22 +export function deleteCategory(id) {
  23 + return request({
  24 + url: '/infra/category/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得分类
  30 +export function getCategory(id) {
  31 + return request({
  32 + url: '/infra/category/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得分类列表
  38 +export function getCategoryList(params) {
  39 + return request({
  40 + url: '/infra/category/list',
  41 + method: 'get',
  42 + params
  43 + })
  44 +}
  45 +// 导出分类 Excel
  46 +export function exportCategoryExcel(params) {
  47 + return request({
  48 + url: '/infra/category/export-excel',
  49 + method: 'get',
  50 + params,
  51 + responseType: 'blob'
  52 + })
  53 +}
0 \ No newline at end of file 54 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/sql/h2 0 → 100644
  1 +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
  2 +CREATE TABLE IF NOT EXISTS "infra_category" (
  3 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  4 + "name" varchar NOT NULL,
  5 + "description" bigint NOT NULL,
  6 + PRIMARY KEY ("id")
  7 +) COMMENT '分类表';
  8 +
  9 +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
  10 +DELETE FROM "infra_category";
0 \ No newline at end of file 11 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/sql/sql 0 → 100644
  1 +-- 菜单 SQL
  2 +INSERT INTO system_menu(
  3 + name, permission, type, sort, parent_id,
  4 + path, icon, component, status, component_name
  5 +)
  6 +VALUES (
  7 + '分类管理', '', 2, 0, 888,
  8 + 'category', '', 'infra/demo/index', 0, 'InfraCategory'
  9 +);
  10 +
  11 +-- 按钮父菜单ID
  12 +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
  13 +SELECT @parentId := LAST_INSERT_ID();
  14 +
  15 +-- 按钮 SQL
  16 +INSERT INTO system_menu(
  17 + name, permission, type, sort, parent_id,
  18 + path, icon, component, status
  19 +)
  20 +VALUES (
  21 + '分类查询', 'infra:category:query', 3, 1, @parentId,
  22 + '', '', '', 0
  23 +);
  24 +INSERT INTO system_menu(
  25 + name, permission, type, sort, parent_id,
  26 + path, icon, component, status
  27 +)
  28 +VALUES (
  29 + '分类创建', 'infra:category:create', 3, 2, @parentId,
  30 + '', '', '', 0
  31 +);
  32 +INSERT INTO system_menu(
  33 + name, permission, type, sort, parent_id,
  34 + path, icon, component, status
  35 +)
  36 +VALUES (
  37 + '分类更新', 'infra:category:update', 3, 3, @parentId,
  38 + '', '', '', 0
  39 +);
  40 +INSERT INTO system_menu(
  41 + name, permission, type, sort, parent_id,
  42 + path, icon, component, status
  43 +)
  44 +VALUES (
  45 + '分类删除', 'infra:category:delete', 3, 4, @parentId,
  46 + '', '', '', 0
  47 +);
  48 +INSERT INTO system_menu(
  49 + name, permission, type, sort, parent_id,
  50 + path, icon, component, status
  51 +)
  52 +VALUES (
  53 + '分类导出', 'infra:category:export', 3, 5, @parentId,
  54 + '', '', '', 0
  55 +);
0 \ No newline at end of file 56 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/vue/CategoryForm 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 对话框(添加 / 修改) -->
  4 + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
  5 + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="formData.name" placeholder="请输入名字" />
  8 + </el-form-item>
  9 + <el-form-item label="父编号" prop="parentId">
  10 + <TreeSelect
  11 + v-model="formData.parentId"
  12 + :options="categoryTree"
  13 + :normalizer="normalizer"
  14 + placeholder="请选择父编号"
  15 + />
  16 + </el-form-item>
  17 + </el-form>
  18 + <div slot="footer" class="dialog-footer">
  19 + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
  20 + <el-button @click="dialogVisible = false">取 消</el-button>
  21 + </div>
  22 + </el-dialog>
  23 + </div>
  24 +</template>
  25 +
  26 +<script>
  27 + import * as CategoryApi from '@/api/infra/demo'
  28 + import TreeSelect from "@riophae/vue-treeselect";
  29 + import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  30 + export default {
  31 + name: "CategoryForm",
  32 + components: {
  33 + TreeSelect,
  34 + },
  35 + data() {
  36 + return {
  37 + // 弹出层标题
  38 + dialogTitle: "",
  39 + // 是否显示弹出层
  40 + dialogVisible: false,
  41 + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  42 + formLoading: false,
  43 + // 表单参数
  44 + formData: {
  45 + id: undefined,
  46 + name: undefined,
  47 + parentId: undefined,
  48 + },
  49 + // 表单校验
  50 + formRules: {
  51 + name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
  52 + parentId: [{ required: true, message: '父编号不能为空', trigger: 'blur' }],
  53 + },
  54 + categoryTree: [], // 树形结构
  55 + };
  56 + },
  57 + methods: {
  58 + /** 打开弹窗 */
  59 + open(id) {
  60 + this.dialogVisible = true;
  61 + this.reset();
  62 + const that = this;
  63 + // 修改时,设置数据
  64 + if (id) {
  65 + this.formLoading = true;
  66 + try {
  67 + CategoryApi.getCategory(id).then(res=>{
  68 + that.formData = res.data;
  69 + that.title = "修改分类";
  70 + })
  71 + } finally {
  72 + this.formLoading = false;
  73 + }
  74 + }
  75 + this.title = "新增分类";
  76 + this.getCategoryTree();
  77 + },
  78 + /** 提交按钮 */
  79 + submitForm() {
  80 + this.formLoading = true;
  81 + try {
  82 + const that = this;
  83 + let data = this.formData;
  84 + let validate = false;
  85 + // 校验主表
  86 + this.getRef("formRef").validate(valid => {
  87 + validate = valid;
  88 + });
  89 + // 所有表单校验通过后方可提交
  90 + if (!validate) {
  91 + return;
  92 + }
  93 + // 修改的提交
  94 + if (data.id) {
  95 + CategoryApi.updateCategory(data).then(response => {
  96 + that.$modal.msgSuccess("修改成功");
  97 + that.dialogVisible = false;
  98 + that.$emit('success');
  99 + });
  100 + return;
  101 + }
  102 + // 添加的提交
  103 + CategoryApi.createCategory(data).then(response => {
  104 + that.$modal.msgSuccess("新增成功");
  105 + that.dialogVisible = false;
  106 + that.$emit('success');
  107 + });
  108 + }finally {
  109 + this.formLoading = false;
  110 + }
  111 + },
  112 + getRef(refName){
  113 + return this.$refs[refName];
  114 + },
  115 + /** 获得分类树 */
  116 + getCategoryTree() {
  117 + const that = this;
  118 + that.categoryTree = [];
  119 + CategoryApi.getCategoryList().then(res=>{
  120 + const root = { id: 0, name: '顶级分类', children: [] };
  121 + root.children = this.handleTree(res.data, 'id', 'parentId')
  122 + that.categoryTree.push(root)
  123 + });
  124 + },
  125 + /** 转换分类数据结构 */
  126 + normalizer(node) {
  127 + if (node.children && !node.children.length) {
  128 + delete node.children;
  129 + }
  130 + return {
  131 + id: node.id,
  132 + label: node.name,
  133 + children: node.children
  134 + };
  135 + },
  136 + /** 表单重置 */
  137 + reset() {
  138 + this.formData = {
  139 + id: undefined,
  140 + name: undefined,
  141 + parentId: undefined,
  142 + };
  143 + this.resetForm("formRef");
  144 + },
  145 + }
  146 + };
  147 +</script>
0 \ No newline at end of file 148 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/vue/index 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="名字" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入名字" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item>
  10 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  11 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  12 + </el-form-item>
  13 + </el-form>
  14 +
  15 + <!-- 操作工具栏 -->
  16 + <el-row :gutter="10" class="mb8">
  17 + <el-col :span="1.5">
  18 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
  19 + v-hasPermi="['infra:category:create']">新增</el-button>
  20 + </el-col>
  21 + <el-col :span="1.5">
  22 + <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  23 + v-hasPermi="['infra:category:export']">导出</el-button>
  24 + </el-col>
  25 + <el-col :span="1.5">
  26 + <el-button type="danger" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">
  27 + 展开/折叠
  28 + </el-button>
  29 + </el-col>
  30 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  31 + </el-row>
  32 +
  33 + <el-table
  34 + v-loading="loading"
  35 + :data="list"
  36 + :stripe="true"
  37 + :show-overflow-tooltip="true"
  38 + v-if="refreshTable"
  39 + row-key="id"
  40 + :default-expand-all="isExpandAll"
  41 + :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
  42 + >
  43 + <el-table-column label="编号" align="center" prop="id">
  44 + <template v-slot="scope">
  45 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.id" />
  46 + </template>
  47 + </el-table-column>
  48 + <el-table-column label="名字" align="center" prop="name">
  49 + <template v-slot="scope">
  50 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.name" />
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column label="父编号" align="center" prop="parentId">
  54 + <template v-slot="scope">
  55 + <dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.parentId" />
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  59 + <template v-slot="scope">
  60 + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)"
  61 + v-hasPermi="['infra:category:update']">修改</el-button>
  62 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  63 + v-hasPermi="['infra:category:delete']">删除</el-button>
  64 + </template>
  65 + </el-table-column>
  66 + </el-table>
  67 + <!-- 对话框(添加 / 修改) -->
  68 + <CategoryForm ref="formRef" @success="getList" />
  69 + </div>
  70 +</template>
  71 +
  72 +<script>
  73 +import * as CategoryApi from '@/api/infra/demo';
  74 +import CategoryForm from './CategoryForm.vue';
  75 +export default {
  76 + name: "Category",
  77 + components: {
  78 + CategoryForm,
  79 + },
  80 + data() {
  81 + return {
  82 + // 遮罩层
  83 + loading: true,
  84 + // 导出遮罩层
  85 + exportLoading: false,
  86 + // 显示搜索条件
  87 + showSearch: true,
  88 + // 分类列表
  89 + list: [],
  90 + // 是否展开,默认全部展开
  91 + isExpandAll: true,
  92 + // 重新渲染表格状态
  93 + refreshTable: true,
  94 + // 选中行
  95 + currentRow: {},
  96 + // 查询参数
  97 + queryParams: {
  98 + name: null,
  99 + },
  100 + };
  101 + },
  102 + created() {
  103 + this.getList();
  104 + },
  105 + methods: {
  106 + /** 查询列表 */
  107 + getList() {
  108 + try {
  109 + this.loading = true;
  110 + CategoryApi.getCategoryList(this.queryParams).then(response => {
  111 + this.list = this.handleTree(response.data, 'id', 'parentId');
  112 + })
  113 + } finally {
  114 + this.loading = false;
  115 + }
  116 + },
  117 + /** 搜索按钮操作 */
  118 + handleQuery() {
  119 + this.queryParams.pageNo = 1;
  120 + this.getList();
  121 + },
  122 + /** 重置按钮操作 */
  123 + resetQuery() {
  124 + this.resetForm("queryForm");
  125 + this.handleQuery();
  126 + },
  127 + /** 添加/修改操作 */
  128 + openForm(id) {
  129 + this.$refs["formRef"].open(id);
  130 + },
  131 + /** 删除按钮操作 */
  132 + handleDelete(row) {
  133 + const that = this;
  134 + try {
  135 + const id = row.id;
  136 + this.$modal.confirm('是否确认删除分类编号为"' + id + '"的数据项?').then(()=>{
  137 + return CategoryApi.deleteCategory(id);
  138 + }).then(() => {
  139 + that.getList();
  140 + that.$modal.msgSuccess("删除成功");
  141 + }).catch(() => {});
  142 + } catch {}
  143 + },
  144 + /** 导出按钮操作 */
  145 + handleExport() {
  146 + const that = this;
  147 + try {
  148 + this.$modal.confirm('是否确认导出所有分类数据项?').then(() => {
  149 + that.exportLoading = true;
  150 + return CategoryApi.exportCategoryExcel(params);
  151 + }).then(response => {
  152 + that.$download.excel(response, '分类.xls');
  153 + });
  154 + } catch {
  155 + } finally {
  156 + that.exportLoading = false;
  157 + }
  158 + },
  159 + /** 展开/折叠操作 */
  160 + toggleExpandAll() {
  161 + this.refreshTable = false
  162 + this.isExpandAll = !this.isExpandAll
  163 + this.$nextTick(function () {
  164 + this.refreshTable = true
  165 + })
  166 + }
  167 + }
  168 +};
  169 +</script>
yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree/xml/InfraCategoryMapper 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-server/src/main/resources/application-local.yaml
@@ -54,13 +54,13 @@ spring: @@ -54,13 +54,13 @@ spring:
54 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例 54 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例
55 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 55 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
56 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例 56 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
57 -# url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 57 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
58 username: root 58 username: root
59 password: 123456 59 password: 123456
60 # username: sa 60 # username: sa
61 # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W 61 # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
62 -# username: SYSDBA # DM 连接的示例  
63 -# password: SYSDBA # DM 连接的示例 62 + # username: SYSDBA # DM 连接的示例
  63 + # password: SYSDBA # DM 连接的示例
64 slave: # 模拟从库,可根据自己需要修改 64 slave: # 模拟从库,可根据自己需要修改
65 name: ruoyi-vue-pro 65 name: ruoyi-vue-pro
66 lazy: true # 开启懒加载,保证启动速度 66 lazy: true # 开启懒加载,保证启动速度
@@ -123,8 +123,8 @@ spring: @@ -123,8 +123,8 @@ spring:
123 rabbitmq: 123 rabbitmq:
124 host: 127.0.0.1 # RabbitMQ 服务的地址 124 host: 127.0.0.1 # RabbitMQ 服务的地址
125 port: 5672 # RabbitMQ 服务的端口 125 port: 5672 # RabbitMQ 服务的端口
126 - username: guest # RabbitMQ 服务的账号  
127 - password: guest # RabbitMQ 服务的密码 126 + username: rabbit # RabbitMQ 服务的账号
  127 + password: rabbit # RabbitMQ 服务的密码
128 # Kafka 配置项,对应 KafkaProperties 配置类 128 # Kafka 配置项,对应 KafkaProperties 配置类
129 kafka: 129 kafka:
130 bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 130 bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔