Commit 2ef66566d4befc054a5657c475866e78702f0bea
1 parent
020fa9d5
feat(codegen): 完善 UniApp Wot 代码生成
- 支持普通、树表及多种主子表页面生成 - 完善主子表 API、表单、详情和字段控件 - 修复一对一空数据提交及树表搜索问题 - 统一日期时间戳和主键类型生成 - 修复 Wot 组件类型及生成代码格式问题 - 优化生成快照路径并增加文件冲突检测 - 补充 UniApp 代码生成单测和快照
Showing
105 changed files
with
10321 additions
and
190 deletions
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
| ... | ... | @@ -152,6 +152,16 @@ public class CodegenEngine { |
| 152 | 152 | vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/form/index.vue")) |
| 153 | 153 | .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/detail/index.vue"), |
| 154 | 154 | vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/detail/index.vue")) |
| 155 | + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/index_tree.vue"), | |
| 156 | + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/index.vue")) | |
| 157 | + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("components/breadcrumb_tree.vue"), | |
| 158 | + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/components/breadcrumb.vue")) | |
| 159 | + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/detail_master.vue"), | |
| 160 | + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/detail/index.vue")) | |
| 161 | + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/detail_erp.vue"), | |
| 162 | + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/detail/index.vue")) | |
| 163 | + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/form_sub_erp.vue"), | |
| 164 | + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/${subSimpleClassName_strikeCase}/form/index.vue")) | |
| 155 | 165 | // VUE3_VBEN2_ANTD_SCHEMA |
| 156 | 166 | .put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath("views/data.ts"), |
| 157 | 167 | vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts")) |
| ... | ... | @@ -380,12 +390,17 @@ public class CodegenEngine { |
| 380 | 390 | |
| 381 | 391 | // 2. 执行生成 |
| 382 | 392 | Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序 |
| 393 | + Map<String, String> generatedSources = Maps.newHashMapWithExpectedSize(templates.size()); | |
| 383 | 394 | templates.forEach((vmPath, filePath) -> { |
| 384 | - // 2.1 特殊:主子表专属逻辑 | |
| 395 | + // 2.1 特殊:主子表、Uniapp 树表使用独立页面模版 | |
| 396 | + if (isVue3AdminUniappTemplate(vmPath) && !matchVue3AdminUniappTemplate(table, vmPath)) { | |
| 397 | + return; | |
| 398 | + } | |
| 399 | + // 2.2 特殊:主子表专属逻辑 | |
| 385 | 400 | if (isSubTemplate(vmPath)) { |
| 386 | - generateSubCode(table, subTables, result, vmPath, filePath, bindingMap); | |
| 401 | + generateSubCode(table, subTables, result, generatedSources, vmPath, filePath, bindingMap); | |
| 387 | 402 | return; |
| 388 | - // 2.2 特殊:树表专属逻辑 | |
| 403 | + // 2.3 特殊:树表专属逻辑 | |
| 389 | 404 | } else if (isPageReqVOTemplate(vmPath)) { |
| 390 | 405 | // 减少多余的类生成,例如说 PageVO.java 类 |
| 391 | 406 | if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) { |
| ... | ... | @@ -402,15 +417,21 @@ public class CodegenEngine { |
| 402 | 417 | return; |
| 403 | 418 | } |
| 404 | 419 | } |
| 405 | - // 2.3 默认生成 | |
| 406 | - generateCode(result, vmPath, filePath, bindingMap); | |
| 420 | + // 2.4 默认生成 | |
| 421 | + generateCode(result, generatedSources, vmPath, filePath, bindingMap); | |
| 407 | 422 | }); |
| 408 | 423 | return result; |
| 409 | 424 | } |
| 410 | 425 | |
| 411 | - private void generateCode(Map<String, String> result, String vmPath, | |
| 426 | + private void generateCode(Map<String, String> result, Map<String, String> generatedSources, String vmPath, | |
| 412 | 427 | String filePath, Map<String, Object> bindingMap) { |
| 413 | 428 | filePath = formatFilePath(filePath, bindingMap); |
| 429 | + String source = vmPath + (bindingMap.containsKey("subIndex") ? "[subIndex=" + bindingMap.get("subIndex") + "]" : ""); | |
| 430 | + String previousSource = generatedSources.putIfAbsent(filePath, source); | |
| 431 | + if (previousSource != null) { | |
| 432 | + throw new IllegalStateException(String.format("生成文件路径重复:%s,来源模板:%s、%s", | |
| 433 | + filePath, previousSource, source)); | |
| 434 | + } | |
| 414 | 435 | String content = templateEngine.getTemplate(vmPath).render(bindingMap); |
| 415 | 436 | // 格式化代码 |
| 416 | 437 | content = prettyCode(content, vmPath); |
| ... | ... | @@ -418,7 +439,7 @@ public class CodegenEngine { |
| 418 | 439 | } |
| 419 | 440 | |
| 420 | 441 | private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables, |
| 421 | - Map<String, String> result, String vmPath, | |
| 442 | + Map<String, String> result, Map<String, String> generatedSources, String vmPath, | |
| 422 | 443 | String filePath, Map<String, Object> bindingMap) { |
| 423 | 444 | // 没有子表,所以不生成 |
| 424 | 445 | if (CollUtil.isEmpty(subTables)) { |
| ... | ... | @@ -441,7 +462,7 @@ public class CodegenEngine { |
| 441 | 462 | // 逐个生成 |
| 442 | 463 | for (int i = 0; i < subTables.size(); i++) { |
| 443 | 464 | bindingMap.put("subIndex", i); |
| 444 | - generateCode(result, vmPath, filePath, bindingMap); | |
| 465 | + generateCode(result, generatedSources, vmPath, filePath, bindingMap); | |
| 445 | 466 | } |
| 446 | 467 | bindingMap.remove("subIndex"); |
| 447 | 468 | } |
| ... | ... | @@ -482,6 +503,13 @@ public class CodegenEngine { |
| 482 | 503 | if (StrUtil.count(content, "DICT_TYPE.") == 0) { |
| 483 | 504 | content = StrUtils.removeLineContains(content, "DICT_TYPE"); |
| 484 | 505 | } |
| 506 | + // Vue3 Admin UniApp:清理空白行,并补充文件末尾换行,满足 ESLint 格式要求 | |
| 507 | + if (vmPath.contains("vue3_admin_uniapp")) { | |
| 508 | + content = content.replaceAll("(?m)^[\\t ]+$", ""); | |
| 509 | + if (!content.endsWith("\n")) { | |
| 510 | + content += "\n"; | |
| 511 | + } | |
| 512 | + } | |
| 485 | 513 | return content; |
| 486 | 514 | } |
| 487 | 515 | |
| ... | ... | @@ -734,6 +762,33 @@ public class CodegenEngine { |
| 734 | 762 | return path.contains("_sub"); |
| 735 | 763 | } |
| 736 | 764 | |
| 765 | + private static boolean isVue3AdminUniappTemplate(String path) { | |
| 766 | + return path.contains("codegen/vue3_admin_uniapp/"); | |
| 767 | + } | |
| 768 | + | |
| 769 | + /** | |
| 770 | + * 匹配 Uniapp 不同表类型的专用页面模版 | |
| 771 | + */ | |
| 772 | + private static boolean matchVue3AdminUniappTemplate(CodegenTableDO table, String path) { | |
| 773 | + boolean tree = CodegenTemplateTypeEnum.isTree(table.getTemplateType()); | |
| 774 | + boolean masterSubmit = ObjectUtil.equals(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_NORMAL.getType()) | |
| 775 | + || ObjectUtil.equals(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_INNER.getType()); | |
| 776 | + boolean masterErp = ObjectUtil.equals(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_ERP.getType()); | |
| 777 | + if (path.contains("_tree")) { | |
| 778 | + return tree; | |
| 779 | + } | |
| 780 | + if (path.contains("detail_master")) { | |
| 781 | + return masterSubmit; | |
| 782 | + } | |
| 783 | + if (path.contains("_erp")) { | |
| 784 | + return masterErp; | |
| 785 | + } | |
| 786 | + if (tree && path.endsWith("views/index.vue.vm")) { | |
| 787 | + return false; | |
| 788 | + } | |
| 789 | + return !(masterSubmit || masterErp) || !path.endsWith("views/detail/index.vue.vm"); | |
| 790 | + } | |
| 791 | + | |
| 737 | 792 | private static boolean isPageReqVOTemplate(String path) { |
| 738 | 793 | return path.contains("pageReqVO"); |
| 739 | 794 | } | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/api/api.ts.vm
| 1 | +#if ($table.templateType != 2) | |
| 1 | 2 | import type { PageParam, PageResult } from '@/http/types' |
| 3 | +#end | |
| 2 | 4 | import { http } from '@/http/http' |
| 3 | 5 | |
| 4 | 6 | #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) |
| ... | ... | @@ -7,30 +9,72 @@ import { http } from '@/http/http' |
| 7 | 9 | #else |
| 8 | 10 | #set ($primaryTsType = "string") |
| 9 | 11 | #end |
| 12 | +#foreach ($subTable in $subTables) | |
| 13 | +#set ($index = $foreach.count - 1) | |
| 14 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 15 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 16 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 17 | +/** ${subTable.classComment}信息 */ | |
| 18 | +export interface ${subSimpleClassName} { | |
| 19 | +#foreach ($column in $subColumns) | |
| 20 | +#if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult) | |
| 21 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 22 | +#set ($optional = $column.nullable || $column.primaryKey || ($table.templateType != 11 && $column.id == $subJoinColumn.id)) | |
| 23 | +#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") | |
| 24 | + ${column.javaField}#if($optional)?#end: number | |
| 25 | +#elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") | |
| 26 | + ${column.javaField}#if($optional)?#end: number | |
| 27 | +#elseif(${javaType} == "boolean") | |
| 28 | + ${column.javaField}#if($optional)?#end: boolean | |
| 29 | +#else | |
| 30 | + ${column.javaField}#if($optional)?#end: string | |
| 31 | +#end | |
| 32 | +#end | |
| 33 | +#end | |
| 34 | +} | |
| 35 | + | |
| 36 | +#end | |
| 10 | 37 | /** ${table.classComment}信息 */ |
| 11 | 38 | export interface ${simpleClassName} { |
| 12 | 39 | #foreach ($column in $columns) |
| 13 | - #if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult) | |
| 14 | - #set ($javaType = $column.javaType.toLowerCase()) | |
| 15 | - #set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 16 | - #set ($optional = $column.nullable || $column.primaryKey || $javaFieldLower == "createtime" || $javaFieldLower == "updatetime") | |
| 17 | - #if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") | |
| 40 | +#if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult) | |
| 41 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 42 | +#set ($optional = $column.nullable || $column.primaryKey) | |
| 43 | +#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") | |
| 44 | + ${column.javaField}#if($optional)?#end: number | |
| 45 | +#elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") | |
| 18 | 46 | ${column.javaField}#if($optional)?#end: number |
| 19 | - #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") | |
| 20 | - ${column.javaField}#if($optional)?#end: Date | |
| 21 | - #elseif(${javaType} == "boolean") | |
| 47 | +#elseif(${javaType} == "boolean") | |
| 22 | 48 | ${column.javaField}#if($optional)?#end: boolean |
| 23 | - #else | |
| 49 | +#else | |
| 24 | 50 | ${column.javaField}#if($optional)?#end: string |
| 25 | - #end | |
| 26 | - #end | |
| 51 | +#end | |
| 52 | +#end | |
| 53 | +#end | |
| 54 | +#if ($table.templateType == 2) | |
| 55 | + children?: ${simpleClassName}[] | |
| 56 | +#end | |
| 57 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 58 | +#foreach ($subTable in $subTables) | |
| 59 | +#set ($index = $foreach.count - 1) | |
| 60 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 61 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 62 | + ${subClassNameVar}#if($subTable.subJoinMany)s#end?: ${subSimpleClassName}#if($subTable.subJoinMany)[]#else | null#end | |
| 63 | +#end | |
| 27 | 64 | #end |
| 28 | 65 | } |
| 29 | 66 | |
| 67 | +#if ($table.templateType == 2) | |
| 68 | +/** 获取${table.classComment}列表 */ | |
| 69 | +export function get${simpleClassName}List(params?: Record<string, any>) { | |
| 70 | + return http.get<${simpleClassName}[]>('/${table.moduleName}/${simpleClassName_strikeCase}/list', params) | |
| 71 | +} | |
| 72 | +#else | |
| 30 | 73 | /** 获取${table.classComment}分页列表 */ |
| 31 | 74 | export function get${simpleClassName}Page(params: PageParam) { |
| 32 | 75 | return http.get<PageResult<${simpleClassName}>>('/${table.moduleName}/${simpleClassName_strikeCase}/page', params) |
| 33 | 76 | } |
| 77 | +#end | |
| 34 | 78 | |
| 35 | 79 | /** 获取${table.classComment}详情 */ |
| 36 | 80 | export function get${simpleClassName}(id: ${primaryTsType}) { |
| ... | ... | @@ -39,7 +83,7 @@ export function get${simpleClassName}(id: ${primaryTsType}) { |
| 39 | 83 | |
| 40 | 84 | /** 创建${table.classComment} */ |
| 41 | 85 | export function create${simpleClassName}(data: ${simpleClassName}) { |
| 42 | - return http.post<number>('/${table.moduleName}/${simpleClassName_strikeCase}/create', data) | |
| 86 | + return http.post<${primaryTsType}>('/${table.moduleName}/${simpleClassName_strikeCase}/create', data) | |
| 43 | 87 | } |
| 44 | 88 | |
| 45 | 89 | /** 更新${table.classComment} */ |
| ... | ... | @@ -51,3 +95,65 @@ export function update${simpleClassName}(data: ${simpleClassName}) { |
| 51 | 95 | export function delete${simpleClassName}(id: ${primaryTsType}) { |
| 52 | 96 | return http.delete<boolean>(`/${table.moduleName}/${simpleClassName_strikeCase}/delete?id=${id}`) |
| 53 | 97 | } |
| 98 | +#foreach ($subTable in $subTables) | |
| 99 | +#set ($index = $foreach.count - 1) | |
| 100 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 101 | +#set ($subPrimaryColumn = $subPrimaryColumns.get($index)) | |
| 102 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 103 | +#set ($subPrimaryJavaType = $subPrimaryColumn.javaType.toLowerCase()) | |
| 104 | +#if(${subPrimaryJavaType} == "long" || ${subPrimaryJavaType} == "integer" || ${subPrimaryJavaType} == "short" || ${subPrimaryJavaType} == "double" || ${subPrimaryJavaType} == "bigdecimal" || ${subPrimaryJavaType} == "byte") | |
| 105 | +#set ($subPrimaryTsType = "number") | |
| 106 | +#else | |
| 107 | +#set ($subPrimaryTsType = "string") | |
| 108 | +#end | |
| 109 | +#set ($subJoinJavaType = $subJoinColumn.javaType.toLowerCase()) | |
| 110 | +#if(${subJoinJavaType} == "long" || ${subJoinJavaType} == "integer" || ${subJoinJavaType} == "short" || ${subJoinJavaType} == "double" || ${subJoinJavaType} == "bigdecimal" || ${subJoinJavaType} == "byte") | |
| 111 | +#set ($subJoinTsType = "number") | |
| 112 | +#else | |
| 113 | +#set ($subJoinTsType = "string") | |
| 114 | +#end | |
| 115 | +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + $subJoinColumn.javaField.substring(1)) | |
| 116 | +#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index)) | |
| 117 | +#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) | |
| 118 | + | |
| 119 | +// ==================== 子表:${subTable.classComment} ==================== | |
| 120 | +#if ($table.templateType == 11) | |
| 121 | + | |
| 122 | +/** 获取${subTable.classComment}分页列表 */ | |
| 123 | +export function get${subSimpleClassName}Page(params: PageParam & { ${subJoinColumn.javaField}: ${subJoinTsType} }) { | |
| 124 | + return http.get<PageResult<${subSimpleClassName}>>('/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/page', params) | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** 获取${subTable.classComment}详情 */ | |
| 128 | +export function get${subSimpleClassName}(id: ${subPrimaryTsType}) { | |
| 129 | + return http.get<${subSimpleClassName}>(`/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/get?id=${id}`) | |
| 130 | +} | |
| 131 | + | |
| 132 | +/** 创建${subTable.classComment} */ | |
| 133 | +export function create${subSimpleClassName}(data: ${subSimpleClassName}) { | |
| 134 | + return http.post<${subPrimaryTsType}>('/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/create', data) | |
| 135 | +} | |
| 136 | + | |
| 137 | +/** 更新${subTable.classComment} */ | |
| 138 | +export function update${subSimpleClassName}(data: ${subSimpleClassName}) { | |
| 139 | + return http.put<boolean>('/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/update', data) | |
| 140 | +} | |
| 141 | + | |
| 142 | +/** 删除${subTable.classComment} */ | |
| 143 | +export function delete${subSimpleClassName}(id: ${subPrimaryTsType}) { | |
| 144 | + return http.delete<boolean>(`/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/delete?id=${id}`) | |
| 145 | +} | |
| 146 | +#elseif ($subTable.subJoinMany) | |
| 147 | + | |
| 148 | +/** 获取${subTable.classComment}列表 */ | |
| 149 | +export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: ${subJoinTsType}) { | |
| 150 | + return http.get<${subSimpleClassName}[]>(`/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=#[[${]]#${subJoinColumn.javaField}#[[}]]#`) | |
| 151 | +} | |
| 152 | +#else | |
| 153 | + | |
| 154 | +/** 获取${subTable.classComment} */ | |
| 155 | +export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: ${subJoinTsType}) { | |
| 156 | + return http.get<${subSimpleClassName} | null>(`/${table.moduleName}/${simpleClassName_strikeCase}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=#[[${]]#${subJoinColumn.javaField}#[[}]]#`) | |
| 157 | +} | |
| 158 | +#end | |
| 159 | +#end | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/breadcrumb_tree.vue.vm
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="bg-white px-24rpx py-16rpx"> | |
| 3 | + <scroll-view scroll-x class="whitespace-nowrap"> | |
| 4 | + <view class="inline-flex items-center"> | |
| 5 | + <view | |
| 6 | + class="flex items-center text-28rpx" | |
| 7 | + :class="items.length > 0 ? 'text-[#1890ff]' : 'text-[#333]'" | |
| 8 | + @click="handleClick(-1)" | |
| 9 | + > | |
| 10 | + <text>全部${table.classComment}</text> | |
| 11 | + </view> | |
| 12 | + <template v-for="(item, index) in items" :key="item.id"> | |
| 13 | + <wd-icon name="arrow-right" size="12px" color="#999" custom-class="mx-8rpx" /> | |
| 14 | + <view | |
| 15 | + class="flex items-center text-28rpx" | |
| 16 | + :class="index < items.length - 1 ? 'text-[#1890ff]' : 'text-[#333]'" | |
| 17 | + @click="handleClick(index)" | |
| 18 | + > | |
| 19 | + <text>{{ item.name }}</text> | |
| 20 | + </view> | |
| 21 | + </template> | |
| 22 | + </view> | |
| 23 | + </scroll-view> | |
| 24 | + </view> | |
| 25 | +</template> | |
| 26 | + | |
| 27 | +<script lang="ts" setup> | |
| 28 | +import { ref, watch } from 'vue' | |
| 29 | + | |
| 30 | +interface BreadcrumbItem { | |
| 31 | + id: number | |
| 32 | + name: string | |
| 33 | +} | |
| 34 | + | |
| 35 | +const props = defineProps<{ | |
| 36 | + modelValue: number | |
| 37 | +}>() | |
| 38 | + | |
| 39 | +const emit = defineEmits<{ | |
| 40 | + 'update:modelValue': [value: number] | |
| 41 | +}>() | |
| 42 | + | |
| 43 | +const items = ref<BreadcrumbItem[]>([]) // 面包屑数据 | |
| 44 | + | |
| 45 | +/** 监听外部值变化 */ | |
| 46 | +watch(() => props.modelValue, (value) => { | |
| 47 | + if (value === 0) { | |
| 48 | + items.value = [] | |
| 49 | + } | |
| 50 | +}) | |
| 51 | + | |
| 52 | +/** 点击面包屑 */ | |
| 53 | +function handleClick(index: number) { | |
| 54 | + if (index === -1) { | |
| 55 | + reset() | |
| 56 | + return | |
| 57 | + } | |
| 58 | + if (index < items.value.length - 1) { | |
| 59 | + items.value = items.value.slice(0, index + 1) | |
| 60 | + emit('update:modelValue', items.value[index].id) | |
| 61 | + } | |
| 62 | +} | |
| 63 | + | |
| 64 | +/** 进入子层级 */ | |
| 65 | +function enter(item: BreadcrumbItem) { | |
| 66 | + items.value.push(item) | |
| 67 | + emit('update:modelValue', item.id) | |
| 68 | +} | |
| 69 | + | |
| 70 | +/** 返回上一层级 */ | |
| 71 | +function back(): boolean { | |
| 72 | + if (items.value.length === 0) { | |
| 73 | + return false | |
| 74 | + } | |
| 75 | + items.value.pop() | |
| 76 | + emit('update:modelValue', items.value.at(-1)?.id ?? 0) | |
| 77 | + return true | |
| 78 | +} | |
| 79 | + | |
| 80 | +/** 重置面包屑 */ | |
| 81 | +function reset() { | |
| 82 | + items.value = [] | |
| 83 | + emit('update:modelValue', 0) | |
| 84 | +} | |
| 85 | + | |
| 86 | +defineExpose({ back, enter, reset }) | |
| 87 | +</script> | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm
| ... | ... | @@ -39,7 +39,7 @@ |
| 39 | 39 | </wd-button> |
| 40 | 40 | <wd-button |
| 41 | 41 | v-if="hasAccessByCodes(['${permissionPrefix}:delete'])" |
| 42 | - class="flex-1" type="error" :loading="deleting" @click="handleDelete" | |
| 42 | + class="flex-1" type="danger" :loading="deleting" @click="handleDelete" | |
| 43 | 43 | > |
| 44 | 44 | 删除 |
| 45 | 45 | </wd-button> |
| ... | ... | @@ -49,6 +49,12 @@ |
| 49 | 49 | </template> |
| 50 | 50 | |
| 51 | 51 | <script lang="ts" setup> |
| 52 | +#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) | |
| 53 | +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") | |
| 54 | +#set ($primaryTsType = "number") | |
| 55 | +#else | |
| 56 | +#set ($primaryTsType = "string") | |
| 57 | +#end | |
| 52 | 58 | #set ($hasDict = 0) |
| 53 | 59 | #foreach($column in $columns) |
| 54 | 60 | #if ($hasDict == 0 && $column.dictType && "" != $column.dictType) |
| ... | ... | @@ -64,6 +70,7 @@ |
| 64 | 70 | import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' |
| 65 | 71 | import { onUnload } from '@dcloudio/uni-app' |
| 66 | 72 | import { onMounted, ref } from 'vue' |
| 73 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 67 | 74 | import { useToast } from '@wot-ui/ui/components/wd-toast' |
| 68 | 75 | import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' |
| 69 | 76 | import { useAccess } from '@/hooks/useAccess' |
| ... | ... | @@ -76,7 +83,7 @@ import { formatDateTime } from '@/utils/date' |
| 76 | 83 | #end |
| 77 | 84 | |
| 78 | 85 | const props = defineProps<{ |
| 79 | - id?: number | any | |
| 86 | + id?: ${primaryTsType} | |
| 80 | 87 | }>() |
| 81 | 88 | |
| 82 | 89 | definePage({ |
| ... | ... | @@ -87,6 +94,7 @@ definePage({ |
| 87 | 94 | }) |
| 88 | 95 | |
| 89 | 96 | const { hasAccessByCodes } = useAccess() |
| 97 | +const dialog = useDialog() | |
| 90 | 98 | const toast = useToast() |
| 91 | 99 | const formData = ref<${simpleClassName}>() // 详情数据 |
| 92 | 100 | const deleting = ref(false) // 删除状态 |
| ... | ... | @@ -117,28 +125,27 @@ function handleEdit() { |
| 117 | 125 | } |
| 118 | 126 | |
| 119 | 127 | /** 删除${table.classComment} */ |
| 120 | -function handleDelete() { | |
| 128 | +async function handleDelete() { | |
| 121 | 129 | if (!props.id) { |
| 122 | 130 | return |
| 123 | 131 | } |
| 124 | - uni.showModal({ | |
| 125 | - title: '提示', | |
| 126 | - content: '确定要删除该${table.classComment}吗?', | |
| 127 | - success: async (res) => { | |
| 128 | - if (!res.confirm) { | |
| 129 | - return | |
| 130 | - } | |
| 131 | - deleting.value = true | |
| 132 | - try { | |
| 133 | - await delete${simpleClassName}(props.id) | |
| 134 | - toast.success('删除成功') | |
| 135 | - uni.$emit('${table.moduleName}:${table.businessName}:reload') | |
| 136 | - delay(handleBack) | |
| 137 | - } finally { | |
| 138 | - deleting.value = false | |
| 139 | - } | |
| 140 | - }, | |
| 141 | - }) | |
| 132 | + try { | |
| 133 | + await dialog.confirm({ | |
| 134 | + title: '提示', | |
| 135 | + msg: '确定要删除该${table.classComment}吗?', | |
| 136 | + }) | |
| 137 | + } catch { | |
| 138 | + return | |
| 139 | + } | |
| 140 | + deleting.value = true | |
| 141 | + try { | |
| 142 | + await delete${simpleClassName}(props.id) | |
| 143 | + toast.success('删除成功') | |
| 144 | + uni.$emit('${table.moduleName}:${table.businessName}:reload') | |
| 145 | + delay(handleBack) | |
| 146 | + } finally { | |
| 147 | + deleting.value = false | |
| 148 | + } | |
| 142 | 149 | } |
| 143 | 150 | |
| 144 | 151 | /** 初始化 */ | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail_erp.vue.vm
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar title="${table.classComment}详情" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" /> | |
| 5 | + | |
| 6 | + <view class="pb-160rpx"> | |
| 7 | + <!-- 主表详情 --> | |
| 8 | + <wd-cell-group border title="${table.classComment}信息"> | |
| 9 | +#foreach($column in $columns) | |
| 10 | +#if ($column.primaryKey || $column.listOperationResult || $column.createOperation || $column.updateOperation) | |
| 11 | +#if ($column.dictType && "" != $column.dictType) | |
| 12 | + <wd-cell title="${column.columnComment}"> | |
| 13 | + <dict-tag :type="DICT_TYPE.${column.dictType.toUpperCase()}" :value="formData?.${column.javaField}" /> | |
| 14 | + </wd-cell> | |
| 15 | +#elseif ($column.javaType == "LocalDateTime") | |
| 16 | + <wd-cell title="${column.columnComment}" :value="formatDateTime(formData?.${column.javaField}) || '-'" /> | |
| 17 | +#else | |
| 18 | + <wd-cell title="${column.columnComment}" :value="formData?.${column.javaField} ?? '-'" /> | |
| 19 | +#end | |
| 20 | +#end | |
| 21 | +#end | |
| 22 | + </wd-cell-group> | |
| 23 | + | |
| 24 | +#foreach ($subTable in $subTables) | |
| 25 | +#set ($index = $foreach.count - 1) | |
| 26 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 27 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 28 | +#set ($subPrimaryColumn = $subPrimaryColumns.get($index)) | |
| 29 | +#set ($pageSize = 10) | |
| 30 | +#if (!$subTable.subJoinMany) | |
| 31 | +#set ($pageSize = 1) | |
| 32 | +#end | |
| 33 | + <!-- 子表:${subTable.classComment} --> | |
| 34 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 35 | + <text class="text-28rpx text-[#333] font-semibold">${subTable.classComment}({{ ${subClassNameVar}Total }})</text> | |
| 36 | + <wd-button | |
| 37 | +#if (!$subTable.subJoinMany) | |
| 38 | + v-if="${subClassNameVar}Total === 0 && hasAccessByCodes(['${permissionPrefix}:create'])" | |
| 39 | +#else | |
| 40 | + v-if="hasAccessByCodes(['${permissionPrefix}:create'])" | |
| 41 | +#end | |
| 42 | + size="small" type="primary" variant="plain" @click="open${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}()" | |
| 43 | + > | |
| 44 | + 添加 | |
| 45 | + </wd-button> | |
| 46 | + </view> | |
| 47 | + <view class="px-24rpx"> | |
| 48 | + <z-paging | |
| 49 | + ref="${subClassNameVar}PagingRef" | |
| 50 | + v-model="${subClassNameVar}List" | |
| 51 | + :fixed="false" | |
| 52 | + :auto="false" | |
| 53 | + height="560rpx" | |
| 54 | + :default-page-size="${pageSize}" | |
| 55 | + :refresher-enabled="false" | |
| 56 | + :inside-more="true" | |
| 57 | + :to-bottom-loading-more-enabled="false" | |
| 58 | + loading-more-default-text="点击加载更多" | |
| 59 | + empty-view-text="暂无${subTable.classComment}" | |
| 60 | + @query="query${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}List" | |
| 61 | + > | |
| 62 | + <view | |
| 63 | + v-for="item in ${subClassNameVar}List" | |
| 64 | + :key="item.${subPrimaryColumn.javaField}" | |
| 65 | + class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 66 | + > | |
| 67 | +#foreach ($column in $subColumns) | |
| 68 | +#if ($column.listOperationResult && !$column.primaryKey) | |
| 69 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 70 | + ${column.columnComment}:{{ item.${column.javaField} ?? '-' }} | |
| 71 | + </view> | |
| 72 | +#end | |
| 73 | +#end | |
| 74 | + <view class="mt-16rpx flex justify-end gap-16rpx"> | |
| 75 | + <wd-button size="small" variant="plain" @click="open${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}(item)"> | |
| 76 | + 编辑 | |
| 77 | + </wd-button> | |
| 78 | + <wd-button size="small" type="danger" variant="plain" @click="remove${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}(item)"> | |
| 79 | + 删除 | |
| 80 | + </wd-button> | |
| 81 | + </view> | |
| 82 | + </view> | |
| 83 | + </z-paging> | |
| 84 | + </view> | |
| 85 | +#end | |
| 86 | + </view> | |
| 87 | + | |
| 88 | + <!-- 底部操作按钮 --> | |
| 89 | + <view class="yd-detail-footer"> | |
| 90 | + <view class="yd-detail-footer-actions"> | |
| 91 | + <wd-button v-if="hasAccessByCodes(['${permissionPrefix}:update'])" class="flex-1" type="warning" @click="handleEdit"> | |
| 92 | + 编辑 | |
| 93 | + </wd-button> | |
| 94 | + <wd-button v-if="hasAccessByCodes(['${permissionPrefix}:delete'])" class="flex-1" type="danger" :loading="deleting" @click="handleDelete"> | |
| 95 | + 删除 | |
| 96 | + </wd-button> | |
| 97 | + </view> | |
| 98 | + </view> | |
| 99 | + </view> | |
| 100 | +</template> | |
| 101 | + | |
| 102 | +<script lang="ts" setup> | |
| 103 | +#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) | |
| 104 | +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") | |
| 105 | +#set ($primaryTsType = "number") | |
| 106 | +#else | |
| 107 | +#set ($primaryTsType = "string") | |
| 108 | +#end | |
| 109 | +#set ($hasDict = 0) | |
| 110 | +#set ($hasDateTime = 0) | |
| 111 | +#foreach($column in $columns) | |
| 112 | +#if ($column.dictType && "" != $column.dictType) | |
| 113 | +#set ($hasDict = 1) | |
| 114 | +#end | |
| 115 | +#if ($column.javaType == "LocalDateTime") | |
| 116 | +#set ($hasDateTime = 1) | |
| 117 | +#end | |
| 118 | +#end | |
| 119 | +import type { | |
| 120 | + ${simpleClassName}, | |
| 121 | +#foreach ($subSimpleClassName in $subSimpleClassNames) | |
| 122 | + ${subSimpleClassName}, | |
| 123 | +#end | |
| 124 | +} from '@/api/${table.moduleName}/${table.businessName}' | |
| 125 | +import { onShow } from '@dcloudio/uni-app' | |
| 126 | +import { nextTick, ref } from 'vue' | |
| 127 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 128 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 129 | +import { | |
| 130 | + delete${simpleClassName}, | |
| 131 | +#foreach ($subSimpleClassName in $subSimpleClassNames) | |
| 132 | + delete${subSimpleClassName}, | |
| 133 | +#end | |
| 134 | + get${simpleClassName}, | |
| 135 | +#foreach ($subSimpleClassName in $subSimpleClassNames) | |
| 136 | + get${subSimpleClassName}Page, | |
| 137 | +#end | |
| 138 | +} from '@/api/${table.moduleName}/${table.businessName}' | |
| 139 | +import { useAccess } from '@/hooks/useAccess' | |
| 140 | +import { delay, navigateBackPlus } from '@/utils' | |
| 141 | +#if ($hasDict == 1) | |
| 142 | +import { DICT_TYPE } from '@/utils/constants' | |
| 143 | +#end | |
| 144 | +#if ($hasDateTime == 1) | |
| 145 | +import { formatDateTime } from '@/utils/date' | |
| 146 | +#end | |
| 147 | + | |
| 148 | +const props = defineProps<{ id?: ${primaryTsType} }>() | |
| 149 | + | |
| 150 | +definePage({ | |
| 151 | + style: { navigationBarTitleText: '', navigationStyle: 'custom' }, | |
| 152 | +}) | |
| 153 | + | |
| 154 | +const { hasAccessByCodes } = useAccess() | |
| 155 | +const dialog = useDialog() | |
| 156 | +const toast = useToast() | |
| 157 | +const formData = ref<${simpleClassName}>() // 主表数据 | |
| 158 | +#foreach ($subTable in $subTables) | |
| 159 | +#set ($index = $foreach.count - 1) | |
| 160 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 161 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 162 | +const ${subClassNameVar}List = ref<${subSimpleClassName}[]>([]) // ${subTable.classComment}列表 | |
| 163 | +const ${subClassNameVar}Total = ref(0) // ${subTable.classComment}总数 | |
| 164 | +const ${subClassNameVar}PagingRef = ref<any>() // ${subTable.classComment}分页组件引用 | |
| 165 | +#end | |
| 166 | +const deleting = ref(false) // 主表删除状态 | |
| 167 | + | |
| 168 | +/** 返回上一页 */ | |
| 169 | +function handleBack() { | |
| 170 | + navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index') | |
| 171 | +} | |
| 172 | + | |
| 173 | +/** 加载主表 */ | |
| 174 | +async function loadMain() { | |
| 175 | + if (props.id) { | |
| 176 | + formData.value = await get${simpleClassName}(props.id) | |
| 177 | + } | |
| 178 | +} | |
| 179 | + | |
| 180 | +#foreach ($subTable in $subTables) | |
| 181 | +#set ($index = $foreach.count - 1) | |
| 182 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 183 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 184 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 185 | +#set ($subPrimaryColumn = $subPrimaryColumns.get($index)) | |
| 186 | +/** 查询${subTable.classComment}列表 */ | |
| 187 | +async function query${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}List(pageNo: number, pageSize: number) { | |
| 188 | + if (!props.id) { | |
| 189 | + ${subClassNameVar}PagingRef.value?.completeByTotal([], 0) | |
| 190 | + return | |
| 191 | + } | |
| 192 | + try { | |
| 193 | + const data = await get${subSimpleClassName}Page({ ${subJoinColumn.javaField}: props.id, pageNo, pageSize }) | |
| 194 | + ${subClassNameVar}Total.value = data.total | |
| 195 | + ${subClassNameVar}PagingRef.value?.completeByTotal(data.list, data.total) | |
| 196 | + } catch { | |
| 197 | + ${subClassNameVar}PagingRef.value?.complete(false) | |
| 198 | + } | |
| 199 | +} | |
| 200 | + | |
| 201 | +/** 打开${subTable.classComment}表单 */ | |
| 202 | +function open${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}(item?: ${subSimpleClassName}) { | |
| 203 | + const query = [ | |
| 204 | + `${subJoinColumn.javaField}=#[[${]]#props.id#[[}]]#`, | |
| 205 | + item?.${subPrimaryColumn.javaField} ? `id=#[[${]]#item.${subPrimaryColumn.javaField}#[[}]]#` : '', | |
| 206 | + ].filter(Boolean).join('&') | |
| 207 | + uni.navigateTo({ url: `/pages-${table.moduleName}/${table.businessName}/${subSimpleClassName_strikeCases.get($index)}/form/index?#[[${]]#query#[[}]]#` }) | |
| 208 | +} | |
| 209 | + | |
| 210 | +/** 删除${subTable.classComment} */ | |
| 211 | +async function remove${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}(item: ${subSimpleClassName}) { | |
| 212 | + try { | |
| 213 | + await dialog.confirm({ title: '提示', msg: '确定要删除该${subTable.classComment}吗?' }) | |
| 214 | + } catch { | |
| 215 | + return | |
| 216 | + } | |
| 217 | + await delete${subSimpleClassName}(item.${subPrimaryColumn.javaField}!) | |
| 218 | + toast.success('删除成功') | |
| 219 | + ${subClassNameVar}PagingRef.value?.reload() | |
| 220 | +} | |
| 221 | + | |
| 222 | +#end | |
| 223 | +/** 编辑主表 */ | |
| 224 | +function handleEdit() { | |
| 225 | + uni.navigateTo({ url: `/pages-${table.moduleName}/${table.businessName}/form/index?id=#[[${]]#props.id#[[}]]#` }) | |
| 226 | +} | |
| 227 | + | |
| 228 | +/** 删除主表 */ | |
| 229 | +async function handleDelete() { | |
| 230 | + if (!props.id) { | |
| 231 | + return | |
| 232 | + } | |
| 233 | + try { | |
| 234 | + await dialog.confirm({ title: '提示', msg: '确定要删除该${table.classComment}吗?' }) | |
| 235 | + } catch { | |
| 236 | + return | |
| 237 | + } | |
| 238 | + deleting.value = true | |
| 239 | + try { | |
| 240 | + await delete${simpleClassName}(props.id) | |
| 241 | + toast.success('删除成功') | |
| 242 | + uni.$emit('${table.moduleName}:${table.businessName}:reload') | |
| 243 | + delay(handleBack) | |
| 244 | + } finally { | |
| 245 | + deleting.value = false | |
| 246 | + } | |
| 247 | +} | |
| 248 | + | |
| 249 | +/** 进入页面加载(含编辑和子表返回后刷新) */ | |
| 250 | +onShow(() => { | |
| 251 | + loadMain() | |
| 252 | + nextTick(() => { | |
| 253 | +#foreach ($subClassNameVar in $subClassNameVars) | |
| 254 | + ${subClassNameVar}PagingRef.value?.reload() | |
| 255 | +#end | |
| 256 | + }) | |
| 257 | +}) | |
| 258 | +</script> | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail_master.vue.vm
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="${table.classComment}详情" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <view class="pb-160rpx"> | |
| 11 | + <!-- 主表详情 --> | |
| 12 | + <wd-cell-group border title="${table.classComment}信息"> | |
| 13 | +#foreach($column in $columns) | |
| 14 | +#if ($column.primaryKey || $column.listOperationResult || $column.createOperation || $column.updateOperation) | |
| 15 | +#if ($column.dictType && "" != $column.dictType) | |
| 16 | + <wd-cell title="${column.columnComment}"> | |
| 17 | + <dict-tag :type="DICT_TYPE.${column.dictType.toUpperCase()}" :value="formData?.${column.javaField}" /> | |
| 18 | + </wd-cell> | |
| 19 | +#elseif ($column.javaType == "LocalDateTime") | |
| 20 | + <wd-cell title="${column.columnComment}" :value="formatDateTime(formData?.${column.javaField}) || '-'" /> | |
| 21 | +#else | |
| 22 | + <wd-cell title="${column.columnComment}" :value="formData?.${column.javaField} ?? '-'" /> | |
| 23 | +#end | |
| 24 | +#end | |
| 25 | +#end | |
| 26 | + </wd-cell-group> | |
| 27 | + | |
| 28 | +#foreach ($subTable in $subTables) | |
| 29 | +#set ($index = $foreach.count - 1) | |
| 30 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 31 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 32 | +#set ($subPrimaryColumn = $subPrimaryColumns.get($index)) | |
| 33 | + <!-- 子表:${subTable.classComment} --> | |
| 34 | + <view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]"> | |
| 35 | + ${subTable.classComment}#if($subTable.subJoinMany)({{ ${subClassNameVar}s.length }})#end | |
| 36 | + </view> | |
| 37 | + <view class="px-24rpx"> | |
| 38 | +#if ($subTable.subJoinMany) | |
| 39 | + <view v-if="!${subClassNameVar}s.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 40 | + 暂无${subTable.classComment} | |
| 41 | + </view> | |
| 42 | + <view | |
| 43 | + v-for="item in ${subClassNameVar}s" | |
| 44 | + :key="item.${subPrimaryColumn.javaField}" | |
| 45 | + class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 46 | + > | |
| 47 | +#foreach ($column in $subColumns) | |
| 48 | +#if ($column.listOperationResult && !$column.primaryKey) | |
| 49 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 50 | + ${column.columnComment}:{{ item.${column.javaField} ?? '-' }} | |
| 51 | + </view> | |
| 52 | +#end | |
| 53 | +#end | |
| 54 | + </view> | |
| 55 | +#else | |
| 56 | + <view class="rounded-12rpx bg-white p-24rpx shadow-sm"> | |
| 57 | + <template v-if="${subClassNameVar}"> | |
| 58 | +#foreach ($column in $subColumns) | |
| 59 | +#if ($column.listOperationResult && !$column.primaryKey) | |
| 60 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 61 | + ${column.columnComment}:{{ ${subClassNameVar}.${column.javaField} ?? '-' }} | |
| 62 | + </view> | |
| 63 | +#end | |
| 64 | +#end | |
| 65 | + </template> | |
| 66 | + <view v-else class="text-center text-26rpx text-[#999]"> | |
| 67 | + 暂无${subTable.classComment} | |
| 68 | + </view> | |
| 69 | + </view> | |
| 70 | +#end | |
| 71 | + </view> | |
| 72 | +#end | |
| 73 | + </view> | |
| 74 | + | |
| 75 | + <!-- 底部操作按钮 --> | |
| 76 | + <view class="yd-detail-footer"> | |
| 77 | + <view class="yd-detail-footer-actions"> | |
| 78 | + <wd-button | |
| 79 | + v-if="hasAccessByCodes(['${permissionPrefix}:update'])" | |
| 80 | + class="flex-1" type="warning" @click="handleEdit" | |
| 81 | + > | |
| 82 | + 编辑 | |
| 83 | + </wd-button> | |
| 84 | + <wd-button | |
| 85 | + v-if="hasAccessByCodes(['${permissionPrefix}:delete'])" | |
| 86 | + class="flex-1" type="danger" :loading="deleting" @click="handleDelete" | |
| 87 | + > | |
| 88 | + 删除 | |
| 89 | + </wd-button> | |
| 90 | + </view> | |
| 91 | + </view> | |
| 92 | + </view> | |
| 93 | +</template> | |
| 94 | + | |
| 95 | +<script lang="ts" setup> | |
| 96 | +#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) | |
| 97 | +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") | |
| 98 | +#set ($primaryTsType = "number") | |
| 99 | +#else | |
| 100 | +#set ($primaryTsType = "string") | |
| 101 | +#end | |
| 102 | +#set ($hasDict = 0) | |
| 103 | +#set ($hasDateTime = 0) | |
| 104 | +#foreach($column in $columns) | |
| 105 | +#if ($column.dictType && "" != $column.dictType) | |
| 106 | +#set ($hasDict = 1) | |
| 107 | +#end | |
| 108 | +#if ($column.javaType == "LocalDateTime") | |
| 109 | +#set ($hasDateTime = 1) | |
| 110 | +#end | |
| 111 | +#end | |
| 112 | +import type { | |
| 113 | + ${simpleClassName}, | |
| 114 | +#foreach ($subSimpleClassName in $subSimpleClassNames) | |
| 115 | + ${subSimpleClassName}, | |
| 116 | +#end | |
| 117 | +} from '@/api/${table.moduleName}/${table.businessName}' | |
| 118 | +import { onShow } from '@dcloudio/uni-app' | |
| 119 | +import { ref } from 'vue' | |
| 120 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 121 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 122 | +import { | |
| 123 | + delete${simpleClassName}, | |
| 124 | + get${simpleClassName}, | |
| 125 | +#foreach ($subTable in $subTables) | |
| 126 | +#set ($index = $foreach.count - 1) | |
| 127 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 128 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 129 | +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + $subJoinColumn.javaField.substring(1)) | |
| 130 | +#if ($subTable.subJoinMany) | |
| 131 | + get${subSimpleClassName}ListBy${SubJoinColumnName}, | |
| 132 | +#else | |
| 133 | + get${subSimpleClassName}By${SubJoinColumnName}, | |
| 134 | +#end | |
| 135 | +#end | |
| 136 | +} from '@/api/${table.moduleName}/${table.businessName}' | |
| 137 | +import { useAccess } from '@/hooks/useAccess' | |
| 138 | +import { delay, navigateBackPlus } from '@/utils' | |
| 139 | +#if ($hasDict == 1) | |
| 140 | +import { DICT_TYPE } from '@/utils/constants' | |
| 141 | +#end | |
| 142 | +#if ($hasDateTime == 1) | |
| 143 | +import { formatDateTime } from '@/utils/date' | |
| 144 | +#end | |
| 145 | + | |
| 146 | +const props = defineProps<{ | |
| 147 | + id?: ${primaryTsType} | |
| 148 | +}>() | |
| 149 | + | |
| 150 | +definePage({ | |
| 151 | + style: { | |
| 152 | + navigationBarTitleText: '', | |
| 153 | + navigationStyle: 'custom', | |
| 154 | + }, | |
| 155 | +}) | |
| 156 | + | |
| 157 | +const { hasAccessByCodes } = useAccess() | |
| 158 | +const dialog = useDialog() | |
| 159 | +const toast = useToast() | |
| 160 | +const formData = ref<${simpleClassName}>() // 主表数据 | |
| 161 | +#foreach ($subTable in $subTables) | |
| 162 | +#set ($index = $foreach.count - 1) | |
| 163 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 164 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 165 | +#if ($subTable.subJoinMany) | |
| 166 | +const ${subClassNameVar}s = ref<${subSimpleClassName}[]>([]) // ${subTable.classComment}数据 | |
| 167 | +#else | |
| 168 | +const ${subClassNameVar} = ref<${subSimpleClassName} | null>(null) // ${subTable.classComment}数据 | |
| 169 | +#end | |
| 170 | +#end | |
| 171 | +const deleting = ref(false) // 删除状态 | |
| 172 | + | |
| 173 | +/** 返回上一页 */ | |
| 174 | +function handleBack() { | |
| 175 | + navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index') | |
| 176 | +} | |
| 177 | + | |
| 178 | +/** 加载主表和子表详情 */ | |
| 179 | +async function getDetail() { | |
| 180 | + if (!props.id || deleting.value) { | |
| 181 | + return | |
| 182 | + } | |
| 183 | + const [data#foreach ($subTable in $subTables)#set ($index = $foreach.count - 1), ${subClassNameVars.get($index)}Data#end] = await Promise.all([ | |
| 184 | + get${simpleClassName}(props.id), | |
| 185 | +#foreach ($subTable in $subTables) | |
| 186 | +#set ($index = $foreach.count - 1) | |
| 187 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 188 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 189 | +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + $subJoinColumn.javaField.substring(1)) | |
| 190 | +#if ($subTable.subJoinMany) | |
| 191 | + get${subSimpleClassName}ListBy${SubJoinColumnName}(props.id), | |
| 192 | +#else | |
| 193 | + get${subSimpleClassName}By${SubJoinColumnName}(props.id), | |
| 194 | +#end | |
| 195 | +#end | |
| 196 | + ]) | |
| 197 | + formData.value = data | |
| 198 | +#foreach ($subTable in $subTables) | |
| 199 | +#set ($index = $foreach.count - 1) | |
| 200 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 201 | +#if ($subTable.subJoinMany) | |
| 202 | + ${subClassNameVar}s.value = ${subClassNameVar}Data || [] | |
| 203 | +#else | |
| 204 | + ${subClassNameVar}.value = ${subClassNameVar}Data || null | |
| 205 | +#end | |
| 206 | +#end | |
| 207 | +} | |
| 208 | + | |
| 209 | +/** 编辑${table.classComment} */ | |
| 210 | +function handleEdit() { | |
| 211 | + uni.navigateTo({ url: `/pages-${table.moduleName}/${table.businessName}/form/index?id=#[[${]]#props.id#[[}]]#` }) | |
| 212 | +} | |
| 213 | + | |
| 214 | +/** 删除${table.classComment} */ | |
| 215 | +async function handleDelete() { | |
| 216 | + if (!props.id) { | |
| 217 | + return | |
| 218 | + } | |
| 219 | + try { | |
| 220 | + await dialog.confirm({ title: '提示', msg: '确定要删除该${table.classComment}吗?' }) | |
| 221 | + } catch { | |
| 222 | + return | |
| 223 | + } | |
| 224 | + deleting.value = true | |
| 225 | + try { | |
| 226 | + await delete${simpleClassName}(props.id) | |
| 227 | + toast.success('删除成功') | |
| 228 | + uni.$emit('${table.moduleName}:${table.businessName}:reload') | |
| 229 | + delay(handleBack) | |
| 230 | + } finally { | |
| 231 | + deleting.value = false | |
| 232 | + } | |
| 233 | +} | |
| 234 | + | |
| 235 | +/** 进入页面加载(含编辑返回后刷新) */ | |
| 236 | +onShow(() => { | |
| 237 | + getDetail() | |
| 238 | +}) | |
| 239 | +</script> | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm
| ... | ... | @@ -12,21 +12,31 @@ |
| 12 | 12 | <wd-form ref="formRef" :model="formData" :schema="formSchema"> |
| 13 | 13 | <wd-cell-group border> |
| 14 | 14 | #foreach($column in $columns) |
| 15 | - #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey) | |
| 16 | - #set ($dictType = $column.dictType) | |
| 17 | - #set ($javaField = $column.javaField) | |
| 18 | - #set ($javaType = $column.javaType) | |
| 19 | - #set ($comment = $column.columnComment) | |
| 20 | - #set ($dictMethod = "getDictOptions") | |
| 21 | - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") | |
| 22 | - #set ($dictMethod = "getIntDictOptions") | |
| 23 | - #elseif ($javaType == "String") | |
| 24 | - #set ($dictMethod = "getStrDictOptions") | |
| 25 | - #elseif ($javaType == "Boolean") | |
| 26 | - #set ($dictMethod = "getBoolDictOptions") | |
| 27 | - #end | |
| 28 | - ## 下拉选择(å—典)→ yd-form-picker,内部按 dictType è§£æžé€‰é¡¹ï¼Œé€‚åˆé€‰é¡¹è¾ƒå¤š | |
| 29 | - #if ($column.htmlType == "select" && $dictType && "" != $dictType) | |
| 15 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey) | |
| 16 | +#set ($dictType = $column.dictType) | |
| 17 | +#set ($javaField = $column.javaField) | |
| 18 | +#set ($javaType = $column.javaType) | |
| 19 | +#set ($comment = $column.columnComment) | |
| 20 | +#set ($dictMethod = "getDictOptions") | |
| 21 | +#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") | |
| 22 | +#set ($dictMethod = "getIntDictOptions") | |
| 23 | +#elseif ($javaType == "String") | |
| 24 | +#set ($dictMethod = "getStrDictOptions") | |
| 25 | +#elseif ($javaType == "Boolean") | |
| 26 | +#set ($dictMethod = "getBoolDictOptions") | |
| 27 | +#end | |
| 28 | +## æ ‘è¡¨çš„çˆ¶çº§é€‰æ‹© | |
| 29 | +#if ($table.templateType == 2 && $column.id == $treeParentColumn.id) | |
| 30 | + <yd-tree-select | |
| 31 | + v-model="formData.${javaField}" | |
| 32 | + :data="treeData" | |
| 33 | + label="${comment}" | |
| 34 | + prop="${javaField}" | |
| 35 | + :props="{ label: '${treeNameColumn.javaField}', value: '${primaryColumn.javaField}' }" | |
| 36 | + placeholder="请选择${comment}" | |
| 37 | + /> | |
| 38 | +## 下拉选择(å—典)→ yd-form-picker,内部按 dictType è§£æžé€‰é¡¹ï¼Œé€‚åˆé€‰é¡¹è¾ƒå¤š | |
| 39 | +#elseif ($column.htmlType == "select" && $dictType && "" != $dictType) | |
| 30 | 40 | <yd-form-picker |
| 31 | 41 | v-model="formData.${javaField}" |
| 32 | 42 | label="${comment}" |
| ... | ... | @@ -38,8 +48,8 @@ |
| 38 | 48 | #end |
| 39 | 49 | placeholder="请选择${comment}" |
| 40 | 50 | /> |
| 41 | - ## å•选(å—典)→ radio-group,适åˆé€‰é¡¹è¾ƒå°‘ | |
| 42 | - #elseif ($column.htmlType == "radio" && $dictType && "" != $dictType) | |
| 51 | +## å•选(å—典)→ radio-group,适åˆé€‰é¡¹è¾ƒå°‘ | |
| 52 | +#elseif ($column.htmlType == "radio" && $dictType && "" != $dictType) | |
| 43 | 53 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}" center> |
| 44 | 54 | <wd-radio-group v-model="formData.${javaField}" type="button"> |
| 45 | 55 | <wd-radio |
| ... | ... | @@ -51,11 +61,14 @@ |
| 51 | 61 | </wd-radio> |
| 52 | 62 | </wd-radio-group> |
| 53 | 63 | </wd-form-item> |
| 54 | - ## æ•°å—ç±»åž‹ï¼ˆæ— å—典) | |
| 55 | - #elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte") | |
| 64 | +## æ•°å—ç±»åž‹ï¼ˆæ— å—典) | |
| 65 | +#elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte") | |
| 56 | 66 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> |
| 57 | 67 | <wd-input-number |
| 58 | 68 | v-model="formData.${javaField}" |
| 69 | +#if ($column.nullable) | |
| 70 | + allow-null | |
| 71 | +#end | |
| 59 | 72 | #if (${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal") |
| 60 | 73 | :precision="2" |
| 61 | 74 | input-type="number" |
| ... | ... | @@ -63,37 +76,42 @@ |
| 63 | 76 | :min="0" |
| 64 | 77 | /> |
| 65 | 78 | </wd-form-item> |
| 66 | - ## 布尔类型 | |
| 67 | - #elseif (${javaType.toLowerCase()} == "boolean") | |
| 79 | +## 布尔类型 | |
| 80 | +#elseif (${javaType.toLowerCase()} == "boolean") | |
| 68 | 81 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}" center> |
| 69 | 82 | <wd-switch v-model="formData.${javaField}" /> |
| 70 | 83 | </wd-form-item> |
| 71 | - ## 日期时间类型 | |
| 72 | - #elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime") | |
| 73 | - #set ($pickerType = "date") | |
| 74 | - #if (${javaType.toLowerCase()} == "localdatetime") | |
| 75 | - #set ($pickerType = "datetime") | |
| 76 | - #end | |
| 84 | +## 日期时间类型 | |
| 85 | +#elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime") | |
| 86 | +#set ($pickerType = "date") | |
| 87 | +#if (${javaType.toLowerCase()} == "localdatetime") | |
| 88 | +#set ($pickerType = "datetime") | |
| 89 | +#end | |
| 77 | 90 | <wd-form-item |
| 78 | 91 | title="${comment}" |
| 79 | 92 | title-width="180rpx" |
| 80 | 93 | prop="${javaField}" |
| 81 | 94 | is-link |
| 82 | 95 | #if ($pickerType == "datetime") |
| 83 | - :value="formatDateTime(formData.${javaField})" | |
| 96 | + :value="formData.${javaField} ? formatDateTime(formData.${javaField}) : ''" | |
| 84 | 97 | #else |
| 85 | - :value="formatDate(formData.${javaField})" | |
| 98 | + :value="formData.${javaField} ? formatDate(formData.${javaField}) : ''" | |
| 86 | 99 | #end |
| 87 | 100 | placeholder="请选择${comment}" |
| 88 | 101 | @click="${javaField}Visible = true" |
| 89 | 102 | /> |
| 90 | 103 | <wd-datetime-picker |
| 91 | - v-model="formData.${javaField}" | |
| 92 | 104 | v-model:visible="${javaField}Visible" |
| 105 | + :model-value="formData.${javaField} || Date.now()" | |
| 93 | 106 | type="${pickerType}" |
| 107 | +#if ($pickerType == "datetime") | |
| 108 | + @update:model-value="value => formData.${javaField} = value" | |
| 109 | +#else | |
| 110 | + @update:model-value="value => formData.${javaField} = value" | |
| 111 | +#end | |
| 94 | 112 | /> |
| 95 | - ## 文本域 | |
| 96 | - #elseif ($column.htmlType == "textarea") | |
| 113 | +## 文本域 | |
| 114 | +#elseif ($column.htmlType == "textarea") | |
| 97 | 115 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> |
| 98 | 116 | <wd-textarea |
| 99 | 117 | v-model="formData.${javaField}" |
| ... | ... | @@ -103,18 +121,18 @@ |
| 103 | 121 | show-word-limit |
| 104 | 122 | /> |
| 105 | 123 | </wd-form-item> |
| 106 | - ## å›¾ç‰‡ä¸Šä¼ â†’ yd-upload-img(包 wd-form-item æ‰èƒ½æ³¨å†Œåˆ° wd-form åšæ ¡éªŒ/错误展示) | |
| 107 | - #elseif ($column.htmlType == "imageUpload") | |
| 124 | +## å›¾ç‰‡ä¸Šä¼ â†’ yd-upload-img(包 wd-form-item æ‰èƒ½æ³¨å†Œåˆ° wd-form åšæ ¡éªŒ/错误展示) | |
| 125 | +#elseif ($column.htmlType == "imageUpload") | |
| 108 | 126 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> |
| 109 | 127 | <yd-upload-img v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" /> |
| 110 | 128 | </wd-form-item> |
| 111 | - ## æ–‡ä»¶ä¸Šä¼ â†’ yd-upload-file | |
| 112 | - #elseif ($column.htmlType == "fileUpload") | |
| 129 | +## æ–‡ä»¶ä¸Šä¼ â†’ yd-upload-file | |
| 130 | +#elseif ($column.htmlType == "fileUpload") | |
| 113 | 131 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> |
| 114 | 132 | <yd-upload-file v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" /> |
| 115 | 133 | </wd-form-item> |
| 116 | - ## 默认:文本输入 | |
| 117 | - #else | |
| 134 | +## 默认:文本输入 | |
| 135 | +#else | |
| 118 | 136 | <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}"> |
| 119 | 137 | <wd-input |
| 120 | 138 | v-model="formData.${javaField}" |
| ... | ... | @@ -122,11 +140,259 @@ |
| 122 | 140 | placeholder="请输入${comment}" |
| 123 | 141 | /> |
| 124 | 142 | </wd-form-item> |
| 125 | - #end | |
| 126 | - #end | |
| 143 | +#end | |
| 144 | +#end | |
| 127 | 145 | #end |
| 128 | 146 | </wd-cell-group> |
| 129 | 147 | </wd-form> |
| 148 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 149 | +#foreach ($subTable in $subTables) | |
| 150 | +#set ($index = $foreach.count - 1) | |
| 151 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 152 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 153 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 154 | + | |
| 155 | + <!-- å表:${subTable.classComment} --> | |
| 156 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 157 | + <text class="text-28rpx text-[#333] font-semibold">${subTable.classComment}</text> | |
| 158 | +#if ($subTable.subJoinMany) | |
| 159 | + <wd-button size="small" type="primary" variant="plain" @click="add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}"> | |
| 160 | + æ·»åŠ | |
| 161 | + </wd-button> | |
| 162 | +#else | |
| 163 | + <wd-button | |
| 164 | + v-if="!${subClassNameVar}" | |
| 165 | + size="small" | |
| 166 | + type="primary" | |
| 167 | + variant="plain" | |
| 168 | + @click="add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}()" | |
| 169 | + > | |
| 170 | + æ·»åŠ | |
| 171 | + </wd-button> | |
| 172 | +#end | |
| 173 | + </view> | |
| 174 | + <view class="px-24rpx"> | |
| 175 | +#if ($subTable.subJoinMany) | |
| 176 | + <view v-if="!${subClassNameVar}s.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 177 | + æš‚æ— ${subTable.classComment} | |
| 178 | + </view> | |
| 179 | + <view | |
| 180 | + v-for="(item, index) in ${subClassNameVar}s" | |
| 181 | + :key="index" | |
| 182 | + class="mb-20rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 183 | + > | |
| 184 | + <view class="mb-16rpx flex justify-end"> | |
| 185 | + <wd-button size="small" type="danger" variant="plain" @click="remove${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}(index)"> | |
| 186 | + åˆ é™¤ | |
| 187 | + </wd-button> | |
| 188 | + </view> | |
| 189 | +#foreach ($column in $subColumns) | |
| 190 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) | |
| 191 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 192 | +#set ($dictMethod = "getDictOptions") | |
| 193 | +#if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short") | |
| 194 | +#set ($dictMethod = "getIntDictOptions") | |
| 195 | +#elseif ($column.javaType == "String") | |
| 196 | +#set ($dictMethod = "getStrDictOptions") | |
| 197 | +#elseif ($column.javaType == "Boolean") | |
| 198 | +#set ($dictMethod = "getBoolDictOptions") | |
| 199 | +#end | |
| 200 | +#if ($column.htmlType == "select" && $column.dictType && "" != $column.dictType) | |
| 201 | + <yd-form-picker | |
| 202 | + v-model="item.${column.javaField}" | |
| 203 | + label="${column.columnComment}" | |
| 204 | + label-width="180rpx" | |
| 205 | + :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}" | |
| 206 | +#if ($column.javaType == "String") | |
| 207 | + dict-kind="str" | |
| 208 | +#end | |
| 209 | + placeholder="请选择${column.columnComment}" | |
| 210 | + /> | |
| 211 | +#elseif ($column.htmlType == "radio" && $column.dictType && "" != $column.dictType) | |
| 212 | + <wd-form-item title="${column.columnComment}" title-width="180rpx" center> | |
| 213 | + <wd-radio-group v-model="item.${column.javaField}" type="button"> | |
| 214 | + <wd-radio | |
| 215 | + v-for="dict in $dictMethod(DICT_TYPE.${column.dictType.toUpperCase()})" | |
| 216 | + :key="dict.value" | |
| 217 | + :value="dict.value" | |
| 218 | + > | |
| 219 | + {{ dict.label }} | |
| 220 | + </wd-radio> | |
| 221 | + </wd-radio-group> | |
| 222 | + </wd-form-item> | |
| 223 | +#elseif ($javaType == "boolean") | |
| 224 | + <wd-form-item title="${column.columnComment}" title-width="180rpx" center> | |
| 225 | + <wd-switch v-model="item.${column.javaField}" /> | |
| 226 | + </wd-form-item> | |
| 227 | +#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") | |
| 228 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 229 | + <wd-input-number | |
| 230 | + v-model="item.${column.javaField}" | |
| 231 | +#if($column.nullable) | |
| 232 | + allow-null | |
| 233 | +#end | |
| 234 | +#if ($javaType == "double" || $javaType == "bigdecimal") | |
| 235 | + :precision="2" | |
| 236 | + input-type="number" | |
| 237 | +#end | |
| 238 | + :min="0" | |
| 239 | + /> | |
| 240 | + </wd-form-item> | |
| 241 | +#elseif ($javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") | |
| 242 | +#set ($subPickerType = "date") | |
| 243 | +#if ($javaType == "localdatetime") | |
| 244 | +#set ($subPickerType = "datetime") | |
| 245 | +#end | |
| 246 | + <wd-form-item | |
| 247 | + title="${column.columnComment}" | |
| 248 | + title-width="180rpx" | |
| 249 | + is-link | |
| 250 | +#if ($javaType == "localdatetime") | |
| 251 | + :value="item.${column.javaField} ? formatDateTime(item.${column.javaField}) : ''" | |
| 252 | +#else | |
| 253 | + :value="item.${column.javaField} ? formatDate(item.${column.javaField}) : ''" | |
| 254 | +#end | |
| 255 | + placeholder="请选择${column.columnComment}" | |
| 256 | + @click="subDateVisible[`${subClassNameVar}s.#[[${]]#index#[[}]]#.${column.javaField}`] = true" | |
| 257 | + /> | |
| 258 | + <wd-datetime-picker | |
| 259 | + v-model:visible="subDateVisible[`${subClassNameVar}s.#[[${]]#index#[[}]]#.${column.javaField}`]" | |
| 260 | + :model-value="item.${column.javaField} || Date.now()" | |
| 261 | + type="${subPickerType}" | |
| 262 | +#if ($javaType == "localdatetime") | |
| 263 | + @update:model-value="value => item.${column.javaField} = value" | |
| 264 | +#else | |
| 265 | + @update:model-value="value => item.${column.javaField} = value" | |
| 266 | +#end | |
| 267 | + /> | |
| 268 | +#elseif ($column.htmlType == "textarea") | |
| 269 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 270 | + <wd-textarea v-model="item.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> | |
| 271 | + </wd-form-item> | |
| 272 | +#elseif ($column.htmlType == "imageUpload") | |
| 273 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 274 | + <yd-upload-img v-model="item.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> | |
| 275 | + </wd-form-item> | |
| 276 | +#elseif ($column.htmlType == "fileUpload") | |
| 277 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 278 | + <yd-upload-file v-model="item.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> | |
| 279 | + </wd-form-item> | |
| 280 | +#else | |
| 281 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 282 | + <wd-input v-model="item.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> | |
| 283 | + </wd-form-item> | |
| 284 | +#end | |
| 285 | +#end | |
| 286 | +#end | |
| 287 | + </view> | |
| 288 | +#else | |
| 289 | + <view v-if="!${subClassNameVar}" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 290 | + æš‚æ— ${subTable.classComment} | |
| 291 | + </view> | |
| 292 | + <view v-else class="rounded-12rpx bg-white p-24rpx shadow-sm"> | |
| 293 | +#foreach ($column in $subColumns) | |
| 294 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) | |
| 295 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 296 | +#set ($dictMethod = "getDictOptions") | |
| 297 | +#if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short") | |
| 298 | +#set ($dictMethod = "getIntDictOptions") | |
| 299 | +#elseif ($column.javaType == "String") | |
| 300 | +#set ($dictMethod = "getStrDictOptions") | |
| 301 | +#elseif ($column.javaType == "Boolean") | |
| 302 | +#set ($dictMethod = "getBoolDictOptions") | |
| 303 | +#end | |
| 304 | +#if ($column.htmlType == "select" && $column.dictType && "" != $column.dictType) | |
| 305 | + <yd-form-picker | |
| 306 | + v-model="${subClassNameVar}.${column.javaField}" | |
| 307 | + label="${column.columnComment}" | |
| 308 | + label-width="180rpx" | |
| 309 | + :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}" | |
| 310 | +#if ($column.javaType == "String") | |
| 311 | + dict-kind="str" | |
| 312 | +#end | |
| 313 | + placeholder="请选择${column.columnComment}" | |
| 314 | + /> | |
| 315 | +#elseif ($column.htmlType == "radio" && $column.dictType && "" != $column.dictType) | |
| 316 | + <wd-form-item title="${column.columnComment}" title-width="180rpx" center> | |
| 317 | + <wd-radio-group v-model="${subClassNameVar}.${column.javaField}" type="button"> | |
| 318 | + <wd-radio | |
| 319 | + v-for="dict in $dictMethod(DICT_TYPE.${column.dictType.toUpperCase()})" | |
| 320 | + :key="dict.value" | |
| 321 | + :value="dict.value" | |
| 322 | + > | |
| 323 | + {{ dict.label }} | |
| 324 | + </wd-radio> | |
| 325 | + </wd-radio-group> | |
| 326 | + </wd-form-item> | |
| 327 | +#elseif ($javaType == "boolean") | |
| 328 | + <wd-form-item title="${column.columnComment}" title-width="180rpx" center> | |
| 329 | + <wd-switch v-model="${subClassNameVar}.${column.javaField}" /> | |
| 330 | + </wd-form-item> | |
| 331 | +#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") | |
| 332 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 333 | + <wd-input-number | |
| 334 | + v-model="${subClassNameVar}.${column.javaField}" | |
| 335 | +#if($column.nullable) | |
| 336 | + allow-null | |
| 337 | +#end | |
| 338 | +#if ($javaType == "double" || $javaType == "bigdecimal") | |
| 339 | + :precision="2" | |
| 340 | + input-type="number" | |
| 341 | +#end | |
| 342 | + :min="0" | |
| 343 | + /> | |
| 344 | + </wd-form-item> | |
| 345 | +#elseif ($javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") | |
| 346 | +#set ($subPickerType = "date") | |
| 347 | +#if ($javaType == "localdatetime") | |
| 348 | +#set ($subPickerType = "datetime") | |
| 349 | +#end | |
| 350 | + <wd-form-item | |
| 351 | + title="${column.columnComment}" | |
| 352 | + title-width="180rpx" | |
| 353 | + is-link | |
| 354 | +#if ($javaType == "localdatetime") | |
| 355 | + :value="${subClassNameVar}.${column.javaField} ? formatDateTime(${subClassNameVar}.${column.javaField}) : ''" | |
| 356 | +#else | |
| 357 | + :value="${subClassNameVar}.${column.javaField} ? formatDate(${subClassNameVar}.${column.javaField}) : ''" | |
| 358 | +#end | |
| 359 | + placeholder="请选择${column.columnComment}" | |
| 360 | + @click="subDateVisible['${subClassNameVar}.${column.javaField}'] = true" | |
| 361 | + /> | |
| 362 | + <wd-datetime-picker | |
| 363 | + v-model:visible="subDateVisible['${subClassNameVar}.${column.javaField}']" | |
| 364 | + :model-value="${subClassNameVar}.${column.javaField} || Date.now()" | |
| 365 | + type="${subPickerType}" | |
| 366 | +#if ($javaType == "localdatetime") | |
| 367 | + @update:model-value="value => ${subClassNameVar}.${column.javaField} = value" | |
| 368 | +#else | |
| 369 | + @update:model-value="value => ${subClassNameVar}.${column.javaField} = value" | |
| 370 | +#end | |
| 371 | + /> | |
| 372 | +#elseif ($column.htmlType == "textarea") | |
| 373 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 374 | + <wd-textarea v-model="${subClassNameVar}.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> | |
| 375 | + </wd-form-item> | |
| 376 | +#elseif ($column.htmlType == "imageUpload") | |
| 377 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 378 | + <yd-upload-img v-model="${subClassNameVar}.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> | |
| 379 | + </wd-form-item> | |
| 380 | +#elseif ($column.htmlType == "fileUpload") | |
| 381 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 382 | + <yd-upload-file v-model="${subClassNameVar}.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> | |
| 383 | + </wd-form-item> | |
| 384 | +#else | |
| 385 | + <wd-form-item title="${column.columnComment}" title-width="180rpx"> | |
| 386 | + <wd-input v-model="${subClassNameVar}.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> | |
| 387 | + </wd-form-item> | |
| 388 | +#end | |
| 389 | +#end | |
| 390 | +#end | |
| 391 | + </view> | |
| 392 | +#end | |
| 393 | + </view> | |
| 394 | +#end | |
| 395 | +#end | |
| 130 | 396 | </view> |
| 131 | 397 | |
| 132 | 398 | <!-- 底部ä¿å˜æŒ‰é’® --> |
| ... | ... | @@ -154,54 +420,113 @@ import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' |
| 154 | 420 | #set ($hasDict = 0) |
| 155 | 421 | #set ($hasDate = 0) |
| 156 | 422 | #set ($hasDateTime = 0) |
| 423 | +#set ($hasSubDate = 0) | |
| 157 | 424 | #set ($hasGetDictOptions = 0) |
| 158 | 425 | #set ($hasGetIntDictOptions = 0) |
| 159 | 426 | #set ($hasGetStrDictOptions = 0) |
| 160 | 427 | #set ($hasGetBoolDictOptions = 0) |
| 161 | 428 | #foreach ($column in $columns) |
| 162 | - #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 429 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 163 | 430 | && ($column.htmlType == "select" || $column.htmlType == "radio") |
| 164 | 431 | && $column.dictType && "" != $column.dictType) |
| 165 | - #set ($hasDict = 1) | |
| 166 | - #if ($column.htmlType == "radio" && ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")) | |
| 167 | - #set ($hasGetIntDictOptions = 1) | |
| 168 | - #elseif ($column.htmlType == "radio" && $column.javaType == "String") | |
| 169 | - #set ($hasGetStrDictOptions = 1) | |
| 170 | - #elseif ($column.htmlType == "radio" && $column.javaType == "Boolean") | |
| 171 | - #set ($hasGetBoolDictOptions = 1) | |
| 172 | - #elseif ($column.htmlType == "radio") | |
| 173 | - #set ($hasGetDictOptions = 1) | |
| 174 | - #end | |
| 175 | - #end | |
| 176 | - #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 432 | +#set ($hasDict = 1) | |
| 433 | +#if ($column.htmlType == "radio" && ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")) | |
| 434 | +#set ($hasGetIntDictOptions = 1) | |
| 435 | +#elseif ($column.htmlType == "radio" && $column.javaType == "String") | |
| 436 | +#set ($hasGetStrDictOptions = 1) | |
| 437 | +#elseif ($column.htmlType == "radio" && $column.javaType == "Boolean") | |
| 438 | +#set ($hasGetBoolDictOptions = 1) | |
| 439 | +#elseif ($column.htmlType == "radio") | |
| 440 | +#set ($hasGetDictOptions = 1) | |
| 441 | +#end | |
| 442 | +#end | |
| 443 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 177 | 444 | && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate")) |
| 178 | - #set ($hasDate = 1) | |
| 179 | - #end | |
| 180 | - #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 445 | +#set ($hasDate = 1) | |
| 446 | +#end | |
| 447 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 181 | 448 | && ${column.javaType.toLowerCase()} == "localdatetime") |
| 182 | - #set ($hasDateTime = 1) | |
| 183 | - #end | |
| 449 | +#set ($hasDateTime = 1) | |
| 184 | 450 | #end |
| 185 | -import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' | |
| 451 | +#end | |
| 452 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 453 | +#foreach ($subColumns in $subColumnsList) | |
| 454 | +#foreach ($column in $subColumns) | |
| 455 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 456 | + && ($column.htmlType == "select" || $column.htmlType == "radio") | |
| 457 | + && $column.dictType && "" != $column.dictType) | |
| 458 | +#set ($hasDict = 1) | |
| 459 | +#if ($column.htmlType == "radio" && ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")) | |
| 460 | +#set ($hasGetIntDictOptions = 1) | |
| 461 | +#elseif ($column.htmlType == "radio" && $column.javaType == "String") | |
| 462 | +#set ($hasGetStrDictOptions = 1) | |
| 463 | +#elseif ($column.htmlType == "radio" && $column.javaType == "Boolean") | |
| 464 | +#set ($hasGetBoolDictOptions = 1) | |
| 465 | +#elseif ($column.htmlType == "radio") | |
| 466 | +#set ($hasGetDictOptions = 1) | |
| 467 | +#end | |
| 468 | +#end | |
| 469 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 470 | + && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate")) | |
| 471 | +#set ($hasDate = 1) | |
| 472 | +#set ($hasSubDate = 1) | |
| 473 | +#end | |
| 474 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey | |
| 475 | + && ${column.javaType.toLowerCase()} == "localdatetime") | |
| 476 | +#set ($hasDateTime = 1) | |
| 477 | +#set ($hasSubDate = 1) | |
| 478 | +#end | |
| 479 | +#end | |
| 480 | +#end | |
| 481 | +#end | |
| 482 | +import type { | |
| 483 | + ${simpleClassName}, | |
| 484 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 485 | +#foreach ($subSimpleClassName in $subSimpleClassNames) | |
| 486 | + ${subSimpleClassName}, | |
| 487 | +#end | |
| 488 | +#end | |
| 489 | +} from '@/api/${table.moduleName}/${table.businessName}' | |
| 186 | 490 | import { computed, onMounted, ref } from 'vue' |
| 187 | 491 | import { useToast } from '@wot-ui/ui/components/wd-toast' |
| 492 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 493 | +import { | |
| 494 | + create${simpleClassName}, | |
| 495 | + get${simpleClassName}, | |
| 496 | +#foreach ($subTable in $subTables) | |
| 497 | +#set ($index = $foreach.count - 1) | |
| 498 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 499 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 500 | +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + $subJoinColumn.javaField.substring(1)) | |
| 501 | +#if ($subTable.subJoinMany) | |
| 502 | + get${subSimpleClassName}ListBy${SubJoinColumnName}, | |
| 503 | +#else | |
| 504 | + get${subSimpleClassName}By${SubJoinColumnName}, | |
| 505 | +#end | |
| 506 | +#end | |
| 507 | + update${simpleClassName}, | |
| 508 | +} from '@/api/${table.moduleName}/${table.businessName}' | |
| 509 | +#elseif ($table.templateType == 2) | |
| 510 | +import { create${simpleClassName}, get${simpleClassName}, get${simpleClassName}List, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' | |
| 511 | +#else | |
| 188 | 512 | import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' |
| 513 | +#end | |
| 189 | 514 | #if ($hasGetDictOptions == 1 || $hasGetIntDictOptions == 1 || $hasGetStrDictOptions == 1 || $hasGetBoolDictOptions == 1) |
| 190 | - #set ($dictImportNames = "") | |
| 191 | - #if ($hasGetDictOptions == 1) | |
| 192 | - #set ($dictImportNames = "${dictImportNames}getDictOptions, ") | |
| 193 | - #end | |
| 194 | - #if ($hasGetIntDictOptions == 1) | |
| 195 | - #set ($dictImportNames = "${dictImportNames}getIntDictOptions, ") | |
| 196 | - #end | |
| 197 | - #if ($hasGetStrDictOptions == 1) | |
| 198 | - #set ($dictImportNames = "${dictImportNames}getStrDictOptions, ") | |
| 199 | - #end | |
| 200 | - #if ($hasGetBoolDictOptions == 1) | |
| 201 | - #set ($dictImportNames = "${dictImportNames}getBoolDictOptions, ") | |
| 202 | - #end | |
| 203 | - #set ($dictImportNames = $dictImportNames.trim()) | |
| 204 | - #set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1)) | |
| 515 | +#set ($dictImportNames = "") | |
| 516 | +#if ($hasGetDictOptions == 1) | |
| 517 | +#set ($dictImportNames = "${dictImportNames}getDictOptions, ") | |
| 518 | +#end | |
| 519 | +#if ($hasGetIntDictOptions == 1) | |
| 520 | +#set ($dictImportNames = "${dictImportNames}getIntDictOptions, ") | |
| 521 | +#end | |
| 522 | +#if ($hasGetStrDictOptions == 1) | |
| 523 | +#set ($dictImportNames = "${dictImportNames}getStrDictOptions, ") | |
| 524 | +#end | |
| 525 | +#if ($hasGetBoolDictOptions == 1) | |
| 526 | +#set ($dictImportNames = "${dictImportNames}getBoolDictOptions, ") | |
| 527 | +#end | |
| 528 | +#set ($dictImportNames = $dictImportNames.trim()) | |
| 529 | +#set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1)) | |
| 205 | 530 | import { $dictImportNames } from '@/hooks/useDict' |
| 206 | 531 | #end |
| 207 | 532 | import { delay, navigateBackPlus } from '@/utils' |
| ... | ... | @@ -209,21 +534,27 @@ import { delay, navigateBackPlus } from '@/utils' |
| 209 | 534 | import { DICT_TYPE } from '@/utils/constants' |
| 210 | 535 | #end |
| 211 | 536 | #if ($hasDate == 1 || $hasDateTime == 1) |
| 212 | - #set ($dateImportNames = "") | |
| 213 | - #if ($hasDate == 1) | |
| 214 | - #set ($dateImportNames = "${dateImportNames}formatDate, ") | |
| 215 | - #end | |
| 216 | - #if ($hasDateTime == 1) | |
| 217 | - #set ($dateImportNames = "${dateImportNames}formatDateTime, ") | |
| 218 | - #end | |
| 219 | - #set ($dateImportNames = $dateImportNames.trim()) | |
| 220 | - #set ($dateImportNames = $dateImportNames.substring(0, $dateImportNames.length() - 1)) | |
| 537 | +#set ($dateImportNames = "") | |
| 538 | +#if ($hasDate == 1) | |
| 539 | +#set ($dateImportNames = "${dateImportNames}formatDate, ") | |
| 540 | +#end | |
| 541 | +#if ($hasDateTime == 1) | |
| 542 | +#set ($dateImportNames = "${dateImportNames}formatDateTime, ") | |
| 543 | +#end | |
| 544 | +#set ($dateImportNames = $dateImportNames.trim()) | |
| 545 | +#set ($dateImportNames = $dateImportNames.substring(0, $dateImportNames.length() - 1)) | |
| 221 | 546 | import { $dateImportNames } from '@/utils/date' |
| 222 | 547 | #end |
| 223 | 548 | import { createFormSchema } from '@/utils/wot' |
| 549 | +#if ($table.templateType == 2) | |
| 550 | +import { handleTree } from '@/utils/tree' | |
| 551 | +#end | |
| 224 | 552 | |
| 225 | 553 | const props = defineProps<{ |
| 226 | - id?: ${primaryTsType} | any | |
| 554 | + id?: ${primaryTsType} | |
| 555 | +#if ($table.templateType == 2) | |
| 556 | + ${treeParentColumn.javaField}?: ${primaryTsType} | |
| 557 | +#end | |
| 227 | 558 | }>() |
| 228 | 559 | |
| 229 | 560 | definePage({ |
| ... | ... | @@ -238,40 +569,153 @@ const getTitle = computed(() => props.id ? '编辑${table.classComment}' : 'æ–°å |
| 238 | 569 | const formLoading = ref(false) // è¡¨å•æäº¤çŠ¶æ€ |
| 239 | 570 | const formData = ref<${simpleClassName}>({ |
| 240 | 571 | #foreach($column in $columns) |
| 241 | - #if (($column.createOperation || $column.updateOperation) || $column.primaryKey) | |
| 242 | - #set ($javaType = $column.javaType.toLowerCase()) | |
| 243 | - #set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 244 | - #set ($optional = $column.nullable || $column.primaryKey || $javaFieldLower == "createtime" || $javaFieldLower == "updatetime") | |
| 245 | - #if ($column.primaryKey) | |
| 572 | +#if (($column.createOperation || $column.updateOperation) || $column.primaryKey) | |
| 573 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 574 | +#set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 575 | +#set ($optional = $column.nullable || $column.primaryKey || $javaFieldLower == "createtime" || $javaFieldLower == "updatetime") | |
| 576 | +#if ($column.primaryKey) | |
| 246 | 577 | ${column.javaField}: undefined, |
| 247 | - #elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") | |
| 578 | +#elseif($table.templateType == 2 && $column.id == $treeParentColumn.id) | |
| 579 | + ${column.javaField}: props.${column.javaField} ?? 0, | |
| 580 | +#elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte") | |
| 581 | +#if($column.nullable) | |
| 582 | + ${column.javaField}: undefined, | |
| 583 | +#else | |
| 248 | 584 | ${column.javaField}: 0, |
| 249 | - #elseif(${javaType} == "boolean") | |
| 585 | +#end | |
| 586 | +#elseif(${javaType} == "boolean") | |
| 250 | 587 | ${column.javaField}: false, |
| 251 | - #elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") | |
| 252 | - ${column.javaField}: undefined, | |
| 253 | - #else | |
| 588 | +#elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime") | |
| 589 | + ${column.javaField}:#if($column.nullable || $column.primaryKey || $javaFieldLower == "createtime" || $javaFieldLower == "updatetime") undefined#else 0#end, | |
| 590 | +#else | |
| 254 | 591 | ${column.javaField}: '', |
| 255 | - #end | |
| 256 | - #end | |
| 592 | +#end | |
| 593 | +#end | |
| 257 | 594 | #end |
| 258 | 595 | }) // è¡¨å•æ•°æ® |
| 596 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 597 | +#foreach ($subTable in $subTables) | |
| 598 | +#set ($index = $foreach.count - 1) | |
| 599 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 600 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 601 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 602 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 603 | +#if ($subTable.subJoinMany) | |
| 604 | +const ${subClassNameVar}s = ref<${subSimpleClassName}[]>([]) // ${subTable.classComment}æ•°æ® | |
| 605 | +#else | |
| 606 | +const ${subClassNameVar} = ref<${subSimpleClassName} | null>(null) // ${subTable.classComment}æ•°æ® | |
| 607 | +#end | |
| 608 | +#end | |
| 609 | +#end | |
| 259 | 610 | #foreach($column in $columns) |
| 260 | - #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")) | |
| 611 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")) | |
| 261 | 612 | const ${column.javaField}Visible = ref(false) // ${column.columnComment}é€‰æ‹©å™¨çŠ¶æ€ |
| 262 | - #end | |
| 613 | +#end | |
| 614 | +#end | |
| 615 | +#if ($hasSubDate == 1) | |
| 616 | +const subDateVisible = ref<Record<string, boolean>>({}) // åè¡¨æ—¥æœŸé€‰æ‹©å™¨çŠ¶æ€ | |
| 263 | 617 | #end |
| 264 | 618 | const formSchema = createFormSchema({ |
| 265 | 619 | #foreach($column in $columns) |
| 266 | - #set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 267 | - #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey | |
| 620 | +#set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 621 | +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey | |
| 268 | 622 | && $javaFieldLower != "createtime" && $javaFieldLower != "updatetime") |
| 269 | 623 | ${column.javaField}: [{ required: true, message: '${column.columnComment}ä¸èƒ½ä¸ºç©º' }], |
| 270 | - #end | |
| 624 | +#end | |
| 271 | 625 | #end |
| 272 | 626 | }) // è¡¨å•æ ¡éªŒè§„则 |
| 273 | 627 | const formRef = ref<FormInstance>() // 表å•组件引用 |
| 628 | +#if ($table.templateType == 2) | |
| 629 | +const treeData = ref<${simpleClassName}[]>([]) // çˆ¶çº§æ ‘å½¢é€‰é¡¹ | |
| 630 | +#end | |
| 631 | + | |
| 632 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 633 | +#foreach ($subTable in $subTables) | |
| 634 | +#set ($index = $foreach.count - 1) | |
| 635 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 636 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 637 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 638 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 639 | +#if ($subTable.subJoinMany) | |
| 640 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 641 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 642 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 643 | +/** æ·»åŠ ${subTable.classComment} */ | |
| 644 | +function add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}() { | |
| 645 | + ${subClassNameVar}s.value.push({ | |
| 646 | +#foreach ($column in $subColumns) | |
| 647 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) | |
| 648 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 649 | + ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte" || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 0#elseif($javaType == "boolean") false#else ''#end, | |
| 650 | +#end | |
| 651 | +#end | |
| 652 | + }) | |
| 653 | +} | |
| 274 | 654 | |
| 655 | +/** åˆ é™¤${subTable.classComment} */ | |
| 656 | +function remove${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}(index: number) { | |
| 657 | + ${subClassNameVar}s.value.splice(index, 1) | |
| 658 | +} | |
| 659 | + | |
| 660 | +#else | |
| 661 | +/** æ·»åŠ ${subTable.classComment} */ | |
| 662 | +function add${subClassNameVar.substring(0,1).toUpperCase()}${subClassNameVar.substring(1)}() { | |
| 663 | + ${subClassNameVar}.value = { | |
| 664 | +#foreach ($column in $subColumns) | |
| 665 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) | |
| 666 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 667 | + ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte" || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 0#elseif($javaType == "boolean") false#else ''#end, | |
| 668 | +#end | |
| 669 | +#end | |
| 670 | + } | |
| 671 | +} | |
| 672 | + | |
| 673 | +#end | |
| 674 | +#end | |
| 675 | +/** æ ¡éªŒä¸»åè¡¨æ•°æ® */ | |
| 676 | +function isEmptySubValue(value: unknown) { | |
| 677 | + return value === undefined || value === null || value === '' | |
| 678 | +} | |
| 679 | + | |
| 680 | +function validateSubTables() { | |
| 681 | +#foreach ($subTable in $subTables) | |
| 682 | +#set ($index = $foreach.count - 1) | |
| 683 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 684 | +#set ($subColumns = $subColumnsList.get($index)) | |
| 685 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 686 | +#if ($subTable.subJoinMany) | |
| 687 | + for (let index = 0; index < ${subClassNameVar}s.value.length; index++) { | |
| 688 | + const item = ${subClassNameVar}s.value[index]! | |
| 689 | +#foreach ($column in $subColumns) | |
| 690 | +#set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 691 | +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey | |
| 692 | + && $column.id != $subJoinColumn.id && $javaFieldLower != "createtime" && $javaFieldLower != "updatetime") | |
| 693 | + if (isEmptySubValue(item.${column.javaField})) { | |
| 694 | + toast.warning(`${subTable.classComment}第#[[${]]#index + 1#[[}]]#项的${column.columnComment}ä¸èƒ½ä¸ºç©º`) | |
| 695 | + return false | |
| 696 | + } | |
| 697 | +#end | |
| 698 | +#end | |
| 699 | + } | |
| 700 | +#else | |
| 701 | + if (${subClassNameVar}.value) { | |
| 702 | +#foreach ($column in $subColumns) | |
| 703 | +#set ($javaFieldLower = $column.javaField.toLowerCase()) | |
| 704 | +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey | |
| 705 | + && $column.id != $subJoinColumn.id && $javaFieldLower != "createtime" && $javaFieldLower != "updatetime") | |
| 706 | + if (isEmptySubValue(${subClassNameVar}.value.${column.javaField})) { | |
| 707 | + toast.warning('${subTable.classComment}çš„${column.columnComment}ä¸èƒ½ä¸ºç©º') | |
| 708 | + return false | |
| 709 | + } | |
| 710 | +#end | |
| 711 | +#end | |
| 712 | + } | |
| 713 | +#end | |
| 714 | +#end | |
| 715 | + return true | |
| 716 | +} | |
| 717 | + | |
| 718 | +#end | |
| 275 | 719 | /** 返回上一页 */ |
| 276 | 720 | function handleBack() { |
| 277 | 721 | navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index') |
| ... | ... | @@ -282,7 +726,34 @@ async function getDetail() { |
| 282 | 726 | if (!props.id) { |
| 283 | 727 | return |
| 284 | 728 | } |
| 729 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 730 | + const [data#foreach ($subTable in $subTables)#set ($index = $foreach.count - 1), ${subClassNameVars.get($index)}Data#end] = await Promise.all([ | |
| 731 | + get${simpleClassName}(props.id), | |
| 732 | +#foreach ($subTable in $subTables) | |
| 733 | +#set ($index = $foreach.count - 1) | |
| 734 | +#set ($subSimpleClassName = $subSimpleClassNames.get($index)) | |
| 735 | +#set ($subJoinColumn = $subJoinColumns.get($index)) | |
| 736 | +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + $subJoinColumn.javaField.substring(1)) | |
| 737 | +#if ($subTable.subJoinMany) | |
| 738 | + get${subSimpleClassName}ListBy${SubJoinColumnName}(props.id), | |
| 739 | +#else | |
| 740 | + get${subSimpleClassName}By${SubJoinColumnName}(props.id), | |
| 741 | +#end | |
| 742 | +#end | |
| 743 | + ]) | |
| 744 | + formData.value = data | |
| 745 | +#foreach ($subTable in $subTables) | |
| 746 | +#set ($index = $foreach.count - 1) | |
| 747 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 748 | +#if ($subTable.subJoinMany) | |
| 749 | + ${subClassNameVar}s.value = ${subClassNameVar}Data || [] | |
| 750 | +#else | |
| 751 | + ${subClassNameVar}.value = ${subClassNameVar}Data || null | |
| 752 | +#end | |
| 753 | +#end | |
| 754 | +#else | |
| 285 | 755 | formData.value = await get${simpleClassName}(props.id) |
| 756 | +#end | |
| 286 | 757 | } |
| 287 | 758 | |
| 288 | 759 | /** æäº¤è¡¨å• */ |
| ... | ... | @@ -291,14 +762,31 @@ async function handleSubmit() { |
| 291 | 762 | if (!valid) { |
| 292 | 763 | return |
| 293 | 764 | } |
| 765 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 766 | + if (!validateSubTables()) { | |
| 767 | + return | |
| 768 | + } | |
| 769 | +#end | |
| 294 | 770 | |
| 295 | 771 | formLoading.value = true |
| 296 | 772 | try { |
| 773 | +#if ($table.templateType == 10 || $table.templateType == 12) | |
| 774 | + const data: ${simpleClassName} = { | |
| 775 | + ...formData.value, | |
| 776 | +#foreach ($subTable in $subTables) | |
| 777 | +#set ($index = $foreach.count - 1) | |
| 778 | +#set ($subClassNameVar = $subClassNameVars.get($index)) | |
| 779 | + ${subClassNameVar}#if($subTable.subJoinMany)s#end: ${subClassNameVar}#if($subTable.subJoinMany)s#end.value, | |
| 780 | +#end | |
| 781 | + } | |
| 782 | +#else | |
| 783 | + const data = formData.value | |
| 784 | +#end | |
| 297 | 785 | if (props.id) { |
| 298 | - await update${simpleClassName}(formData.value) | |
| 786 | + await update${simpleClassName}(data) | |
| 299 | 787 | toast.success('修改æˆåŠŸ') |
| 300 | 788 | } else { |
| 301 | - await create${simpleClassName}(formData.value) | |
| 789 | + await create${simpleClassName}(data) | |
| 302 | 790 | toast.success('新增æˆåŠŸ') |
| 303 | 791 | } |
| 304 | 792 | uni.$emit('${table.moduleName}:${table.businessName}:reload') |
| ... | ... | @@ -309,7 +797,16 @@ async function handleSubmit() { |
| 309 | 797 | } |
| 310 | 798 | |
| 311 | 799 | /** åˆå§‹åŒ– */ |
| 312 | -onMounted(() => { | |
| 800 | +onMounted(async () => { | |
| 801 | +#if ($table.templateType == 2) | |
| 802 | + const data = await get${simpleClassName}List() | |
| 803 | + treeData.value = [{ | |
| 804 | + ${primaryColumn.javaField}: 0, | |
| 805 | + ${treeNameColumn.javaField}: '顶级${table.classComment}', | |
| 806 | + ${treeParentColumn.javaField}: 0, | |
| 807 | + children: handleTree(data, '${primaryColumn.javaField}', '${treeParentColumn.javaField}'), | |
| 808 | + } as ${simpleClassName}] | |
| 809 | +#end | |
| 313 | 810 | getDetail() |
| 314 | 811 | }) |
| 315 | 812 | </script> | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/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 ($subPrimaryColumn = $subPrimaryColumns.get($subIndex)) | |
| 5 | +#set ($subJoinColumn = $subJoinColumns.get($subIndex)) | |
| 6 | +#set ($subPrimaryJavaType = $subPrimaryColumn.javaType.toLowerCase()) | |
| 7 | +#if(${subPrimaryJavaType} == "long" || ${subPrimaryJavaType} == "integer" || ${subPrimaryJavaType} == "short" || ${subPrimaryJavaType} == "double" || ${subPrimaryJavaType} == "bigdecimal" || ${subPrimaryJavaType} == "byte") | |
| 8 | +#set ($subPrimaryTsType = "number") | |
| 9 | +#else | |
| 10 | +#set ($subPrimaryTsType = "string") | |
| 11 | +#end | |
| 12 | +#set ($subJoinJavaType = $subJoinColumn.javaType.toLowerCase()) | |
| 13 | +#if(${subJoinJavaType} == "long" || ${subJoinJavaType} == "integer" || ${subJoinJavaType} == "short" || ${subJoinJavaType} == "double" || ${subJoinJavaType} == "bigdecimal" || ${subJoinJavaType} == "byte") | |
| 14 | +#set ($subJoinTsType = "number") | |
| 15 | +#else | |
| 16 | +#set ($subJoinTsType = "string") | |
| 17 | +#end | |
| 18 | +#set ($hasDict = 0) | |
| 19 | +#set ($hasDate = 0) | |
| 20 | +#set ($hasDateTime = 0) | |
| 21 | +#foreach ($column in $subColumns) | |
| 22 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) | |
| 23 | +#if (($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType) | |
| 24 | +#set ($hasDict = 1) | |
| 25 | +#end | |
| 26 | +#if (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate") | |
| 27 | +#set ($hasDate = 1) | |
| 28 | +#end | |
| 29 | +#if (${column.javaType.toLowerCase()} == "localdatetime") | |
| 30 | +#set ($hasDateTime = 1) | |
| 31 | +#end | |
| 32 | +#end | |
| 33 | +#end | |
| 34 | +<template> | |
| 35 | + <view class="yd-page-container"> | |
| 36 | + <!-- 顶部导航栏 --> | |
| 37 | + <wd-navbar :title="getTitle" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" /> | |
| 38 | + | |
| 39 | + <!-- 表单区域 --> | |
| 40 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 41 | + <wd-cell-group border> | |
| 42 | +#foreach ($column in $subColumns) | |
| 43 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id) | |
| 44 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 45 | +#if (($column.htmlType == "select" || $column.htmlType == "radio") && $column.dictType && "" != $column.dictType) | |
| 46 | + <yd-form-picker | |
| 47 | + v-model="formData.${column.javaField}" | |
| 48 | + label="${column.columnComment}" | |
| 49 | + label-width="180rpx" | |
| 50 | + prop="${column.javaField}" | |
| 51 | + :dict-type="DICT_TYPE.${column.dictType.toUpperCase()}" | |
| 52 | +#if ($column.javaType == "String") | |
| 53 | + dict-kind="str" | |
| 54 | +#end | |
| 55 | + placeholder="请选择${column.columnComment}" | |
| 56 | + /> | |
| 57 | +#elseif ($javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") | |
| 58 | +#set ($pickerType = "date") | |
| 59 | +#if ($javaType == "localdatetime") | |
| 60 | +#set ($pickerType = "datetime") | |
| 61 | +#end | |
| 62 | + <wd-form-item | |
| 63 | + title="${column.columnComment}" | |
| 64 | + title-width="180rpx" | |
| 65 | + prop="${column.javaField}" | |
| 66 | + is-link | |
| 67 | +#if ($javaType == "localdatetime") | |
| 68 | + :value="formData.${column.javaField} ? formatDateTime(formData.${column.javaField}) : ''" | |
| 69 | +#else | |
| 70 | + :value="formData.${column.javaField} ? formatDate(formData.${column.javaField}) : ''" | |
| 71 | +#end | |
| 72 | + placeholder="请选择${column.columnComment}" | |
| 73 | + @click="${column.javaField}Visible = true" | |
| 74 | + /> | |
| 75 | + <wd-datetime-picker | |
| 76 | + v-model:visible="${column.javaField}Visible" | |
| 77 | + :model-value="formData.${column.javaField} || Date.now()" | |
| 78 | + type="${pickerType}" | |
| 79 | +#if ($javaType == "localdatetime") | |
| 80 | + @update:model-value="value => formData.${column.javaField} = value" | |
| 81 | +#else | |
| 82 | + @update:model-value="value => formData.${column.javaField} = value" | |
| 83 | +#end | |
| 84 | + /> | |
| 85 | +#else | |
| 86 | + <wd-form-item title="${column.columnComment}" title-width="180rpx" prop="${column.javaField}"> | |
| 87 | +#if ($javaType == "boolean") | |
| 88 | + <wd-switch v-model="formData.${column.javaField}" /> | |
| 89 | +#elseif ($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte") | |
| 90 | + <wd-input-number | |
| 91 | + v-model="formData.${column.javaField}" | |
| 92 | +#if($column.nullable) | |
| 93 | + allow-null | |
| 94 | +#end | |
| 95 | +#if ($javaType == "double" || $javaType == "bigdecimal") | |
| 96 | + :precision="2" | |
| 97 | + input-type="number" | |
| 98 | +#end | |
| 99 | + :min="0" | |
| 100 | + /> | |
| 101 | +#elseif ($column.htmlType == "textarea") | |
| 102 | + <wd-textarea v-model="formData.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> | |
| 103 | +#elseif ($column.htmlType == "imageUpload") | |
| 104 | + <yd-upload-img v-model="formData.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> | |
| 105 | +#elseif ($column.htmlType == "fileUpload") | |
| 106 | + <yd-upload-file v-model="formData.${column.javaField}" directory="${table.moduleName}/${table.businessName}" /> | |
| 107 | +#else | |
| 108 | + <wd-input v-model="formData.${column.javaField}" clearable placeholder="请输入${column.columnComment}" /> | |
| 109 | +#end | |
| 110 | + </wd-form-item> | |
| 111 | +#end | |
| 112 | +#end | |
| 113 | +#end | |
| 114 | + </wd-cell-group> | |
| 115 | + </wd-form> | |
| 116 | + | |
| 117 | + <!-- 底部保存按钮 --> | |
| 118 | + <view class="yd-detail-footer"> | |
| 119 | + <wd-button type="primary" block :loading="formLoading" @click="handleSubmit"> | |
| 120 | + 保存 | |
| 121 | + </wd-button> | |
| 122 | + </view> | |
| 123 | + </view> | |
| 124 | +</template> | |
| 125 | + | |
| 126 | +<script lang="ts" setup> | |
| 127 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 128 | +import type { ${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' | |
| 129 | +import { computed, onMounted, ref } from 'vue' | |
| 130 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 131 | +import { create${subSimpleClassName}, get${subSimpleClassName}, update${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' | |
| 132 | +import { delay, navigateBackPlus } from '@/utils' | |
| 133 | +#if ($hasDict == 1) | |
| 134 | +import { DICT_TYPE } from '@/utils/constants' | |
| 135 | +#end | |
| 136 | +#if ($hasDate == 1 || $hasDateTime == 1) | |
| 137 | +#set ($dateImportNames = "") | |
| 138 | +#if ($hasDate == 1) | |
| 139 | +#set ($dateImportNames = "${dateImportNames}formatDate, ") | |
| 140 | +#end | |
| 141 | +#if ($hasDateTime == 1) | |
| 142 | +#set ($dateImportNames = "${dateImportNames}formatDateTime, ") | |
| 143 | +#end | |
| 144 | +#set ($dateImportNames = $dateImportNames.trim()) | |
| 145 | +#set ($dateImportNames = $dateImportNames.substring(0, $dateImportNames.length() - 1)) | |
| 146 | +import { $dateImportNames } from '@/utils/date' | |
| 147 | +#end | |
| 148 | +import { createFormSchema } from '@/utils/wot' | |
| 149 | + | |
| 150 | +const props = defineProps<{ | |
| 151 | + id?: ${subPrimaryTsType} | |
| 152 | + ${subJoinColumn.javaField}: ${subJoinTsType} | |
| 153 | +}>() | |
| 154 | + | |
| 155 | +definePage({ | |
| 156 | + style: { navigationBarTitleText: '', navigationStyle: 'custom' }, | |
| 157 | +}) | |
| 158 | + | |
| 159 | +const toast = useToast() | |
| 160 | +const getTitle = computed(() => props.id ? '编辑${subTable.classComment}' : '新增${subTable.classComment}') | |
| 161 | +const formLoading = ref(false) // 表单提交状态 | |
| 162 | +const formData = ref<${subSimpleClassName}>({ | |
| 163 | +#foreach ($column in $subColumns) | |
| 164 | +#if ($column.primaryKey) | |
| 165 | + ${column.javaField}: undefined, | |
| 166 | +#elseif ($column.id == $subJoinColumn.id) | |
| 167 | + ${column.javaField}: props.${column.javaField}, | |
| 168 | +#elseif ($column.createOperation || $column.updateOperation) | |
| 169 | +#set ($javaType = $column.javaType.toLowerCase()) | |
| 170 | + ${column.javaField}:#if($column.nullable) undefined#elseif($javaType == "long" || $javaType == "integer" || $javaType == "short" || $javaType == "double" || $javaType == "bigdecimal" || $javaType == "byte" || $javaType == "date" || $javaType == "localdate" || $javaType == "localdatetime") 0#elseif($javaType == "boolean") false#else ''#end, | |
| 171 | +#end | |
| 172 | +#end | |
| 173 | +}) // 表单数据 | |
| 174 | +#foreach ($column in $subColumns) | |
| 175 | +#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && $column.id != $subJoinColumn.id | |
| 176 | + && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime")) | |
| 177 | +const ${column.javaField}Visible = ref(false) // ${column.columnComment}选择器状态 | |
| 178 | +#end | |
| 179 | +#end | |
| 180 | +const formSchema = createFormSchema({ | |
| 181 | +#foreach ($column in $subColumns) | |
| 182 | +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey) | |
| 183 | + ${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }], | |
| 184 | +#end | |
| 185 | +#end | |
| 186 | +}) // 表单校验规则 | |
| 187 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 188 | + | |
| 189 | +/** 返回上一页 */ | |
| 190 | +function handleBack() { | |
| 191 | + navigateBackPlus() | |
| 192 | +} | |
| 193 | + | |
| 194 | +/** 加载${subTable.classComment}详情 */ | |
| 195 | +async function getDetail() { | |
| 196 | + if (props.id) { | |
| 197 | + formData.value = await get${subSimpleClassName}(props.id) | |
| 198 | + } | |
| 199 | +} | |
| 200 | + | |
| 201 | +/** 提交表单 */ | |
| 202 | +async function handleSubmit() { | |
| 203 | + const { valid } = await formRef.value.validate() | |
| 204 | + if (!valid) { | |
| 205 | + return | |
| 206 | + } | |
| 207 | + formLoading.value = true | |
| 208 | + try { | |
| 209 | + const data = { ...formData.value, ${subJoinColumn.javaField}: props.${subJoinColumn.javaField} ?? formData.value.${subJoinColumn.javaField} } | |
| 210 | + if (props.id) { | |
| 211 | + await update${subSimpleClassName}(data) | |
| 212 | + toast.success('修改成功') | |
| 213 | + } else { | |
| 214 | + await create${subSimpleClassName}(data) | |
| 215 | + toast.success('新增成功') | |
| 216 | + } | |
| 217 | + delay(handleBack) | |
| 218 | + } finally { | |
| 219 | + formLoading.value = false | |
| 220 | + } | |
| 221 | +} | |
| 222 | + | |
| 223 | +/** 初始化 */ | |
| 224 | +onMounted(() => { | |
| 225 | + getDetail() | |
| 226 | +}) | |
| 227 | +</script> | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/index_tree.vue.vm
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="${table.classComment}管理" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 搜索组件 --> | |
| 11 | + <SearchForm @search="handleQuery" @reset="handleReset" /> | |
| 12 | + | |
| 13 | + <!-- 面包屑导航 --> | |
| 14 | + <Breadcrumb ref="breadcrumbRef" v-model="currentParentId" /> | |
| 15 | + | |
| 16 | + <!-- ${table.classComment}列表 --> | |
| 17 | + <view class="p-24rpx"> | |
| 18 | + <view | |
| 19 | + v-for="item in currentList" | |
| 20 | + :key="item.${primaryColumn.javaField}" | |
| 21 | + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 22 | + > | |
| 23 | + <view class="p-24rpx" @click="handleDetail(item)"> | |
| 24 | + <view class="flex items-center justify-between"> | |
| 25 | + <view class="flex min-w-0 items-center"> | |
| 26 | + <view class="mr-16rpx h-48rpx w-48rpx shrink-0 flex items-center justify-center rounded-8rpx bg-[#1890ff]"> | |
| 27 | + <wd-icon name="folder" size="20px" color="#fff" /> | |
| 28 | + </view> | |
| 29 | + <view class="truncate text-32rpx text-[#333] font-semibold"> | |
| 30 | + {{ item.${treeNameColumn.javaField} }} | |
| 31 | + </view> | |
| 32 | + </view> | |
| 33 | + <view | |
| 34 | + v-if="item.children?.length" | |
| 35 | + class="ml-16rpx shrink-0 flex items-center" | |
| 36 | + @click.stop="handleEnterChildren(item)" | |
| 37 | + > | |
| 38 | + <text class="text-24rpx text-[#1890ff]">子级 ({{ item.children.length }})</text> | |
| 39 | + <wd-icon name="arrow-right" size="12px" color="#1890ff" /> | |
| 40 | + </view> | |
| 41 | + </view> | |
| 42 | + </view> | |
| 43 | + </view> | |
| 44 | + | |
| 45 | + <!-- 空状态 --> | |
| 46 | + <view v-if="!loading && currentList.length === 0" class="py-100rpx text-center"> | |
| 47 | + <wd-empty icon="content" tip="暂无${table.classComment}数据" /> | |
| 48 | + </view> | |
| 49 | + </view> | |
| 50 | + | |
| 51 | + <!-- 新增按钮 --> | |
| 52 | + <wd-fab | |
| 53 | + v-if="hasAccessByCodes(['${permissionPrefix}:create'])" | |
| 54 | + position="right-bottom" | |
| 55 | + type="primary" | |
| 56 | + :expandable="false" | |
| 57 | + @click="handleAdd" | |
| 58 | + /> | |
| 59 | + </view> | |
| 60 | +</template> | |
| 61 | + | |
| 62 | +<script lang="ts" setup> | |
| 63 | +#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase()) | |
| 64 | +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte") | |
| 65 | +#set ($primaryTsType = "number") | |
| 66 | +#else | |
| 67 | +#set ($primaryTsType = "string") | |
| 68 | +#end | |
| 69 | +import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' | |
| 70 | +import { onUnload } from '@dcloudio/uni-app' | |
| 71 | +import { computed, onMounted, ref } from 'vue' | |
| 72 | +import { get${simpleClassName}List } from '@/api/${table.moduleName}/${table.businessName}' | |
| 73 | +import { useAccess } from '@/hooks/useAccess' | |
| 74 | +import { navigateBackPlus } from '@/utils' | |
| 75 | +import { findChildren, handleTree } from '@/utils/tree' | |
| 76 | +import Breadcrumb from './components/breadcrumb.vue' | |
| 77 | +import SearchForm from './components/search-form.vue' | |
| 78 | + | |
| 79 | +definePage({ | |
| 80 | + style: { | |
| 81 | + navigationBarTitleText: '', | |
| 82 | + navigationStyle: 'custom', | |
| 83 | + }, | |
| 84 | +}) | |
| 85 | + | |
| 86 | +const { hasAccessByCodes } = useAccess() | |
| 87 | +const loading = ref(false) // 列表加载状态 | |
| 88 | +const list = ref<${simpleClassName}[]>([]) // 完整树形列表 | |
| 89 | +const currentParentId = ref<${primaryTsType}>(0) // 当前层级的父节点编号 | |
| 90 | +const currentList = computed(() => currentParentId.value === 0 | |
| 91 | + ? list.value | |
| 92 | + : findChildren(list.value, currentParentId.value, '${primaryColumn.javaField}')) // 当前层级列表 | |
| 93 | +const breadcrumbRef = ref<InstanceType<typeof Breadcrumb>>() // 面包屑组件引用 | |
| 94 | +const queryParams = ref<Record<string, any>>({}) // 查询参数 | |
| 95 | + | |
| 96 | +/** 返回上一页或上一层级 */ | |
| 97 | +function handleBack() { | |
| 98 | + if (!breadcrumbRef.value?.back()) { | |
| 99 | + navigateBackPlus() | |
| 100 | + } | |
| 101 | +} | |
| 102 | + | |
| 103 | +/** 进入子层级 */ | |
| 104 | +function handleEnterChildren(item: ${simpleClassName}) { | |
| 105 | + breadcrumbRef.value?.enter({ id: item.${primaryColumn.javaField}!, name: item.${treeNameColumn.javaField} }) | |
| 106 | +} | |
| 107 | + | |
| 108 | +/** 查询${table.classComment}列表 */ | |
| 109 | +async function getList() { | |
| 110 | + loading.value = true | |
| 111 | + try { | |
| 112 | + list.value = handleTree(await get${simpleClassName}List(queryParams.value), '${primaryColumn.javaField}', '${treeParentColumn.javaField}') | |
| 113 | + } finally { | |
| 114 | + loading.value = false | |
| 115 | + } | |
| 116 | +} | |
| 117 | + | |
| 118 | +/** 搜索按钮操作 */ | |
| 119 | +function handleQuery(data?: Record<string, any>) { | |
| 120 | + queryParams.value = { ...data } | |
| 121 | + currentParentId.value = 0 | |
| 122 | + breadcrumbRef.value?.reset() | |
| 123 | + getList() | |
| 124 | +} | |
| 125 | + | |
| 126 | +/** 重置按钮操作 */ | |
| 127 | +function handleReset() { | |
| 128 | + handleQuery() | |
| 129 | +} | |
| 130 | + | |
| 131 | +/** 新增${table.classComment} */ | |
| 132 | +function handleAdd() { | |
| 133 | + uni.navigateTo({ url: `/pages-${table.moduleName}/${table.businessName}/form/index?${treeParentColumn.javaField}=#[[${]]#currentParentId.value#[[}]]#` }) | |
| 134 | +} | |
| 135 | + | |
| 136 | +/** 查看详情 */ | |
| 137 | +function handleDetail(item: ${simpleClassName}) { | |
| 138 | + uni.navigateTo({ url: `/pages-${table.moduleName}/${table.businessName}/detail/index?id=#[[${]]#item.${primaryColumn.javaField}#[[}]]#` }) | |
| 139 | +} | |
| 140 | + | |
| 141 | +/** 初始化 */ | |
| 142 | +onMounted(() => { | |
| 143 | + getList() | |
| 144 | + uni.$on('${table.moduleName}:${table.businessName}:reload', getList) | |
| 145 | +}) | |
| 146 | + | |
| 147 | +/** 卸载 */ | |
| 148 | +onUnload(() => { | |
| 149 | + uni.$off('${table.moduleName}:${table.businessName}:reload', getList) | |
| 150 | +}) | |
| 151 | +</script> | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java
| ... | ... | @@ -157,13 +157,17 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { |
| 157 | 157 | String lastFilePath = StrUtil.subAfter(filePath, '/', true); |
| 158 | 158 | String ext = StrUtil.subAfter(lastFilePath, '.', true); |
| 159 | 159 | String name = StrUtil.subBefore(lastFilePath, '.', true); |
| 160 | - String contentPath = ext + '/' + name; | |
| 161 | - // 同名文件(如 index.vue 同时出现在 列表/form/detail)会撞名,撞名时前缀补上级目录区分;仍撞则加序号兜底,避免快照互相覆盖 | |
| 160 | + String parentPath = StrUtil.subBefore(filePath, '/' + lastFilePath, true); | |
| 161 | + String parentDir = StrUtil.subAfter(parentPath, '/', true); | |
| 162 | + // index.vue 按页面语义稳定归档,避免结果迭代顺序导致列表页和表单页的快照路径互换 | |
| 163 | + String contentPath = "index".equals(name) && ("form".equals(parentDir) || "detail".equals(parentDir)) | |
| 164 | + ? ext + '/' + parentDir + '/' + name : ext + '/' + name; | |
| 165 | + // ERP 多个子表的 form/index.vue 仍可能撞名,继续补充祖父目录区分 | |
| 162 | 166 | if (usedContentPaths.contains(contentPath)) { |
| 163 | - String parentDir = StrUtil.subAfter(StrUtil.subBefore(filePath, '/' + lastFilePath, true), '/', true); | |
| 164 | - contentPath = ext + '/' + parentDir + '/' + name; | |
| 167 | + String grandParentDir = StrUtil.subAfter(StrUtil.subBefore(parentPath, '/' + parentDir, true), '/', true); | |
| 168 | + contentPath = ext + '/' + grandParentDir + '/' + parentDir + '/' + name; | |
| 165 | 169 | for (int i = 2; usedContentPaths.contains(contentPath); i++) { |
| 166 | - contentPath = ext + '/' + parentDir + '/' + name + '-' + i; | |
| 170 | + contentPath = ext + '/' + grandParentDir + '/' + parentDir + '/' + name + '-' + i; | |
| 167 | 171 | } |
| 168 | 172 | } |
| 169 | 173 | usedContentPaths.add(contentPath); | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineUniappTest.java
| ... | ... | @@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; |
| 7 | 7 | import com.baomidou.mybatisplus.annotation.DbType; |
| 8 | 8 | import org.junit.jupiter.api.Test; |
| 9 | 9 | |
| 10 | +import java.util.Arrays; | |
| 10 | 11 | import java.util.List; |
| 11 | 12 | import java.util.Map; |
| 13 | +import java.util.Objects; | |
| 12 | 14 | |
| 13 | 15 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| 16 | +import static org.junit.jupiter.api.Assertions.assertThrows; | |
| 14 | 17 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 15 | 18 | |
| 16 | 19 | /** |
| ... | ... | @@ -59,4 +62,182 @@ public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { |
| 59 | 62 | assertTrue(searchForm.contains("sex: undefined as string | undefined,")); |
| 60 | 63 | } |
| 61 | 64 | |
| 65 | + @Test | |
| 66 | + public void testExecute_tree() { | |
| 67 | + // 准备参数 | |
| 68 | + CodegenTableDO table = getTable("category") | |
| 69 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 70 | + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); | |
| 71 | + List<CodegenColumnDO> columns = getColumnList("category"); | |
| 72 | + | |
| 73 | + // 调用 | |
| 74 | + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null); | |
| 75 | + // 断言 | |
| 76 | + assertResult(result, "/vue3_admin_uniapp_tree"); | |
| 77 | + } | |
| 78 | + | |
| 79 | + @Test | |
| 80 | + public void testExecute_semantics() { | |
| 81 | + // 准备参数 | |
| 82 | + CodegenTableDO table = getTable("student") | |
| 83 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 84 | + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); | |
| 85 | + List<CodegenColumnDO> columns = getColumnList("student"); | |
| 86 | + | |
| 87 | + // 调用 | |
| 88 | + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null); | |
| 89 | + // 断言 | |
| 90 | + String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"); | |
| 91 | + assertTrue(api.contains("birthday: number")); | |
| 92 | + assertTrue(api.contains("createTime?: number")); | |
| 93 | + assertFalse(api.contains("createTime?: Date")); | |
| 94 | + String detail = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue"); | |
| 95 | + assertTrue(detail.contains("type=\"danger\"")); | |
| 96 | + assertFalse(detail.contains("type=\"error\"")); | |
| 97 | + } | |
| 98 | + | |
| 99 | + @Test | |
| 100 | + public void testExecute_stringPrimaryKey() { | |
| 101 | + // 准备主表 | |
| 102 | + CodegenTableDO table = getTable("student") | |
| 103 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 104 | + .setTemplateType(CodegenTemplateTypeEnum.MASTER_ERP.getType()); | |
| 105 | + List<CodegenColumnDO> columns = getColumnList("student"); | |
| 106 | + columns.stream().filter(CodegenColumnDO::getPrimaryKey).findFirst().orElseThrow().setJavaType("String"); | |
| 107 | + // 准备子表 | |
| 108 | + CodegenTableDO contactTable = getTable("contact") | |
| 109 | + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) | |
| 110 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 111 | + .setSubJoinColumnId(100L).setSubJoinMany(true); | |
| 112 | + List<CodegenColumnDO> contactColumns = getColumnList("contact"); | |
| 113 | + contactColumns.stream().filter(CodegenColumnDO::getPrimaryKey).findFirst().orElseThrow().setJavaType("String"); | |
| 114 | + contactColumns.stream().filter(column -> Objects.equals(column.getId(), 100L)) | |
| 115 | + .findFirst().orElseThrow().setJavaType("String"); | |
| 116 | + | |
| 117 | + // 调用 | |
| 118 | + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, | |
| 119 | + List.of(contactTable), List.of(contactColumns)); | |
| 120 | + // 断言 | |
| 121 | + String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"); | |
| 122 | + assertTrue(api.contains("return http.post<string>('/infra/student/create', data)")); | |
| 123 | + assertTrue(api.contains("studentId: string")); | |
| 124 | + assertTrue(api.contains("getStudentContact(id: string)")); | |
| 125 | + assertTrue(api.contains("deleteStudentContact(id: string)")); | |
| 126 | + String detail = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue"); | |
| 127 | + assertFalse(detail.contains("Number(props.id)")); | |
| 128 | + String subForm = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/student-contact/form/index.vue"); | |
| 129 | + assertTrue(subForm.contains("id?: string")); | |
| 130 | + assertTrue(subForm.contains("studentId: string")); | |
| 131 | + } | |
| 132 | + | |
| 133 | + @Test | |
| 134 | + public void testExecute_treeSearch() { | |
| 135 | + // 准备参数 | |
| 136 | + CodegenTableDO table = getTable("category") | |
| 137 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 138 | + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); | |
| 139 | + List<CodegenColumnDO> columns = getColumnList("category"); | |
| 140 | + | |
| 141 | + // 调用 | |
| 142 | + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null); | |
| 143 | + // 断言 | |
| 144 | + String index = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue"); | |
| 145 | + assertTrue(index.contains("currentParentId.value === 0\n ? list.value")); | |
| 146 | + assertFalse(index.contains("list.value.filter")); | |
| 147 | + String breadcrumb = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/components/breadcrumb.vue"); | |
| 148 | + assertTrue(breadcrumb.contains("modelValue: number")); | |
| 149 | + assertFalse(breadcrumb.contains("TreeValue")); | |
| 150 | + } | |
| 151 | + | |
| 152 | + @Test | |
| 153 | + public void testExecute_duplicateSubPath() { | |
| 154 | + // 准备主表 | |
| 155 | + CodegenTableDO table = getTable("student") | |
| 156 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 157 | + .setTemplateType(CodegenTemplateTypeEnum.MASTER_ERP.getType()); | |
| 158 | + List<CodegenColumnDO> columns = getColumnList("student"); | |
| 159 | + // 准备两个归一化名称相同的子表 | |
| 160 | + CodegenTableDO crmItemTable = getTable("contact").setModuleName("crm").setClassName("CrmOrderItem") | |
| 161 | + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) | |
| 162 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 163 | + .setSubJoinColumnId(100L).setSubJoinMany(true); | |
| 164 | + CodegenTableDO erpItemTable = getTable("contact").setModuleName("erp").setClassName("ErpOrderItem") | |
| 165 | + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) | |
| 166 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 167 | + .setSubJoinColumnId(100L).setSubJoinMany(true); | |
| 168 | + List<CodegenColumnDO> crmItemColumns = getColumnList("contact"); | |
| 169 | + List<CodegenColumnDO> erpItemColumns = getColumnList("contact"); | |
| 170 | + | |
| 171 | + // 调用 | |
| 172 | + IllegalStateException exception = assertThrows(IllegalStateException.class, | |
| 173 | + () -> codegenEngine.execute(DbType.MYSQL, table, columns, | |
| 174 | + Arrays.asList(crmItemTable, erpItemTable), Arrays.asList(crmItemColumns, erpItemColumns))); | |
| 175 | + // 断言 | |
| 176 | + assertTrue(exception.getMessage().contains("生成文件路径重复")); | |
| 177 | + assertTrue(exception.getMessage().contains("subIndex=0")); | |
| 178 | + assertTrue(exception.getMessage().contains("subIndex=1")); | |
| 179 | + } | |
| 180 | + | |
| 181 | + @Test | |
| 182 | + public void testExecute_masterNormal() { | |
| 183 | + testExecute_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue3_admin_uniapp_master_normal"); | |
| 184 | + } | |
| 185 | + | |
| 186 | + @Test | |
| 187 | + public void testExecute_masterInner() { | |
| 188 | + testExecute_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue3_admin_uniapp_master_inner"); | |
| 189 | + } | |
| 190 | + | |
| 191 | + @Test | |
| 192 | + public void testExecute_masterErp() { | |
| 193 | + testExecute_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue3_admin_uniapp_master_erp"); | |
| 194 | + } | |
| 195 | + | |
| 196 | + private void testExecute_master(CodegenTemplateTypeEnum templateType, String path) { | |
| 197 | + // 准备主表 | |
| 198 | + CodegenTableDO table = getTable("student") | |
| 199 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 200 | + .setTemplateType(templateType.getType()); | |
| 201 | + List<CodegenColumnDO> columns = getColumnList("student"); | |
| 202 | + // 准备一对多子表 | |
| 203 | + CodegenTableDO contactTable = getTable("contact") | |
| 204 | + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) | |
| 205 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 206 | + .setSubJoinColumnId(100L).setSubJoinMany(true); | |
| 207 | + List<CodegenColumnDO> contactColumns = getColumnList("contact"); | |
| 208 | + // 准备一对一子表 | |
| 209 | + CodegenTableDO teacherTable = getTable("teacher") | |
| 210 | + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) | |
| 211 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 212 | + .setSubJoinColumnId(200L).setSubJoinMany(false); | |
| 213 | + List<CodegenColumnDO> teacherColumns = getColumnList("teacher"); | |
| 214 | + | |
| 215 | + // 调用 | |
| 216 | + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, | |
| 217 | + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); | |
| 218 | + // 断言 | |
| 219 | + assertResult(result, path); | |
| 220 | + if (templateType != CodegenTemplateTypeEnum.MASTER_ERP) { | |
| 221 | + String api = result.get("yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"); | |
| 222 | + assertTrue(api.contains("studentTeacher?: StudentTeacher | null")); | |
| 223 | + String form = result.get("yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue"); | |
| 224 | + assertTrue(form.contains("ref<StudentTeacher | null>(null)")); | |
| 225 | + assertTrue(form.contains("studentTeacher.value = studentTeacherData || null")); | |
| 226 | + assertTrue(form.contains(":model-value=\"item.birthday || Date.now()\"")); | |
| 227 | + assertTrue(form.contains("v-model=\"item.sex\"")); | |
| 228 | + assertTrue(form.contains("<yd-upload-img v-model=\"item.avatar\"")); | |
| 229 | + assertTrue(form.contains("<yd-upload-file v-model=\"item.video\"")); | |
| 230 | + assertTrue(form.contains("<wd-textarea v-model=\"item.description\"")); | |
| 231 | + assertTrue(form.contains("function validateSubTables()")); | |
| 232 | + } else { | |
| 233 | + String contactForm = result.get( | |
| 234 | + "yudao-ui-admin-uniapp/src/pages-infra/demo/student-contact/form/index.vue"); | |
| 235 | + assertTrue(contactForm.contains(":model-value=\"formData.birthday || Date.now()\"")); | |
| 236 | + assertTrue(contactForm.contains("v-model=\"formData.sex\"")); | |
| 237 | + assertTrue(contactForm.contains("<yd-upload-img v-model=\"formData.avatar\"")); | |
| 238 | + assertTrue(contactForm.contains("<yd-upload-file v-model=\"formData.video\"")); | |
| 239 | + assertTrue(contactForm.contains("<wd-textarea v-model=\"formData.description\"")); | |
| 240 | + } | |
| 241 | + } | |
| 242 | + | |
| 62 | 243 | } | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/assert.json
0 → 100644
| 1 | +[ { | |
| 2 | + "contentPath" : "java/InfraStudentPageReqVO", | |
| 3 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" | |
| 4 | +}, { | |
| 5 | + "contentPath" : "java/InfraStudentRespVO", | |
| 6 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" | |
| 7 | +}, { | |
| 8 | + "contentPath" : "java/InfraStudentSaveReqVO", | |
| 9 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" | |
| 10 | +}, { | |
| 11 | + "contentPath" : "java/InfraStudentController", | |
| 12 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" | |
| 13 | +}, { | |
| 14 | + "contentPath" : "java/InfraStudentDO", | |
| 15 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" | |
| 16 | +}, { | |
| 17 | + "contentPath" : "java/InfraStudentContactDO", | |
| 18 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" | |
| 19 | +}, { | |
| 20 | + "contentPath" : "java/InfraStudentTeacherDO", | |
| 21 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" | |
| 22 | +}, { | |
| 23 | + "contentPath" : "java/InfraStudentMapper", | |
| 24 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" | |
| 25 | +}, { | |
| 26 | + "contentPath" : "java/InfraStudentContactMapper", | |
| 27 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" | |
| 28 | +}, { | |
| 29 | + "contentPath" : "java/InfraStudentTeacherMapper", | |
| 30 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" | |
| 31 | +}, { | |
| 32 | + "contentPath" : "xml/InfraStudentMapper", | |
| 33 | + "filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml" | |
| 34 | +}, { | |
| 35 | + "contentPath" : "java/InfraStudentServiceImpl", | |
| 36 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" | |
| 37 | +}, { | |
| 38 | + "contentPath" : "java/InfraStudentService", | |
| 39 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" | |
| 40 | +}, { | |
| 41 | + "contentPath" : "java/InfraStudentServiceImplTest", | |
| 42 | + "filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" | |
| 43 | +}, { | |
| 44 | + "contentPath" : "java/ErrorCodeConstants_手动操作", | |
| 45 | + "filePath" : "yudao-module-infra/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" : "ts/index", | |
| 54 | + "filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts" | |
| 55 | +}, { | |
| 56 | + "contentPath" : "vue/index", | |
| 57 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue" | |
| 58 | +}, { | |
| 59 | + "contentPath" : "vue/search-form", | |
| 60 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue" | |
| 61 | +}, { | |
| 62 | + "contentPath" : "vue/form/index", | |
| 63 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue" | |
| 64 | +}, { | |
| 65 | + "contentPath" : "vue/detail/index", | |
| 66 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue" | |
| 67 | +}, { | |
| 68 | + "contentPath" : "vue/student-contact/form/index", | |
| 69 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/student-contact/form/index.vue" | |
| 70 | +}, { | |
| 71 | + "contentPath" : "vue/student-teacher/form/index", | |
| 72 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/student-teacher/form/index.vue" | |
| 73 | +} ] | |
| 0 | 74 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/java/ErrorCodeConstants_手动操作
0 → 100644
| 1 | +// TODO 待办:请将下面的错误码复制到 yudao-module-infra 模块的 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 | 7 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 72 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + default int deleteByStudentIds(List<Long> studentIds) { | |
| 31 | + return deleteBatch(InfraStudentContactDO::getStudentId, studentIds); | |
| 32 | + } | |
| 33 | + | |
| 34 | +} | |
| 0 | 35 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.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 jakarta.validation.constraints.*; | |
| 12 | +import jakarta.validation.*; | |
| 13 | +import jakarta.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.apilog.core.annotation.ApiAccessLog; | |
| 26 | +import static cn.iocoder.yudao.framework.apilog.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 | + @DeleteMapping("/delete-list") | |
| 68 | + @Parameter(name = "ids", description = "编号", required = true) | |
| 69 | + @Operation(summary = "批量删除学生") | |
| 70 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 71 | + public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) { | |
| 72 | + studentService.deleteStudentListByIds(ids); | |
| 73 | + return success(true); | |
| 74 | + } | |
| 75 | + | |
| 76 | + @GetMapping("/get") | |
| 77 | + @Operation(summary = "获得学生") | |
| 78 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 79 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 80 | + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { | |
| 81 | + InfraStudentDO student = studentService.getStudent(id); | |
| 82 | + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @GetMapping("/page") | |
| 86 | + @Operation(summary = "获得学生分页") | |
| 87 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 88 | + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { | |
| 89 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); | |
| 90 | + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); | |
| 91 | + } | |
| 92 | + | |
| 93 | + @GetMapping("/export-excel") | |
| 94 | + @Operation(summary = "导出学生 Excel") | |
| 95 | + @PreAuthorize("@ss.hasPermission('infra:student:export')") | |
| 96 | + @ApiAccessLog(operateType = EXPORT) | |
| 97 | + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, | |
| 98 | + HttpServletResponse response) throws IOException { | |
| 99 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 100 | + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); | |
| 101 | + // 导出 Excel | |
| 102 | + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, | |
| 103 | + BeanUtils.toBean(list, InfraStudentRespVO.class)); | |
| 104 | + } | |
| 105 | + | |
| 106 | + // ==================== 子表(学生联系人) ==================== | |
| 107 | + | |
| 108 | + @GetMapping("/student-contact/page") | |
| 109 | + @Operation(summary = "获得学生联系人分页") | |
| 110 | + @Parameter(name = "studentId", description = "学生编号") | |
| 111 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 112 | + public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO, | |
| 113 | + @RequestParam("studentId") Long studentId) { | |
| 114 | + return success(studentService.getStudentContactPage(pageReqVO, studentId)); | |
| 115 | + } | |
| 116 | + | |
| 117 | + @PostMapping("/student-contact/create") | |
| 118 | + @Operation(summary = "创建学生联系人") | |
| 119 | + @PreAuthorize("@ss.hasPermission('infra:student:create')") | |
| 120 | + public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { | |
| 121 | + return success(studentService.createStudentContact(studentContact)); | |
| 122 | + } | |
| 123 | + | |
| 124 | + @PutMapping("/student-contact/update") | |
| 125 | + @Operation(summary = "更新学生联系人") | |
| 126 | + @PreAuthorize("@ss.hasPermission('infra:student:update')") | |
| 127 | + public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { | |
| 128 | + studentService.updateStudentContact(studentContact); | |
| 129 | + return success(true); | |
| 130 | + } | |
| 131 | + | |
| 132 | + @DeleteMapping("/student-contact/delete") | |
| 133 | + @Parameter(name = "id", description = "编号", required = true) | |
| 134 | + @Operation(summary = "删除学生联系人") | |
| 135 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 136 | + public CommonResult<Boolean> deleteStudentContact(@RequestParam("id") Long id) { | |
| 137 | + studentService.deleteStudentContact(id); | |
| 138 | + return success(true); | |
| 139 | + } | |
| 140 | + | |
| 141 | + @DeleteMapping("/student-contact/delete-list") | |
| 142 | + @Parameter(name = "ids", description = "编号", required = true) | |
| 143 | + @Operation(summary = "批量删除学生联系人") | |
| 144 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 145 | + public CommonResult<Boolean> deleteStudentContactList(@RequestParam("ids") List<Long> ids) { | |
| 146 | + studentService.deleteStudentContactListByIds(ids); | |
| 147 | + return success(true); | |
| 148 | + } | |
| 149 | + | |
| 150 | + @GetMapping("/student-contact/get") | |
| 151 | + @Operation(summary = "获得学生联系人") | |
| 152 | + @Parameter(name = "id", description = "编号", required = true) | |
| 153 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 154 | + public CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam("id") Long id) { | |
| 155 | + return success(studentService.getStudentContact(id)); | |
| 156 | + } | |
| 157 | + | |
| 158 | + // ==================== 子表(学生班主任) ==================== | |
| 159 | + | |
| 160 | + @GetMapping("/student-teacher/page") | |
| 161 | + @Operation(summary = "获得学生班主任分页") | |
| 162 | + @Parameter(name = "studentId", description = "学生编号") | |
| 163 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 164 | + public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO, | |
| 165 | + @RequestParam("studentId") Long studentId) { | |
| 166 | + return success(studentService.getStudentTeacherPage(pageReqVO, studentId)); | |
| 167 | + } | |
| 168 | + | |
| 169 | + @PostMapping("/student-teacher/create") | |
| 170 | + @Operation(summary = "创建学生班主任") | |
| 171 | + @PreAuthorize("@ss.hasPermission('infra:student:create')") | |
| 172 | + public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { | |
| 173 | + return success(studentService.createStudentTeacher(studentTeacher)); | |
| 174 | + } | |
| 175 | + | |
| 176 | + @PutMapping("/student-teacher/update") | |
| 177 | + @Operation(summary = "更新学生班主任") | |
| 178 | + @PreAuthorize("@ss.hasPermission('infra:student:update')") | |
| 179 | + public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { | |
| 180 | + studentService.updateStudentTeacher(studentTeacher); | |
| 181 | + return success(true); | |
| 182 | + } | |
| 183 | + | |
| 184 | + @DeleteMapping("/student-teacher/delete") | |
| 185 | + @Parameter(name = "id", description = "编号", required = true) | |
| 186 | + @Operation(summary = "删除学生班主任") | |
| 187 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 188 | + public CommonResult<Boolean> deleteStudentTeacher(@RequestParam("id") Long id) { | |
| 189 | + studentService.deleteStudentTeacher(id); | |
| 190 | + return success(true); | |
| 191 | + } | |
| 192 | + | |
| 193 | + @DeleteMapping("/student-teacher/delete-list") | |
| 194 | + @Parameter(name = "ids", description = "编号", required = true) | |
| 195 | + @Operation(summary = "批量删除学生班主任") | |
| 196 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 197 | + public CommonResult<Boolean> deleteStudentTeacherList(@RequestParam("ids") List<Long> ids) { | |
| 198 | + studentService.deleteStudentTeacherListByIds(ids); | |
| 199 | + return success(true); | |
| 200 | + } | |
| 201 | + | |
| 202 | + @GetMapping("/student-teacher/get") | |
| 203 | + @Operation(summary = "获得学生班主任") | |
| 204 | + @Parameter(name = "id", description = "编号", required = true) | |
| 205 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 206 | + public CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam("id") Long id) { | |
| 207 | + return success(studentService.getStudentTeacher(id)); | |
| 208 | + } | |
| 209 | + | |
| 210 | +} | |
| 0 | 211 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 68 | +} | |
| 0 | 69 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 31 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +public class InfraStudentPageReqVO extends PageParam { | |
| 15 | + | |
| 16 | + @Schema(description = "名字", example = "芋头") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "出生日期") | |
| 20 | + private LocalDateTime birthday; | |
| 21 | + | |
| 22 | + @Schema(description = "性别", example = "1") | |
| 23 | + private Integer sex; | |
| 24 | + | |
| 25 | + @Schema(description = "是否有效", example = "true") | |
| 26 | + private Boolean enabled; | |
| 27 | + | |
| 28 | + @Schema(description = "创建时间") | |
| 29 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 30 | + private LocalDateTime[] createTime; | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 org.springframework.format.annotation.DateTimeFormat; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import cn.idev.excel.annotation.*; | |
| 9 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 10 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生 Response VO") | |
| 13 | +@Data | |
| 14 | +@ExcelIgnoreUnannotated | |
| 15 | +public class InfraStudentRespVO { | |
| 16 | + | |
| 17 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @ExcelProperty("编号") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 22 | + @ExcelProperty("名字") | |
| 23 | + private String name; | |
| 24 | + | |
| 25 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 26 | + @ExcelProperty("简介") | |
| 27 | + private String description; | |
| 28 | + | |
| 29 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 30 | + @ExcelProperty("出生日期") | |
| 31 | + private LocalDateTime birthday; | |
| 32 | + | |
| 33 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 34 | + @ExcelProperty(value = "性别", converter = DictConvert.class) | |
| 35 | + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 | |
| 36 | + private Integer sex; | |
| 37 | + | |
| 38 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 39 | + @ExcelProperty(value = "是否有效", converter = DictConvert.class) | |
| 40 | + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 | |
| 41 | + private Boolean enabled; | |
| 42 | + | |
| 43 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 44 | + @ExcelProperty("头像") | |
| 45 | + private String avatar; | |
| 46 | + | |
| 47 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 48 | + @ExcelProperty("附件") | |
| 49 | + private String video; | |
| 50 | + | |
| 51 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 52 | + @ExcelProperty("备注") | |
| 53 | + private String memo; | |
| 54 | + | |
| 55 | + @Schema(description = "创建时间") | |
| 56 | + @ExcelProperty("创建时间") | |
| 57 | + private LocalDateTime createTime; | |
| 58 | + | |
| 59 | +} | |
| 0 | 60 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.validation.constraints.*; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; | |
| 10 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生新增/修改 Request VO") | |
| 13 | +@Data | |
| 14 | +public class InfraStudentSaveReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 20 | + @NotEmpty(message = "名字不能为空") | |
| 21 | + private String name; | |
| 22 | + | |
| 23 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 24 | + @NotEmpty(message = "简介不能为空") | |
| 25 | + private String description; | |
| 26 | + | |
| 27 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 28 | + @NotNull(message = "出生日期不能为空") | |
| 29 | + private LocalDateTime birthday; | |
| 30 | + | |
| 31 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 32 | + @NotNull(message = "性别不能为空") | |
| 33 | + private Integer sex; | |
| 34 | + | |
| 35 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 36 | + @NotNull(message = "是否有效不能为空") | |
| 37 | + private Boolean enabled; | |
| 38 | + | |
| 39 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 40 | + @NotEmpty(message = "头像不能为空") | |
| 41 | + private String avatar; | |
| 42 | + | |
| 43 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 44 | + @NotEmpty(message = "附件不能为空") | |
| 45 | + private String video; | |
| 46 | + | |
| 47 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 48 | + @NotEmpty(message = "备注不能为空") | |
| 49 | + private String memo; | |
| 50 | + | |
| 51 | +} | |
| 0 | 52 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/java/InfraStudentService
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | +import jakarta.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 ids 编号 | |
| 45 | + */ | |
| 46 | + void deleteStudentListByIds(List<Long> ids); | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 获得学生 | |
| 50 | + * | |
| 51 | + * @param id 编号 | |
| 52 | + * @return 学生 | |
| 53 | + */ | |
| 54 | + InfraStudentDO getStudent(Long id); | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 获得学生分页 | |
| 58 | + * | |
| 59 | + * @param pageReqVO 分页查询 | |
| 60 | + * @return 学生分页 | |
| 61 | + */ | |
| 62 | + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO); | |
| 63 | + | |
| 64 | + // ==================== 子表(学生联系人) ==================== | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 获得学生联系人分页 | |
| 68 | + * | |
| 69 | + * @param pageReqVO 分页查询 | |
| 70 | + * @param studentId 学生编号 | |
| 71 | + * @return 学生联系人分页 | |
| 72 | + */ | |
| 73 | + PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId); | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * 创建学生联系人 | |
| 77 | + * | |
| 78 | + * @param studentContact 创建信息 | |
| 79 | + * @return 编号 | |
| 80 | + */ | |
| 81 | + Long createStudentContact(@Valid InfraStudentContactDO studentContact); | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * 更新学生联系人 | |
| 85 | + * | |
| 86 | + * @param studentContact 更新信息 | |
| 87 | + */ | |
| 88 | + void updateStudentContact(@Valid InfraStudentContactDO studentContact); | |
| 89 | + | |
| 90 | + /** | |
| 91 | + * 删除学生联系人 | |
| 92 | + * | |
| 93 | + * @param id 编号 | |
| 94 | + */ | |
| 95 | + void deleteStudentContact(Long id); | |
| 96 | + | |
| 97 | + /** | |
| 98 | + * 批量删除学生联系人 | |
| 99 | + * | |
| 100 | + * @param ids 编号 | |
| 101 | + */ | |
| 102 | + void deleteStudentContactListByIds(List<Long> ids); | |
| 103 | + | |
| 104 | + /** | |
| 105 | + * 获得学生联系人 | |
| 106 | + * | |
| 107 | + * @param id 编号 | |
| 108 | + * @return 学生联系人 | |
| 109 | + */ | |
| 110 | + InfraStudentContactDO getStudentContact(Long id); | |
| 111 | + | |
| 112 | + // ==================== 子表(学生班主任) ==================== | |
| 113 | + | |
| 114 | + /** | |
| 115 | + * 获得学生班主任分页 | |
| 116 | + * | |
| 117 | + * @param pageReqVO 分页查询 | |
| 118 | + * @param studentId 学生编号 | |
| 119 | + * @return 学生班主任分页 | |
| 120 | + */ | |
| 121 | + PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId); | |
| 122 | + | |
| 123 | + /** | |
| 124 | + * 创建学生班主任 | |
| 125 | + * | |
| 126 | + * @param studentTeacher 创建信息 | |
| 127 | + * @return 编号 | |
| 128 | + */ | |
| 129 | + Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); | |
| 130 | + | |
| 131 | + /** | |
| 132 | + * 更新学生班主任 | |
| 133 | + * | |
| 134 | + * @param studentTeacher 更新信息 | |
| 135 | + */ | |
| 136 | + void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * 删除学生班主任 | |
| 140 | + * | |
| 141 | + * @param id 编号 | |
| 142 | + */ | |
| 143 | + void deleteStudentTeacher(Long id); | |
| 144 | + | |
| 145 | + /** | |
| 146 | + * 批量删除学生班主任 | |
| 147 | + * | |
| 148 | + * @param ids 编号 | |
| 149 | + */ | |
| 150 | + void deleteStudentTeacherListByIds(List<Long> ids); | |
| 151 | + | |
| 152 | + /** | |
| 153 | + * 获得学生班主任 | |
| 154 | + * | |
| 155 | + * @param id 编号 | |
| 156 | + * @return 学生班主任 | |
| 157 | + */ | |
| 158 | + InfraStudentTeacherDO getStudentTeacher(Long id); | |
| 159 | + | |
| 160 | +} | |
| 0 | 161 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/java/InfraStudentServiceImpl
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import org.springframework.stereotype.Service; | |
| 5 | +import jakarta.annotation.Resource; | |
| 6 | +import org.springframework.validation.annotation.Validated; | |
| 7 | +import org.springframework.transaction.annotation.Transactional; | |
| 8 | + | |
| 9 | +import java.util.*; | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; | |
| 12 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; | |
| 13 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; | |
| 14 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 15 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 16 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 17 | + | |
| 18 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; | |
| 19 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; | |
| 20 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; | |
| 21 | + | |
| 22 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 23 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; | |
| 25 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * 学生 Service 实现类 | |
| 29 | + * | |
| 30 | + * @author 芋道源码 | |
| 31 | + */ | |
| 32 | +@Service | |
| 33 | +@Validated | |
| 34 | +public class InfraStudentServiceImpl implements InfraStudentService { | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private InfraStudentMapper studentMapper; | |
| 38 | + @Resource | |
| 39 | + private InfraStudentContactMapper studentContactMapper; | |
| 40 | + @Resource | |
| 41 | + private InfraStudentTeacherMapper studentTeacherMapper; | |
| 42 | + | |
| 43 | + @Override | |
| 44 | + public Long createStudent(InfraStudentSaveReqVO createReqVO) { | |
| 45 | + // 插入 | |
| 46 | + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); | |
| 47 | + studentMapper.insert(student); | |
| 48 | + | |
| 49 | + // 返回 | |
| 50 | + return student.getId(); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { | |
| 55 | + // 校验存在 | |
| 56 | + validateStudentExists(updateReqVO.getId()); | |
| 57 | + // 更新 | |
| 58 | + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); | |
| 59 | + studentMapper.updateById(updateObj); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @Override | |
| 63 | + @Transactional(rollbackFor = Exception.class) | |
| 64 | + public void deleteStudent(Long id) { | |
| 65 | + // 校验存在 | |
| 66 | + validateStudentExists(id); | |
| 67 | + // 删除 | |
| 68 | + studentMapper.deleteById(id); | |
| 69 | + | |
| 70 | + // 删除子表 | |
| 71 | + deleteStudentContactByStudentId(id); | |
| 72 | + deleteStudentTeacherByStudentId(id); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Override | |
| 76 | + @Transactional(rollbackFor = Exception.class) | |
| 77 | + public void deleteStudentListByIds(List<Long> ids) { | |
| 78 | + // 删除 | |
| 79 | + studentMapper.deleteByIds(ids); | |
| 80 | + | |
| 81 | + // 删除子表 | |
| 82 | + deleteStudentContactByStudentIds(ids); | |
| 83 | + deleteStudentTeacherByStudentIds(ids); | |
| 84 | + } | |
| 85 | + | |
| 86 | + | |
| 87 | + private void validateStudentExists(Long id) { | |
| 88 | + if (studentMapper.selectById(id) == null) { | |
| 89 | + throw exception(STUDENT_NOT_EXISTS); | |
| 90 | + } | |
| 91 | + } | |
| 92 | + | |
| 93 | + @Override | |
| 94 | + public InfraStudentDO getStudent(Long id) { | |
| 95 | + return studentMapper.selectById(id); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Override | |
| 99 | + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) { | |
| 100 | + return studentMapper.selectPage(pageReqVO); | |
| 101 | + } | |
| 102 | + | |
| 103 | + // ==================== 子表(学生联系人) ==================== | |
| 104 | + | |
| 105 | + @Override | |
| 106 | + public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) { | |
| 107 | + return studentContactMapper.selectPage(pageReqVO, studentId); | |
| 108 | + } | |
| 109 | + | |
| 110 | + @Override | |
| 111 | + public Long createStudentContact(InfraStudentContactDO studentContact) { | |
| 112 | + studentContact.clean(); // 清理掉创建、更新时间等相关属性值 | |
| 113 | + studentContactMapper.insert(studentContact); | |
| 114 | + return studentContact.getId(); | |
| 115 | + } | |
| 116 | + | |
| 117 | + @Override | |
| 118 | + public void updateStudentContact(InfraStudentContactDO studentContact) { | |
| 119 | + // 校验存在 | |
| 120 | + validateStudentContactExists(studentContact.getId()); | |
| 121 | + // 更新 | |
| 122 | + studentContact.clean(); // 解决更新情况下:updateTime 不更新 | |
| 123 | + studentContactMapper.updateById(studentContact); | |
| 124 | + } | |
| 125 | + | |
| 126 | + @Override | |
| 127 | + public void deleteStudentContact(Long id) { | |
| 128 | + // 删除 | |
| 129 | + studentContactMapper.deleteById(id); | |
| 130 | + } | |
| 131 | + | |
| 132 | + @Override | |
| 133 | + public void deleteStudentContactListByIds(List<Long> ids) { | |
| 134 | + // 删除 | |
| 135 | + studentContactMapper.deleteByIds(ids); | |
| 136 | + } | |
| 137 | + | |
| 138 | + @Override | |
| 139 | + public InfraStudentContactDO getStudentContact(Long id) { | |
| 140 | + return studentContactMapper.selectById(id); | |
| 141 | + } | |
| 142 | + | |
| 143 | + private void validateStudentContactExists(Long id) { | |
| 144 | + if (studentContactMapper.selectById(id) == null) { | |
| 145 | + throw exception(STUDENT_CONTACT_NOT_EXISTS); | |
| 146 | + } | |
| 147 | + } | |
| 148 | + | |
| 149 | + private void deleteStudentContactByStudentId(Long studentId) { | |
| 150 | + studentContactMapper.deleteByStudentId(studentId); | |
| 151 | + } | |
| 152 | + | |
| 153 | + private void deleteStudentContactByStudentIds(List<Long> studentIds) { | |
| 154 | + studentContactMapper.deleteByStudentIds(studentIds); | |
| 155 | + } | |
| 156 | + | |
| 157 | + // ==================== 子表(学生班主任) ==================== | |
| 158 | + | |
| 159 | + @Override | |
| 160 | + public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) { | |
| 161 | + return studentTeacherMapper.selectPage(pageReqVO, studentId); | |
| 162 | + } | |
| 163 | + | |
| 164 | + @Override | |
| 165 | + public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) { | |
| 166 | + // 校验是否已经存在 | |
| 167 | + if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) { | |
| 168 | + throw exception(STUDENT_TEACHER_EXISTS); | |
| 169 | + } | |
| 170 | + // 插入 | |
| 171 | + studentTeacher.clean(); // 清理掉创建、更新时间等相关属性值 | |
| 172 | + studentTeacherMapper.insert(studentTeacher); | |
| 173 | + return studentTeacher.getId(); | |
| 174 | + } | |
| 175 | + | |
| 176 | + @Override | |
| 177 | + public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) { | |
| 178 | + // 校验存在 | |
| 179 | + validateStudentTeacherExists(studentTeacher.getId()); | |
| 180 | + // 更新 | |
| 181 | + studentTeacher.clean(); // 解决更新情况下:updateTime 不更新 | |
| 182 | + studentTeacherMapper.updateById(studentTeacher); | |
| 183 | + } | |
| 184 | + | |
| 185 | + @Override | |
| 186 | + public void deleteStudentTeacher(Long id) { | |
| 187 | + // 删除 | |
| 188 | + studentTeacherMapper.deleteById(id); | |
| 189 | + } | |
| 190 | + | |
| 191 | + @Override | |
| 192 | + public void deleteStudentTeacherListByIds(List<Long> ids) { | |
| 193 | + // 删除 | |
| 194 | + studentTeacherMapper.deleteByIds(ids); | |
| 195 | + } | |
| 196 | + | |
| 197 | + @Override | |
| 198 | + public InfraStudentTeacherDO getStudentTeacher(Long id) { | |
| 199 | + return studentTeacherMapper.selectById(id); | |
| 200 | + } | |
| 201 | + | |
| 202 | + private void validateStudentTeacherExists(Long id) { | |
| 203 | + if (studentTeacherMapper.selectById(id) == null) { | |
| 204 | + throw exception(STUDENT_TEACHER_NOT_EXISTS); | |
| 205 | + } | |
| 206 | + } | |
| 207 | + | |
| 208 | + private void deleteStudentTeacherByStudentId(Long studentId) { | |
| 209 | + studentTeacherMapper.deleteByStudentId(studentId); | |
| 210 | + } | |
| 211 | + | |
| 212 | + private void deleteStudentTeacherByStudentIds(List<Long> studentIds) { | |
| 213 | + studentTeacherMapper.deleteByStudentIds(studentIds); | |
| 214 | + } | |
| 215 | + | |
| 216 | +} | |
| 0 | 217 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | + | |
| 8 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 9 | + | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; | |
| 12 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; | |
| 13 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 14 | + | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import org.springframework.context.annotation.Import; | |
| 17 | +import java.util.*; | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | + | |
| 20 | +import static cn.hutool.core.util.RandomUtil.*; | |
| 21 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 22 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; | |
| 23 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; | |
| 26 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; | |
| 27 | +import static org.junit.jupiter.api.Assertions.*; | |
| 28 | +import static org.mockito.Mockito.*; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + * {@link InfraStudentServiceImpl} 的单元测试类 | |
| 32 | + * | |
| 33 | + * @author 芋道源码 | |
| 34 | + */ | |
| 35 | +@Import(InfraStudentServiceImpl.class) | |
| 36 | +public class InfraStudentServiceImplTest extends BaseDbUnitTest { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private InfraStudentServiceImpl studentService; | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private InfraStudentMapper studentMapper; | |
| 43 | + | |
| 44 | + @Test | |
| 45 | + public void testCreateStudent_success() { | |
| 46 | + // 准备参数 | |
| 47 | + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); | |
| 48 | + | |
| 49 | + // 调用 | |
| 50 | + Long studentId = studentService.createStudent(createReqVO); | |
| 51 | + // 断言 | |
| 52 | + assertNotNull(studentId); | |
| 53 | + // 校验记录的属性是否正确 | |
| 54 | + InfraStudentDO student = studentMapper.selectById(studentId); | |
| 55 | + assertPojoEquals(createReqVO, student, "id"); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Test | |
| 59 | + public void testUpdateStudent_success() { | |
| 60 | + // mock 数据 | |
| 61 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); | |
| 62 | + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 | |
| 63 | + // 准备参数 | |
| 64 | + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { | |
| 65 | + o.setId(dbStudent.getId()); // 设置更新的 ID | |
| 66 | + }); | |
| 67 | + | |
| 68 | + // 调用 | |
| 69 | + studentService.updateStudent(updateReqVO); | |
| 70 | + // 校验是否更新正确 | |
| 71 | + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 | |
| 72 | + assertPojoEquals(updateReqVO, student); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Test | |
| 76 | + public void testUpdateStudent_notExists() { | |
| 77 | + // 准备参数 | |
| 78 | + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); | |
| 79 | + | |
| 80 | + // 调用, 并断言异常 | |
| 81 | + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Test | |
| 85 | + public void testDeleteStudent_success() { | |
| 86 | + // mock 数据 | |
| 87 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); | |
| 88 | + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 | |
| 89 | + // 准备参数 | |
| 90 | + Long id = dbStudent.getId(); | |
| 91 | + | |
| 92 | + // 调用 | |
| 93 | + studentService.deleteStudent(id); | |
| 94 | + // 校验数据不存在了 | |
| 95 | + assertNull(studentMapper.selectById(id)); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Test | |
| 99 | + public void testDeleteStudent_notExists() { | |
| 100 | + // 准备参数 | |
| 101 | + Long id = randomLongId(); | |
| 102 | + | |
| 103 | + // 调用, 并断言异常 | |
| 104 | + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); | |
| 105 | + } | |
| 106 | + | |
| 107 | + @Test | |
| 108 | + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 109 | + public void testGetStudentPage() { | |
| 110 | + // mock 数据 | |
| 111 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 | |
| 112 | + o.setName(null); | |
| 113 | + o.setBirthday(null); | |
| 114 | + o.setSex(null); | |
| 115 | + o.setEnabled(null); | |
| 116 | + o.setCreateTime(null); | |
| 117 | + }); | |
| 118 | + studentMapper.insert(dbStudent); | |
| 119 | + // 测试 name 不匹配 | |
| 120 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); | |
| 121 | + // 测试 birthday 不匹配 | |
| 122 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); | |
| 123 | + // 测试 sex 不匹配 | |
| 124 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); | |
| 125 | + // 测试 enabled 不匹配 | |
| 126 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); | |
| 127 | + // 测试 createTime 不匹配 | |
| 128 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); | |
| 129 | + // 准备参数 | |
| 130 | + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); | |
| 131 | + reqVO.setName(null); | |
| 132 | + reqVO.setBirthday(null); | |
| 133 | + reqVO.setSex(null); | |
| 134 | + reqVO.setEnabled(null); | |
| 135 | + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | |
| 136 | + | |
| 137 | + // 调用 | |
| 138 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO); | |
| 139 | + // 断言 | |
| 140 | + assertEquals(1, pageResult.getTotal()); | |
| 141 | + assertEquals(1, pageResult.getList().size()); | |
| 142 | + assertPojoEquals(dbStudent, pageResult.getList().get(0)); | |
| 143 | + } | |
| 144 | + | |
| 145 | +} | |
| 0 | 146 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 72 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + default InfraStudentTeacherDO selectByStudentId(Long studentId) { | |
| 26 | + return selectOne(InfraStudentTeacherDO::getStudentId, studentId); | |
| 27 | + } | |
| 28 | + | |
| 29 | + default int deleteByStudentId(Long studentId) { | |
| 30 | + return delete(InfraStudentTeacherDO::getStudentId, studentId); | |
| 31 | + } | |
| 32 | + | |
| 33 | + default int deleteByStudentIds(List<Long> studentIds) { | |
| 34 | + return deleteBatch(InfraStudentTeacherDO::getStudentId, studentIds); | |
| 35 | + } | |
| 36 | + | |
| 37 | +} | |
| 0 | 38 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 18 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +SELECT @parentId := LAST_INSERT_ID(); | |
| 13 | + | |
| 14 | +-- 按钮 SQL | |
| 15 | +INSERT INTO system_menu( | |
| 16 | + name, permission, type, sort, parent_id, | |
| 17 | + path, icon, component, status | |
| 18 | +) | |
| 19 | +VALUES ( | |
| 20 | + '学生查询', 'infra:student:query', 3, 1, @parentId, | |
| 21 | + '', '', '', 0 | |
| 22 | +); | |
| 23 | +INSERT INTO system_menu( | |
| 24 | + name, permission, type, sort, parent_id, | |
| 25 | + path, icon, component, status | |
| 26 | +) | |
| 27 | +VALUES ( | |
| 28 | + '学生创建', 'infra:student:create', 3, 2, @parentId, | |
| 29 | + '', '', '', 0 | |
| 30 | +); | |
| 31 | +INSERT INTO system_menu( | |
| 32 | + name, permission, type, sort, parent_id, | |
| 33 | + path, icon, component, status | |
| 34 | +) | |
| 35 | +VALUES ( | |
| 36 | + '学生更新', 'infra:student:update', 3, 3, @parentId, | |
| 37 | + '', '', '', 0 | |
| 38 | +); | |
| 39 | +INSERT INTO system_menu( | |
| 40 | + name, permission, type, sort, parent_id, | |
| 41 | + path, icon, component, status | |
| 42 | +) | |
| 43 | +VALUES ( | |
| 44 | + '学生删除', 'infra:student:delete', 3, 4, @parentId, | |
| 45 | + '', '', '', 0 | |
| 46 | +); | |
| 47 | +INSERT INTO system_menu( | |
| 48 | + name, permission, type, sort, parent_id, | |
| 49 | + path, icon, component, status | |
| 50 | +) | |
| 51 | +VALUES ( | |
| 52 | + '学生导出', 'infra:student:export', 3, 5, @parentId, | |
| 53 | + '', '', '', 0 | |
| 54 | +); | |
| 0 | 55 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/ts/index
0 → 100644
| 1 | +import type { PageParam, PageResult } from '@/http/types' | |
| 2 | +import { http } from '@/http/http' | |
| 3 | + | |
| 4 | +/** 学生联系人信息 */ | |
| 5 | +export interface StudentContact { | |
| 6 | + id?: number | |
| 7 | + studentId: number | |
| 8 | + name: string | |
| 9 | + description: string | |
| 10 | + birthday: number | |
| 11 | + sex: number | |
| 12 | + enabled: boolean | |
| 13 | + avatar: string | |
| 14 | + video?: string | |
| 15 | + memo: string | |
| 16 | + createTime?: number | |
| 17 | +} | |
| 18 | + | |
| 19 | +/** 学生班主任信息 */ | |
| 20 | +export interface StudentTeacher { | |
| 21 | + id?: number | |
| 22 | + studentId: number | |
| 23 | + name: string | |
| 24 | + description: string | |
| 25 | + birthday: number | |
| 26 | + sex: number | |
| 27 | + enabled: boolean | |
| 28 | + avatar: string | |
| 29 | + video?: string | |
| 30 | + memo: string | |
| 31 | + createTime?: number | |
| 32 | +} | |
| 33 | + | |
| 34 | +/** 学生信息 */ | |
| 35 | +export interface Student { | |
| 36 | + id?: number | |
| 37 | + name: string | |
| 38 | + description: string | |
| 39 | + birthday: number | |
| 40 | + sex: number | |
| 41 | + enabled: boolean | |
| 42 | + avatar: string | |
| 43 | + video: string | |
| 44 | + memo: string | |
| 45 | + createTime?: number | |
| 46 | +} | |
| 47 | + | |
| 48 | +/** 获取学生分页列表 */ | |
| 49 | +export function getStudentPage(params: PageParam) { | |
| 50 | + return http.get<PageResult<Student>>('/infra/student/page', params) | |
| 51 | +} | |
| 52 | + | |
| 53 | +/** 获取学生详情 */ | |
| 54 | +export function getStudent(id: number) { | |
| 55 | + return http.get<Student>(`/infra/student/get?id=${id}`) | |
| 56 | +} | |
| 57 | + | |
| 58 | +/** 创建学生 */ | |
| 59 | +export function createStudent(data: Student) { | |
| 60 | + return http.post<number>('/infra/student/create', data) | |
| 61 | +} | |
| 62 | + | |
| 63 | +/** 更新学生 */ | |
| 64 | +export function updateStudent(data: Student) { | |
| 65 | + return http.put<boolean>('/infra/student/update', data) | |
| 66 | +} | |
| 67 | + | |
| 68 | +/** 删除学生 */ | |
| 69 | +export function deleteStudent(id: number) { | |
| 70 | + return http.delete<boolean>(`/infra/student/delete?id=${id}`) | |
| 71 | +} | |
| 72 | + | |
| 73 | +// ==================== 子表:学生联系人 ==================== | |
| 74 | + | |
| 75 | +/** 获取学生联系人分页列表 */ | |
| 76 | +export function getStudentContactPage(params: PageParam & { studentId: number }) { | |
| 77 | + return http.get<PageResult<StudentContact>>('/infra/student/student-contact/page', params) | |
| 78 | +} | |
| 79 | + | |
| 80 | +/** 获取学生联系人详情 */ | |
| 81 | +export function getStudentContact(id: number) { | |
| 82 | + return http.get<StudentContact>(`/infra/student/student-contact/get?id=${id}`) | |
| 83 | +} | |
| 84 | + | |
| 85 | +/** 创建学生联系人 */ | |
| 86 | +export function createStudentContact(data: StudentContact) { | |
| 87 | + return http.post<number>('/infra/student/student-contact/create', data) | |
| 88 | +} | |
| 89 | + | |
| 90 | +/** 更新学生联系人 */ | |
| 91 | +export function updateStudentContact(data: StudentContact) { | |
| 92 | + return http.put<boolean>('/infra/student/student-contact/update', data) | |
| 93 | +} | |
| 94 | + | |
| 95 | +/** 删除学生联系人 */ | |
| 96 | +export function deleteStudentContact(id: number) { | |
| 97 | + return http.delete<boolean>(`/infra/student/student-contact/delete?id=${id}`) | |
| 98 | +} | |
| 99 | + | |
| 100 | +// ==================== 子表:学生班主任 ==================== | |
| 101 | + | |
| 102 | +/** 获取学生班主任分页列表 */ | |
| 103 | +export function getStudentTeacherPage(params: PageParam & { studentId: number }) { | |
| 104 | + return http.get<PageResult<StudentTeacher>>('/infra/student/student-teacher/page', params) | |
| 105 | +} | |
| 106 | + | |
| 107 | +/** 获取学生班主任详情 */ | |
| 108 | +export function getStudentTeacher(id: number) { | |
| 109 | + return http.get<StudentTeacher>(`/infra/student/student-teacher/get?id=${id}`) | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** 创建学生班主任 */ | |
| 113 | +export function createStudentTeacher(data: StudentTeacher) { | |
| 114 | + return http.post<number>('/infra/student/student-teacher/create', data) | |
| 115 | +} | |
| 116 | + | |
| 117 | +/** 更新学生班主任 */ | |
| 118 | +export function updateStudentTeacher(data: StudentTeacher) { | |
| 119 | + return http.put<boolean>('/infra/student/student-teacher/update', data) | |
| 120 | +} | |
| 121 | + | |
| 122 | +/** 删除学生班主任 */ | |
| 123 | +export function deleteStudentTeacher(id: number) { | |
| 124 | + return http.delete<boolean>(`/infra/student/student-teacher/delete?id=${id}`) | |
| 125 | +} | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/detail/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar title="学生详情" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" /> | |
| 5 | + | |
| 6 | + <view class="pb-160rpx"> | |
| 7 | + <!-- 主表详情 --> | |
| 8 | + <wd-cell-group border title="学生信息"> | |
| 9 | + <wd-cell title="编号" :value="formData?.id ?? '-'" /> | |
| 10 | + <wd-cell title="名字" :value="formData?.name ?? '-'" /> | |
| 11 | + <wd-cell title="简介" :value="formData?.description ?? '-'" /> | |
| 12 | + <wd-cell title="出生日期" :value="formatDateTime(formData?.birthday) || '-'" /> | |
| 13 | + <wd-cell title="性别"> | |
| 14 | + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="formData?.sex" /> | |
| 15 | + </wd-cell> | |
| 16 | + <wd-cell title="是否有效"> | |
| 17 | + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="formData?.enabled" /> | |
| 18 | + </wd-cell> | |
| 19 | + <wd-cell title="头像" :value="formData?.avatar ?? '-'" /> | |
| 20 | + <wd-cell title="附件" :value="formData?.video ?? '-'" /> | |
| 21 | + <wd-cell title="备注" :value="formData?.memo ?? '-'" /> | |
| 22 | + <wd-cell title="创建时间" :value="formatDateTime(formData?.createTime) || '-'" /> | |
| 23 | + </wd-cell-group> | |
| 24 | + | |
| 25 | + <!-- 子表:学生联系人 --> | |
| 26 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 27 | + <text class="text-28rpx text-[#333] font-semibold">学生联系人({{ studentContactTotal }})</text> | |
| 28 | + <wd-button | |
| 29 | + v-if="hasAccessByCodes(['infra:student:create'])" | |
| 30 | + size="small" type="primary" variant="plain" @click="openStudentContact()" | |
| 31 | + > | |
| 32 | + 添加 | |
| 33 | + </wd-button> | |
| 34 | + </view> | |
| 35 | + <view class="px-24rpx"> | |
| 36 | + <z-paging | |
| 37 | + ref="studentContactPagingRef" | |
| 38 | + v-model="studentContactList" | |
| 39 | + :fixed="false" | |
| 40 | + :auto="false" | |
| 41 | + height="560rpx" | |
| 42 | + :default-page-size="10" | |
| 43 | + :refresher-enabled="false" | |
| 44 | + :inside-more="true" | |
| 45 | + :to-bottom-loading-more-enabled="false" | |
| 46 | + loading-more-default-text="点击加载更多" | |
| 47 | + empty-view-text="暂无学生联系人" | |
| 48 | + @query="queryStudentContactList" | |
| 49 | + > | |
| 50 | + <view | |
| 51 | + v-for="item in studentContactList" | |
| 52 | + :key="item.id" | |
| 53 | + class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 54 | + > | |
| 55 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 56 | + 学生编号:{{ item.studentId ?? '-' }} | |
| 57 | + </view> | |
| 58 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 59 | + 名字:{{ item.name ?? '-' }} | |
| 60 | + </view> | |
| 61 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 62 | + 简介:{{ item.description ?? '-' }} | |
| 63 | + </view> | |
| 64 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 65 | + 出生日期:{{ item.birthday ?? '-' }} | |
| 66 | + </view> | |
| 67 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 68 | + 性别:{{ item.sex ?? '-' }} | |
| 69 | + </view> | |
| 70 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 71 | + 是否有效:{{ item.enabled ?? '-' }} | |
| 72 | + </view> | |
| 73 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 74 | + 头像:{{ item.avatar ?? '-' }} | |
| 75 | + </view> | |
| 76 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 77 | + 附件:{{ item.video ?? '-' }} | |
| 78 | + </view> | |
| 79 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 80 | + 备注:{{ item.memo ?? '-' }} | |
| 81 | + </view> | |
| 82 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 83 | + 创建时间:{{ item.createTime ?? '-' }} | |
| 84 | + </view> | |
| 85 | + <view class="mt-16rpx flex justify-end gap-16rpx"> | |
| 86 | + <wd-button size="small" variant="plain" @click="openStudentContact(item)"> | |
| 87 | + 编辑 | |
| 88 | + </wd-button> | |
| 89 | + <wd-button size="small" type="danger" variant="plain" @click="removeStudentContact(item)"> | |
| 90 | + 删除 | |
| 91 | + </wd-button> | |
| 92 | + </view> | |
| 93 | + </view> | |
| 94 | + </z-paging> | |
| 95 | + </view> | |
| 96 | + <!-- 子表:学生班主任 --> | |
| 97 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 98 | + <text class="text-28rpx text-[#333] font-semibold">学生班主任({{ studentTeacherTotal }})</text> | |
| 99 | + <wd-button | |
| 100 | + v-if="studentTeacherTotal === 0 && hasAccessByCodes(['infra:student:create'])" | |
| 101 | + size="small" type="primary" variant="plain" @click="openStudentTeacher()" | |
| 102 | + > | |
| 103 | + 添加 | |
| 104 | + </wd-button> | |
| 105 | + </view> | |
| 106 | + <view class="px-24rpx"> | |
| 107 | + <z-paging | |
| 108 | + ref="studentTeacherPagingRef" | |
| 109 | + v-model="studentTeacherList" | |
| 110 | + :fixed="false" | |
| 111 | + :auto="false" | |
| 112 | + height="560rpx" | |
| 113 | + :default-page-size="1" | |
| 114 | + :refresher-enabled="false" | |
| 115 | + :inside-more="true" | |
| 116 | + :to-bottom-loading-more-enabled="false" | |
| 117 | + loading-more-default-text="点击加载更多" | |
| 118 | + empty-view-text="暂无学生班主任" | |
| 119 | + @query="queryStudentTeacherList" | |
| 120 | + > | |
| 121 | + <view | |
| 122 | + v-for="item in studentTeacherList" | |
| 123 | + :key="item.id" | |
| 124 | + class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 125 | + > | |
| 126 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 127 | + 学生编号:{{ item.studentId ?? '-' }} | |
| 128 | + </view> | |
| 129 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 130 | + 名字:{{ item.name ?? '-' }} | |
| 131 | + </view> | |
| 132 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 133 | + 简介:{{ item.description ?? '-' }} | |
| 134 | + </view> | |
| 135 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 136 | + 出生日期:{{ item.birthday ?? '-' }} | |
| 137 | + </view> | |
| 138 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 139 | + 性别:{{ item.sex ?? '-' }} | |
| 140 | + </view> | |
| 141 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 142 | + 是否有效:{{ item.enabled ?? '-' }} | |
| 143 | + </view> | |
| 144 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 145 | + 头像:{{ item.avatar ?? '-' }} | |
| 146 | + </view> | |
| 147 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 148 | + 附件:{{ item.video ?? '-' }} | |
| 149 | + </view> | |
| 150 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 151 | + 备注:{{ item.memo ?? '-' }} | |
| 152 | + </view> | |
| 153 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 154 | + 创建时间:{{ item.createTime ?? '-' }} | |
| 155 | + </view> | |
| 156 | + <view class="mt-16rpx flex justify-end gap-16rpx"> | |
| 157 | + <wd-button size="small" variant="plain" @click="openStudentTeacher(item)"> | |
| 158 | + 编辑 | |
| 159 | + </wd-button> | |
| 160 | + <wd-button size="small" type="danger" variant="plain" @click="removeStudentTeacher(item)"> | |
| 161 | + 删除 | |
| 162 | + </wd-button> | |
| 163 | + </view> | |
| 164 | + </view> | |
| 165 | + </z-paging> | |
| 166 | + </view> | |
| 167 | + </view> | |
| 168 | + | |
| 169 | + <!-- 底部操作按钮 --> | |
| 170 | + <view class="yd-detail-footer"> | |
| 171 | + <view class="yd-detail-footer-actions"> | |
| 172 | + <wd-button v-if="hasAccessByCodes(['infra:student:update'])" class="flex-1" type="warning" @click="handleEdit"> | |
| 173 | + 编辑 | |
| 174 | + </wd-button> | |
| 175 | + <wd-button v-if="hasAccessByCodes(['infra:student:delete'])" class="flex-1" type="danger" :loading="deleting" @click="handleDelete"> | |
| 176 | + 删除 | |
| 177 | + </wd-button> | |
| 178 | + </view> | |
| 179 | + </view> | |
| 180 | + </view> | |
| 181 | +</template> | |
| 182 | + | |
| 183 | +<script lang="ts" setup> | |
| 184 | +import type { | |
| 185 | + Student, | |
| 186 | + StudentContact, | |
| 187 | + StudentTeacher, | |
| 188 | +} from '@/api/infra/demo' | |
| 189 | +import { onShow } from '@dcloudio/uni-app' | |
| 190 | +import { nextTick, ref } from 'vue' | |
| 191 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 192 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 193 | +import { | |
| 194 | + deleteStudent, | |
| 195 | + deleteStudentContact, | |
| 196 | + deleteStudentTeacher, | |
| 197 | + getStudent, | |
| 198 | + getStudentContactPage, | |
| 199 | + getStudentTeacherPage, | |
| 200 | +} from '@/api/infra/demo' | |
| 201 | +import { useAccess } from '@/hooks/useAccess' | |
| 202 | +import { delay, navigateBackPlus } from '@/utils' | |
| 203 | +import { DICT_TYPE } from '@/utils/constants' | |
| 204 | +import { formatDateTime } from '@/utils/date' | |
| 205 | + | |
| 206 | +const props = defineProps<{ id?: number }>() | |
| 207 | + | |
| 208 | +definePage({ | |
| 209 | + style: { navigationBarTitleText: '', navigationStyle: 'custom' }, | |
| 210 | +}) | |
| 211 | + | |
| 212 | +const { hasAccessByCodes } = useAccess() | |
| 213 | +const dialog = useDialog() | |
| 214 | +const toast = useToast() | |
| 215 | +const formData = ref<Student>() // 主表数据 | |
| 216 | +const studentContactList = ref<StudentContact[]>([]) // 学生联系人列表 | |
| 217 | +const studentContactTotal = ref(0) // 学生联系人总数 | |
| 218 | +const studentContactPagingRef = ref<any>() // 学生联系人分页组件引用 | |
| 219 | +const studentTeacherList = ref<StudentTeacher[]>([]) // 学生班主任列表 | |
| 220 | +const studentTeacherTotal = ref(0) // 学生班主任总数 | |
| 221 | +const studentTeacherPagingRef = ref<any>() // 学生班主任分页组件引用 | |
| 222 | +const deleting = ref(false) // 主表删除状态 | |
| 223 | + | |
| 224 | +/** 返回上一页 */ | |
| 225 | +function handleBack() { | |
| 226 | + navigateBackPlus('/pages-infra/demo/index') | |
| 227 | +} | |
| 228 | + | |
| 229 | +/** 加载主表 */ | |
| 230 | +async function loadMain() { | |
| 231 | + if (props.id) { | |
| 232 | + formData.value = await getStudent(props.id) | |
| 233 | + } | |
| 234 | +} | |
| 235 | + | |
| 236 | +/** 查询学生联系人列表 */ | |
| 237 | +async function queryStudentContactList(pageNo: number, pageSize: number) { | |
| 238 | + if (!props.id) { | |
| 239 | + studentContactPagingRef.value?.completeByTotal([], 0) | |
| 240 | + return | |
| 241 | + } | |
| 242 | + try { | |
| 243 | + const data = await getStudentContactPage({ studentId: props.id, pageNo, pageSize }) | |
| 244 | + studentContactTotal.value = data.total | |
| 245 | + studentContactPagingRef.value?.completeByTotal(data.list, data.total) | |
| 246 | + } catch { | |
| 247 | + studentContactPagingRef.value?.complete(false) | |
| 248 | + } | |
| 249 | +} | |
| 250 | + | |
| 251 | +/** 打开学生联系人表单 */ | |
| 252 | +function openStudentContact(item?: StudentContact) { | |
| 253 | + const query = [ | |
| 254 | + `studentId=${props.id}`, | |
| 255 | + item?.id ? `id=${item.id}` : '', | |
| 256 | + ].filter(Boolean).join('&') | |
| 257 | + uni.navigateTo({ url: `/pages-infra/demo/student-contact/form/index?${query}` }) | |
| 258 | +} | |
| 259 | + | |
| 260 | +/** 删除学生联系人 */ | |
| 261 | +async function removeStudentContact(item: StudentContact) { | |
| 262 | + try { | |
| 263 | + await dialog.confirm({ title: '提示', msg: '确定要删除该学生联系人吗?' }) | |
| 264 | + } catch { | |
| 265 | + return | |
| 266 | + } | |
| 267 | + await deleteStudentContact(item.id!) | |
| 268 | + toast.success('删除成功') | |
| 269 | + studentContactPagingRef.value?.reload() | |
| 270 | +} | |
| 271 | + | |
| 272 | +/** 查询学生班主任列表 */ | |
| 273 | +async function queryStudentTeacherList(pageNo: number, pageSize: number) { | |
| 274 | + if (!props.id) { | |
| 275 | + studentTeacherPagingRef.value?.completeByTotal([], 0) | |
| 276 | + return | |
| 277 | + } | |
| 278 | + try { | |
| 279 | + const data = await getStudentTeacherPage({ studentId: props.id, pageNo, pageSize }) | |
| 280 | + studentTeacherTotal.value = data.total | |
| 281 | + studentTeacherPagingRef.value?.completeByTotal(data.list, data.total) | |
| 282 | + } catch { | |
| 283 | + studentTeacherPagingRef.value?.complete(false) | |
| 284 | + } | |
| 285 | +} | |
| 286 | + | |
| 287 | +/** 打开学生班主任表单 */ | |
| 288 | +function openStudentTeacher(item?: StudentTeacher) { | |
| 289 | + const query = [ | |
| 290 | + `studentId=${props.id}`, | |
| 291 | + item?.id ? `id=${item.id}` : '', | |
| 292 | + ].filter(Boolean).join('&') | |
| 293 | + uni.navigateTo({ url: `/pages-infra/demo/student-teacher/form/index?${query}` }) | |
| 294 | +} | |
| 295 | + | |
| 296 | +/** 删除学生班主任 */ | |
| 297 | +async function removeStudentTeacher(item: StudentTeacher) { | |
| 298 | + try { | |
| 299 | + await dialog.confirm({ title: '提示', msg: '确定要删除该学生班主任吗?' }) | |
| 300 | + } catch { | |
| 301 | + return | |
| 302 | + } | |
| 303 | + await deleteStudentTeacher(item.id!) | |
| 304 | + toast.success('删除成功') | |
| 305 | + studentTeacherPagingRef.value?.reload() | |
| 306 | +} | |
| 307 | + | |
| 308 | +/** 编辑主表 */ | |
| 309 | +function handleEdit() { | |
| 310 | + uni.navigateTo({ url: `/pages-infra/demo/form/index?id=${props.id}` }) | |
| 311 | +} | |
| 312 | + | |
| 313 | +/** 删除主表 */ | |
| 314 | +async function handleDelete() { | |
| 315 | + if (!props.id) { | |
| 316 | + return | |
| 317 | + } | |
| 318 | + try { | |
| 319 | + await dialog.confirm({ title: '提示', msg: '确定要删除该学生吗?' }) | |
| 320 | + } catch { | |
| 321 | + return | |
| 322 | + } | |
| 323 | + deleting.value = true | |
| 324 | + try { | |
| 325 | + await deleteStudent(props.id) | |
| 326 | + toast.success('删除成功') | |
| 327 | + uni.$emit('infra:demo:reload') | |
| 328 | + delay(handleBack) | |
| 329 | + } finally { | |
| 330 | + deleting.value = false | |
| 331 | + } | |
| 332 | +} | |
| 333 | + | |
| 334 | +/** 进入页面加载(含编辑和子表返回后刷新) */ | |
| 335 | +onShow(() => { | |
| 336 | + loadMain() | |
| 337 | + nextTick(() => { | |
| 338 | + studentContactPagingRef.value?.reload() | |
| 339 | + studentTeacherPagingRef.value?.reload() | |
| 340 | + }) | |
| 341 | +}) | |
| 342 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/form/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + :title="getTitle" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 表单区域 --> | |
| 11 | + <view> | |
| 12 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 13 | + <wd-cell-group border> | |
| 14 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 15 | + <wd-input | |
| 16 | + v-model="formData.name" | |
| 17 | + clearable | |
| 18 | + placeholder="请输入名字" | |
| 19 | + /> | |
| 20 | + </wd-form-item> | |
| 21 | + <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 22 | + <wd-textarea | |
| 23 | + v-model="formData.description" | |
| 24 | + clearable | |
| 25 | + placeholder="请输入简介" | |
| 26 | + :maxlength="200" | |
| 27 | + show-word-limit | |
| 28 | + /> | |
| 29 | + </wd-form-item> | |
| 30 | + <wd-form-item | |
| 31 | + title="出生日期" | |
| 32 | + title-width="180rpx" | |
| 33 | + prop="birthday" | |
| 34 | + is-link | |
| 35 | + :value="formData.birthday ? formatDateTime(formData.birthday) : ''" | |
| 36 | + placeholder="请选择出生日期" | |
| 37 | + @click="birthdayVisible = true" | |
| 38 | + /> | |
| 39 | + <wd-datetime-picker | |
| 40 | + v-model:visible="birthdayVisible" | |
| 41 | + :model-value="formData.birthday || Date.now()" | |
| 42 | + type="datetime" | |
| 43 | + @update:model-value="value => formData.birthday = value" | |
| 44 | + /> | |
| 45 | + <yd-form-picker | |
| 46 | + v-model="formData.sex" | |
| 47 | + label="性别" | |
| 48 | + label-width="180rpx" | |
| 49 | + prop="sex" | |
| 50 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 51 | + placeholder="请选择性别" | |
| 52 | + /> | |
| 53 | + <wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center> | |
| 54 | + <wd-radio-group v-model="formData.enabled" type="button"> | |
| 55 | + <wd-radio | |
| 56 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 57 | + :key="dict.value" | |
| 58 | + :value="dict.value" | |
| 59 | + > | |
| 60 | + {{ dict.label }} | |
| 61 | + </wd-radio> | |
| 62 | + </wd-radio-group> | |
| 63 | + </wd-form-item> | |
| 64 | + <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 65 | + <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> | |
| 66 | + </wd-form-item> | |
| 67 | + <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 68 | + <yd-upload-file v-model="formData.video" directory="infra/demo" /> | |
| 69 | + </wd-form-item> | |
| 70 | + <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 71 | + <wd-input | |
| 72 | + v-model="formData.memo" | |
| 73 | + clearable | |
| 74 | + placeholder="请输入备注" | |
| 75 | + /> | |
| 76 | + </wd-form-item> | |
| 77 | + </wd-cell-group> | |
| 78 | + </wd-form> | |
| 79 | + </view> | |
| 80 | + | |
| 81 | + <!-- 底部保存按钮 --> | |
| 82 | + <view class="yd-detail-footer"> | |
| 83 | + <wd-button | |
| 84 | + type="primary" | |
| 85 | + block | |
| 86 | + :loading="formLoading" | |
| 87 | + @click="handleSubmit" | |
| 88 | + > | |
| 89 | + 保存 | |
| 90 | + </wd-button> | |
| 91 | + </view> | |
| 92 | + </view> | |
| 93 | +</template> | |
| 94 | + | |
| 95 | +<script lang="ts" setup> | |
| 96 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 97 | +import type { | |
| 98 | + Student, | |
| 99 | +} from '@/api/infra/demo' | |
| 100 | +import { computed, onMounted, ref } from 'vue' | |
| 101 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 102 | +import { createStudent, getStudent, updateStudent } from '@/api/infra/demo' | |
| 103 | +import { getBoolDictOptions } from '@/hooks/useDict' | |
| 104 | +import { delay, navigateBackPlus } from '@/utils' | |
| 105 | +import { DICT_TYPE } from '@/utils/constants' | |
| 106 | +import { formatDateTime } from '@/utils/date' | |
| 107 | +import { createFormSchema } from '@/utils/wot' | |
| 108 | + | |
| 109 | +const props = defineProps<{ | |
| 110 | + id?: number | |
| 111 | +}>() | |
| 112 | + | |
| 113 | +definePage({ | |
| 114 | + style: { | |
| 115 | + navigationBarTitleText: '', | |
| 116 | + navigationStyle: 'custom', | |
| 117 | + }, | |
| 118 | +}) | |
| 119 | + | |
| 120 | +const toast = useToast() | |
| 121 | +const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') | |
| 122 | +const formLoading = ref(false) // 表单提交状态 | |
| 123 | +const formData = ref<Student>({ | |
| 124 | + id: undefined, | |
| 125 | + name: '', | |
| 126 | + description: '', | |
| 127 | + birthday: 0, | |
| 128 | + sex: 0, | |
| 129 | + enabled: false, | |
| 130 | + avatar: '', | |
| 131 | + video: '', | |
| 132 | + memo: '', | |
| 133 | +}) // 表单数据 | |
| 134 | +const birthdayVisible = ref(false) // 出生日期选择器状态 | |
| 135 | +const formSchema = createFormSchema({ | |
| 136 | + name: [{ required: true, message: '名字不能为空' }], | |
| 137 | + description: [{ required: true, message: '简介不能为空' }], | |
| 138 | + birthday: [{ required: true, message: '出生日期不能为空' }], | |
| 139 | + sex: [{ required: true, message: '性别不能为空' }], | |
| 140 | + enabled: [{ required: true, message: '是否有效不能为空' }], | |
| 141 | + avatar: [{ required: true, message: '头像不能为空' }], | |
| 142 | + video: [{ required: true, message: '附件不能为空' }], | |
| 143 | + memo: [{ required: true, message: '备注不能为空' }], | |
| 144 | +}) // 表单校验规则 | |
| 145 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 146 | + | |
| 147 | +/** 返回上一页 */ | |
| 148 | +function handleBack() { | |
| 149 | + navigateBackPlus('/pages-infra/demo/index') | |
| 150 | +} | |
| 151 | + | |
| 152 | +/** 加载学生详情 */ | |
| 153 | +async function getDetail() { | |
| 154 | + if (!props.id) { | |
| 155 | + return | |
| 156 | + } | |
| 157 | + formData.value = await getStudent(props.id) | |
| 158 | +} | |
| 159 | + | |
| 160 | +/** 提交表单 */ | |
| 161 | +async function handleSubmit() { | |
| 162 | + const { valid } = await formRef.value.validate() | |
| 163 | + if (!valid) { | |
| 164 | + return | |
| 165 | + } | |
| 166 | + | |
| 167 | + formLoading.value = true | |
| 168 | + try { | |
| 169 | + const data = formData.value | |
| 170 | + if (props.id) { | |
| 171 | + await updateStudent(data) | |
| 172 | + toast.success('修改成功') | |
| 173 | + } else { | |
| 174 | + await createStudent(data) | |
| 175 | + toast.success('新增成功') | |
| 176 | + } | |
| 177 | + uni.$emit('infra:demo:reload') | |
| 178 | + delay(handleBack) | |
| 179 | + } finally { | |
| 180 | + formLoading.value = false | |
| 181 | + } | |
| 182 | +} | |
| 183 | + | |
| 184 | +/** 初始化 */ | |
| 185 | +onMounted(async () => { | |
| 186 | + getDetail() | |
| 187 | +}) | |
| 188 | +</script> | |
| 189 | + | |
| 190 | +<style lang="scss" scoped> | |
| 191 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container yd-page-container-paging"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="学生管理" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 搜索组件 --> | |
| 11 | + <SearchForm @search="handleQuery" @reset="handleReset" /> | |
| 12 | + | |
| 13 | + <!-- 学生列表 --> | |
| 14 | + <z-paging | |
| 15 | + ref="pagingRef" | |
| 16 | + v-model="list" | |
| 17 | + :fixed="false" | |
| 18 | + class="min-h-0 flex-1" | |
| 19 | + :default-page-size="10" | |
| 20 | + :refresher-enabled="true" | |
| 21 | + :inside-more="true" | |
| 22 | + :loading-more-default-as-loading="true" | |
| 23 | + empty-view-text="暂无学生数据" | |
| 24 | + @query="queryList" | |
| 25 | + > | |
| 26 | + <view class="p-24rpx"> | |
| 27 | + <view | |
| 28 | + v-for="item in list" | |
| 29 | + :key="item.id" | |
| 30 | + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 31 | + @click="handleDetail(item)" | |
| 32 | + > | |
| 33 | + <view class="p-24rpx"> | |
| 34 | + <view class="mb-16rpx flex items-center justify-between"> | |
| 35 | + <view class="text-32rpx text-[#333] font-semibold"> | |
| 36 | + {{ item.name }} | |
| 37 | + </view> | |
| 38 | + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="item.sex" /> | |
| 39 | + </view> | |
| 40 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 41 | + <text class="mr-8rpx text-[#999]">简介:</text> | |
| 42 | + <text class="line-clamp-1">{{ item.description }}</text> | |
| 43 | + </view> | |
| 44 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 45 | + <text class="mr-8rpx text-[#999]">出生日期:</text> | |
| 46 | + <text class="line-clamp-1">{{ formatDateTime(item.birthday) || '-' }}</text> | |
| 47 | + </view> | |
| 48 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 49 | + <text class="mr-8rpx text-[#999]">是否有效:</text> | |
| 50 | + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="item.enabled" /> | |
| 51 | + </view> | |
| 52 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 53 | + <text class="mr-8rpx text-[#999]">头像:</text> | |
| 54 | + <text class="line-clamp-1">{{ item.avatar }}</text> | |
| 55 | + </view> | |
| 56 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 57 | + <text class="mr-8rpx text-[#999]">附件:</text> | |
| 58 | + <text class="line-clamp-1">{{ item.video }}</text> | |
| 59 | + </view> | |
| 60 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 61 | + <text class="mr-8rpx text-[#999]">备注:</text> | |
| 62 | + <text class="line-clamp-1">{{ item.memo }}</text> | |
| 63 | + </view> | |
| 64 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 65 | + <text class="mr-8rpx text-[#999]">创建时间:</text> | |
| 66 | + <text class="line-clamp-1">{{ formatDateTime(item.createTime) || '-' }}</text> | |
| 67 | + </view> | |
| 68 | + </view> | |
| 69 | + </view> | |
| 70 | + </view> | |
| 71 | + </z-paging> | |
| 72 | + | |
| 73 | + <!-- 新增按钮 --> | |
| 74 | + <wd-fab | |
| 75 | + v-if="hasAccessByCodes(['infra:student:create'])" | |
| 76 | + position="right-bottom" | |
| 77 | + type="primary" | |
| 78 | + :expandable="false" | |
| 79 | + @click="handleAdd" | |
| 80 | + /> | |
| 81 | + </view> | |
| 82 | +</template> | |
| 83 | + | |
| 84 | +<script lang="ts" setup> | |
| 85 | +import type { Student } from '@/api/infra/demo' | |
| 86 | +import { onUnload } from '@dcloudio/uni-app' | |
| 87 | +import { onMounted, ref } from 'vue' | |
| 88 | +import { getStudentPage } from '@/api/infra/demo' | |
| 89 | +import { useAccess } from '@/hooks/useAccess' | |
| 90 | +import { navigateBackPlus } from '@/utils' | |
| 91 | +import { DICT_TYPE } from '@/utils/constants' | |
| 92 | +import { formatDateTime } from '@/utils/date' | |
| 93 | +import SearchForm from './components/search-form.vue' | |
| 94 | + | |
| 95 | +definePage({ | |
| 96 | + style: { | |
| 97 | + navigationBarTitleText: '', | |
| 98 | + navigationStyle: 'custom', | |
| 99 | + }, | |
| 100 | +}) | |
| 101 | + | |
| 102 | +const { hasAccessByCodes } = useAccess() | |
| 103 | +const list = ref<Student[]>([]) // 列表数据 | |
| 104 | +const pagingRef = ref<any>() // 分页组件引用 | |
| 105 | +const queryParams = ref<Record<string, any>>({}) // 查询参数 | |
| 106 | + | |
| 107 | +/** 返回上一页 */ | |
| 108 | +function handleBack() { | |
| 109 | + navigateBackPlus() | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** 查询学生列表 */ | |
| 113 | +async function queryList(pageNo: number, pageSize: number) { | |
| 114 | + try { | |
| 115 | + const params = { | |
| 116 | + ...queryParams.value, | |
| 117 | + pageNo, | |
| 118 | + pageSize, | |
| 119 | + } | |
| 120 | + const data = await getStudentPage(params) | |
| 121 | + pagingRef.value?.completeByTotal(data.list, data.total) | |
| 122 | + } catch { | |
| 123 | + pagingRef.value?.complete(false) | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** 搜索按钮操作 */ | |
| 128 | +function handleQuery(data?: Record<string, any>) { | |
| 129 | + queryParams.value = { ...data } | |
| 130 | + reload() | |
| 131 | +} | |
| 132 | + | |
| 133 | +/** 重置按钮操作 */ | |
| 134 | +function handleReset() { | |
| 135 | + handleQuery() | |
| 136 | +} | |
| 137 | + | |
| 138 | +/** 重新加载 */ | |
| 139 | +function reload() { | |
| 140 | + pagingRef.value?.reload() | |
| 141 | +} | |
| 142 | + | |
| 143 | +/** 新增学生 */ | |
| 144 | +function handleAdd() { | |
| 145 | + uni.navigateTo({ | |
| 146 | + url: '/pages-infra/demo/form/index', | |
| 147 | + }) | |
| 148 | +} | |
| 149 | + | |
| 150 | +/** 查看详情 */ | |
| 151 | +function handleDetail(item: Student) { | |
| 152 | + uni.navigateTo({ | |
| 153 | + url: `/pages-infra/demo/detail/index?id=${item.id}`, | |
| 154 | + }) | |
| 155 | +} | |
| 156 | + | |
| 157 | +/** 初始化 */ | |
| 158 | +onMounted(() => { | |
| 159 | + uni.$on('infra:demo:reload', reload) | |
| 160 | +}) | |
| 161 | + | |
| 162 | +/** 卸载 */ | |
| 163 | +onUnload(() => { | |
| 164 | + uni.$off('infra:demo:reload', reload) | |
| 165 | +}) | |
| 166 | +</script> | |
| 167 | + | |
| 168 | +<style lang="scss" scoped> | |
| 169 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/search-form
0 → 100644
| 1 | +<template> | |
| 2 | + <!-- 搜索框入口 --> | |
| 3 | + <view @click="visible = true"> | |
| 4 | + <wd-search :placeholder="placeholder" hide-cancel disabled /> | |
| 5 | + </view> | |
| 6 | + | |
| 7 | + <!-- 搜索弹窗 --> | |
| 8 | + <wd-popup v-model="visible" position="top" @close="visible = false"> | |
| 9 | + <view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }"> | |
| 10 | + <view class="yd-search-form-item"> | |
| 11 | + <view class="yd-search-form-label"> | |
| 12 | + 名字 | |
| 13 | + </view> | |
| 14 | + <wd-input | |
| 15 | + v-model="formData.name" | |
| 16 | + placeholder="请输入名字" | |
| 17 | + clearable | |
| 18 | + /> | |
| 19 | + </view> | |
| 20 | + <view class="yd-search-form-item"> | |
| 21 | + <view class="yd-search-form-label"> | |
| 22 | + 出生日期 | |
| 23 | + </view> | |
| 24 | + <wd-input | |
| 25 | + v-model="formData.birthday" | |
| 26 | + placeholder="请输入出生日期" | |
| 27 | + clearable | |
| 28 | + /> | |
| 29 | + </view> | |
| 30 | + <yd-search-picker | |
| 31 | + v-model="formData.sex" | |
| 32 | + label="性别" | |
| 33 | + :columns="getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" | |
| 34 | + all-option | |
| 35 | + /> | |
| 36 | + <yd-search-picker | |
| 37 | + v-model="formData.enabled" | |
| 38 | + label="是否有效" | |
| 39 | + :columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 40 | + all-option | |
| 41 | + /> | |
| 42 | + <yd-search-date-range v-model="formData.createTime" label="创建时间" /> | |
| 43 | + <view class="yd-search-form-actions"> | |
| 44 | + <wd-button class="flex-1" plain @click="handleReset"> | |
| 45 | + 重置 | |
| 46 | + </wd-button> | |
| 47 | + <wd-button class="flex-1" type="primary" @click="handleSearch"> | |
| 48 | + 搜索 | |
| 49 | + </wd-button> | |
| 50 | + </view> | |
| 51 | + </view> | |
| 52 | + </wd-popup> | |
| 53 | +</template> | |
| 54 | + | |
| 55 | +<script lang="ts" setup> | |
| 56 | +import { computed, reactive, ref } from 'vue' | |
| 57 | +import { getNavbarHeight } from '@/utils' | |
| 58 | +import { formatDate, formatDateRange } from '@/utils/date' | |
| 59 | +import { getDictLabel, getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict' | |
| 60 | +import { DICT_TYPE } from '@/utils/constants' | |
| 61 | + | |
| 62 | +const emit = defineEmits<{ | |
| 63 | + search: [data: Record<string, any>] | |
| 64 | + reset: [] | |
| 65 | +}>() | |
| 66 | + | |
| 67 | +const visible = ref(false) // 搜索弹窗显示状态 | |
| 68 | +const formData = reactive({ | |
| 69 | + name: undefined as string | undefined, | |
| 70 | + birthday: undefined as string | undefined, | |
| 71 | + sex: undefined as number | undefined, | |
| 72 | + enabled: undefined as boolean | undefined, | |
| 73 | + createTime: [undefined, undefined] as [number | undefined, number | undefined], | |
| 74 | +}) // 搜索表单数据 | |
| 75 | + | |
| 76 | +/** 搜索条件 placeholder 拼接 */ | |
| 77 | +const placeholder = computed(() => { | |
| 78 | + const conditions: string[] = [] | |
| 79 | + if (formData.name) { | |
| 80 | + conditions.push(`名字:${formData.name}`) | |
| 81 | + } | |
| 82 | + if (formData.birthday) { | |
| 83 | + conditions.push(`出生日期:${formData.birthday}`) | |
| 84 | + } | |
| 85 | + if (formData.sex !== undefined) { | |
| 86 | + conditions.push(`性别:${getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, formData.sex)}`) | |
| 87 | + } | |
| 88 | + if (formData.enabled !== undefined) { | |
| 89 | + conditions.push(`是否有效:${getDictLabel(DICT_TYPE.INFRA_BOOLEAN_STRING, formData.enabled)}`) | |
| 90 | + } | |
| 91 | + if (formData.createTime?.[0] && formData.createTime?.[1]) { | |
| 92 | + conditions.push(`创建时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`) | |
| 93 | + } | |
| 94 | + return conditions.length > 0 ? conditions.join(' | ') : '搜索学生' | |
| 95 | +}) | |
| 96 | + | |
| 97 | +/** 搜索按钮操作 */ | |
| 98 | +function handleSearch() { | |
| 99 | + visible.value = false | |
| 100 | + emit('search', { | |
| 101 | + ...formData, | |
| 102 | + createTime: formatDateRange(formData.createTime), | |
| 103 | + }) | |
| 104 | +} | |
| 105 | + | |
| 106 | +/** 重置按钮操作 */ | |
| 107 | +function handleReset() { | |
| 108 | + formData.name = undefined | |
| 109 | + formData.birthday = undefined | |
| 110 | + formData.sex = undefined | |
| 111 | + formData.enabled = undefined | |
| 112 | + formData.createTime = [undefined, undefined] | |
| 113 | + visible.value = false | |
| 114 | + emit('reset') | |
| 115 | +} | |
| 116 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/student-contact/form/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar :title="getTitle" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" /> | |
| 5 | + | |
| 6 | + <!-- 表单区域 --> | |
| 7 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 8 | + <wd-cell-group border> | |
| 9 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 10 | + <wd-input v-model="formData.name" clearable placeholder="请输入名字" /> | |
| 11 | + </wd-form-item> | |
| 12 | + <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 13 | + <wd-textarea v-model="formData.description" clearable placeholder="请输入简介" /> | |
| 14 | + </wd-form-item> | |
| 15 | + <wd-form-item | |
| 16 | + title="出生日期" | |
| 17 | + title-width="180rpx" | |
| 18 | + prop="birthday" | |
| 19 | + is-link | |
| 20 | + :value="formData.birthday ? formatDateTime(formData.birthday) : ''" | |
| 21 | + placeholder="请选择出生日期" | |
| 22 | + @click="birthdayVisible = true" | |
| 23 | + /> | |
| 24 | + <wd-datetime-picker | |
| 25 | + v-model:visible="birthdayVisible" | |
| 26 | + :model-value="formData.birthday || Date.now()" | |
| 27 | + type="datetime" | |
| 28 | + @update:model-value="value => formData.birthday = value" | |
| 29 | + /> | |
| 30 | + <yd-form-picker | |
| 31 | + v-model="formData.sex" | |
| 32 | + label="性别" | |
| 33 | + label-width="180rpx" | |
| 34 | + prop="sex" | |
| 35 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 36 | + placeholder="请选择性别" | |
| 37 | + /> | |
| 38 | + <yd-form-picker | |
| 39 | + v-model="formData.enabled" | |
| 40 | + label="是否有效" | |
| 41 | + label-width="180rpx" | |
| 42 | + prop="enabled" | |
| 43 | + :dict-type="DICT_TYPE.INFRA_BOOLEAN_STRING" | |
| 44 | + placeholder="请选择是否有效" | |
| 45 | + /> | |
| 46 | + <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 47 | + <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> | |
| 48 | + </wd-form-item> | |
| 49 | + <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 50 | + <yd-upload-file v-model="formData.video" directory="infra/demo" /> | |
| 51 | + </wd-form-item> | |
| 52 | + <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 53 | + <wd-input v-model="formData.memo" clearable placeholder="请输入备注" /> | |
| 54 | + </wd-form-item> | |
| 55 | + </wd-cell-group> | |
| 56 | + </wd-form> | |
| 57 | + | |
| 58 | + <!-- 底部保存按钮 --> | |
| 59 | + <view class="yd-detail-footer"> | |
| 60 | + <wd-button type="primary" block :loading="formLoading" @click="handleSubmit"> | |
| 61 | + 保存 | |
| 62 | + </wd-button> | |
| 63 | + </view> | |
| 64 | + </view> | |
| 65 | +</template> | |
| 66 | + | |
| 67 | +<script lang="ts" setup> | |
| 68 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 69 | +import type { StudentContact } from '@/api/infra/demo' | |
| 70 | +import { computed, onMounted, ref } from 'vue' | |
| 71 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 72 | +import { createStudentContact, getStudentContact, updateStudentContact } from '@/api/infra/demo' | |
| 73 | +import { delay, navigateBackPlus } from '@/utils' | |
| 74 | +import { DICT_TYPE } from '@/utils/constants' | |
| 75 | +import { formatDateTime } from '@/utils/date' | |
| 76 | +import { createFormSchema } from '@/utils/wot' | |
| 77 | + | |
| 78 | +const props = defineProps<{ | |
| 79 | + id?: number | |
| 80 | + studentId: number | |
| 81 | +}>() | |
| 82 | + | |
| 83 | +definePage({ | |
| 84 | + style: { navigationBarTitleText: '', navigationStyle: 'custom' }, | |
| 85 | +}) | |
| 86 | + | |
| 87 | +const toast = useToast() | |
| 88 | +const getTitle = computed(() => props.id ? '编辑学生联系人' : '新增学生联系人') | |
| 89 | +const formLoading = ref(false) // 表单提交状态 | |
| 90 | +const formData = ref<StudentContact>({ | |
| 91 | + id: undefined, | |
| 92 | + studentId: props.studentId, | |
| 93 | + name: '', | |
| 94 | + description: '', | |
| 95 | + birthday: 0, | |
| 96 | + sex: 0, | |
| 97 | + enabled: false, | |
| 98 | + avatar: '', | |
| 99 | + video: undefined, | |
| 100 | + memo: '', | |
| 101 | +}) // 表单数据 | |
| 102 | +const birthdayVisible = ref(false) // 出生日期选择器状态 | |
| 103 | +const formSchema = createFormSchema({ | |
| 104 | + studentId: [{ required: true, message: '学生编号不能为空' }], | |
| 105 | + name: [{ required: true, message: '名字不能为空' }], | |
| 106 | + description: [{ required: true, message: '简介不能为空' }], | |
| 107 | + birthday: [{ required: true, message: '出生日期不能为空' }], | |
| 108 | + sex: [{ required: true, message: '性别不能为空' }], | |
| 109 | + enabled: [{ required: true, message: '是否有效不能为空' }], | |
| 110 | + avatar: [{ required: true, message: '头像不能为空' }], | |
| 111 | + memo: [{ required: true, message: '备注不能为空' }], | |
| 112 | +}) // 表单校验规则 | |
| 113 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 114 | + | |
| 115 | +/** 返回上一页 */ | |
| 116 | +function handleBack() { | |
| 117 | + navigateBackPlus() | |
| 118 | +} | |
| 119 | + | |
| 120 | +/** 加载学生联系人详情 */ | |
| 121 | +async function getDetail() { | |
| 122 | + if (props.id) { | |
| 123 | + formData.value = await getStudentContact(props.id) | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** 提交表单 */ | |
| 128 | +async function handleSubmit() { | |
| 129 | + const { valid } = await formRef.value.validate() | |
| 130 | + if (!valid) { | |
| 131 | + return | |
| 132 | + } | |
| 133 | + formLoading.value = true | |
| 134 | + try { | |
| 135 | + const data = { ...formData.value, studentId: props.studentId ?? formData.value.studentId } | |
| 136 | + if (props.id) { | |
| 137 | + await updateStudentContact(data) | |
| 138 | + toast.success('修改成功') | |
| 139 | + } else { | |
| 140 | + await createStudentContact(data) | |
| 141 | + toast.success('新增成功') | |
| 142 | + } | |
| 143 | + delay(handleBack) | |
| 144 | + } finally { | |
| 145 | + formLoading.value = false | |
| 146 | + } | |
| 147 | +} | |
| 148 | + | |
| 149 | +/** 初始化 */ | |
| 150 | +onMounted(() => { | |
| 151 | + getDetail() | |
| 152 | +}) | |
| 153 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_erp/vue/student-teacher/form/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar :title="getTitle" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" /> | |
| 5 | + | |
| 6 | + <!-- 表单区域 --> | |
| 7 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 8 | + <wd-cell-group border> | |
| 9 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 10 | + <wd-input v-model="formData.name" clearable placeholder="请输入名字" /> | |
| 11 | + </wd-form-item> | |
| 12 | + <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 13 | + <wd-textarea v-model="formData.description" clearable placeholder="请输入简介" /> | |
| 14 | + </wd-form-item> | |
| 15 | + <wd-form-item | |
| 16 | + title="出生日期" | |
| 17 | + title-width="180rpx" | |
| 18 | + prop="birthday" | |
| 19 | + is-link | |
| 20 | + :value="formData.birthday ? formatDateTime(formData.birthday) : ''" | |
| 21 | + placeholder="请选择出生日期" | |
| 22 | + @click="birthdayVisible = true" | |
| 23 | + /> | |
| 24 | + <wd-datetime-picker | |
| 25 | + v-model:visible="birthdayVisible" | |
| 26 | + :model-value="formData.birthday || Date.now()" | |
| 27 | + type="datetime" | |
| 28 | + @update:model-value="value => formData.birthday = value" | |
| 29 | + /> | |
| 30 | + <yd-form-picker | |
| 31 | + v-model="formData.sex" | |
| 32 | + label="性别" | |
| 33 | + label-width="180rpx" | |
| 34 | + prop="sex" | |
| 35 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 36 | + placeholder="请选择性别" | |
| 37 | + /> | |
| 38 | + <yd-form-picker | |
| 39 | + v-model="formData.enabled" | |
| 40 | + label="是否有效" | |
| 41 | + label-width="180rpx" | |
| 42 | + prop="enabled" | |
| 43 | + :dict-type="DICT_TYPE.INFRA_BOOLEAN_STRING" | |
| 44 | + placeholder="请选择是否有效" | |
| 45 | + /> | |
| 46 | + <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 47 | + <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> | |
| 48 | + </wd-form-item> | |
| 49 | + <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 50 | + <yd-upload-file v-model="formData.video" directory="infra/demo" /> | |
| 51 | + </wd-form-item> | |
| 52 | + <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 53 | + <wd-input v-model="formData.memo" clearable placeholder="请输入备注" /> | |
| 54 | + </wd-form-item> | |
| 55 | + </wd-cell-group> | |
| 56 | + </wd-form> | |
| 57 | + | |
| 58 | + <!-- 底部保存按钮 --> | |
| 59 | + <view class="yd-detail-footer"> | |
| 60 | + <wd-button type="primary" block :loading="formLoading" @click="handleSubmit"> | |
| 61 | + 保存 | |
| 62 | + </wd-button> | |
| 63 | + </view> | |
| 64 | + </view> | |
| 65 | +</template> | |
| 66 | + | |
| 67 | +<script lang="ts" setup> | |
| 68 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 69 | +import type { StudentTeacher } from '@/api/infra/demo' | |
| 70 | +import { computed, onMounted, ref } from 'vue' | |
| 71 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 72 | +import { createStudentTeacher, getStudentTeacher, updateStudentTeacher } from '@/api/infra/demo' | |
| 73 | +import { delay, navigateBackPlus } from '@/utils' | |
| 74 | +import { DICT_TYPE } from '@/utils/constants' | |
| 75 | +import { formatDateTime } from '@/utils/date' | |
| 76 | +import { createFormSchema } from '@/utils/wot' | |
| 77 | + | |
| 78 | +const props = defineProps<{ | |
| 79 | + id?: number | |
| 80 | + studentId: number | |
| 81 | +}>() | |
| 82 | + | |
| 83 | +definePage({ | |
| 84 | + style: { navigationBarTitleText: '', navigationStyle: 'custom' }, | |
| 85 | +}) | |
| 86 | + | |
| 87 | +const toast = useToast() | |
| 88 | +const getTitle = computed(() => props.id ? '编辑学生班主任' : '新增学生班主任') | |
| 89 | +const formLoading = ref(false) // 表单提交状态 | |
| 90 | +const formData = ref<StudentTeacher>({ | |
| 91 | + id: undefined, | |
| 92 | + studentId: props.studentId, | |
| 93 | + name: '', | |
| 94 | + description: '', | |
| 95 | + birthday: 0, | |
| 96 | + sex: 0, | |
| 97 | + enabled: false, | |
| 98 | + avatar: '', | |
| 99 | + video: undefined, | |
| 100 | + memo: '', | |
| 101 | +}) // 表单数据 | |
| 102 | +const birthdayVisible = ref(false) // 出生日期选择器状态 | |
| 103 | +const formSchema = createFormSchema({ | |
| 104 | + studentId: [{ required: true, message: '学生编号不能为空' }], | |
| 105 | + name: [{ required: true, message: '名字不能为空' }], | |
| 106 | + description: [{ required: true, message: '简介不能为空' }], | |
| 107 | + birthday: [{ required: true, message: '出生日期不能为空' }], | |
| 108 | + sex: [{ required: true, message: '性别不能为空' }], | |
| 109 | + enabled: [{ required: true, message: '是否有效不能为空' }], | |
| 110 | + avatar: [{ required: true, message: '头像不能为空' }], | |
| 111 | + memo: [{ required: true, message: '备注不能为空' }], | |
| 112 | +}) // 表单校验规则 | |
| 113 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 114 | + | |
| 115 | +/** 返回上一页 */ | |
| 116 | +function handleBack() { | |
| 117 | + navigateBackPlus() | |
| 118 | +} | |
| 119 | + | |
| 120 | +/** 加载学生班主任详情 */ | |
| 121 | +async function getDetail() { | |
| 122 | + if (props.id) { | |
| 123 | + formData.value = await getStudentTeacher(props.id) | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** 提交表单 */ | |
| 128 | +async function handleSubmit() { | |
| 129 | + const { valid } = await formRef.value.validate() | |
| 130 | + if (!valid) { | |
| 131 | + return | |
| 132 | + } | |
| 133 | + formLoading.value = true | |
| 134 | + try { | |
| 135 | + const data = { ...formData.value, studentId: props.studentId ?? formData.value.studentId } | |
| 136 | + if (props.id) { | |
| 137 | + await updateStudentTeacher(data) | |
| 138 | + toast.success('修改成功') | |
| 139 | + } else { | |
| 140 | + await createStudentTeacher(data) | |
| 141 | + toast.success('新增成功') | |
| 142 | + } | |
| 143 | + delay(handleBack) | |
| 144 | + } finally { | |
| 145 | + formLoading.value = false | |
| 146 | + } | |
| 147 | +} | |
| 148 | + | |
| 149 | +/** 初始化 */ | |
| 150 | +onMounted(() => { | |
| 151 | + getDetail() | |
| 152 | +}) | |
| 153 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 13 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/assert.json
0 → 100644
| 1 | +[ { | |
| 2 | + "contentPath" : "java/InfraStudentPageReqVO", | |
| 3 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" | |
| 4 | +}, { | |
| 5 | + "contentPath" : "java/InfraStudentRespVO", | |
| 6 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" | |
| 7 | +}, { | |
| 8 | + "contentPath" : "java/InfraStudentSaveReqVO", | |
| 9 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" | |
| 10 | +}, { | |
| 11 | + "contentPath" : "java/InfraStudentController", | |
| 12 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" | |
| 13 | +}, { | |
| 14 | + "contentPath" : "java/InfraStudentDO", | |
| 15 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" | |
| 16 | +}, { | |
| 17 | + "contentPath" : "java/InfraStudentContactDO", | |
| 18 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" | |
| 19 | +}, { | |
| 20 | + "contentPath" : "java/InfraStudentTeacherDO", | |
| 21 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" | |
| 22 | +}, { | |
| 23 | + "contentPath" : "java/InfraStudentMapper", | |
| 24 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" | |
| 25 | +}, { | |
| 26 | + "contentPath" : "java/InfraStudentContactMapper", | |
| 27 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" | |
| 28 | +}, { | |
| 29 | + "contentPath" : "java/InfraStudentTeacherMapper", | |
| 30 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" | |
| 31 | +}, { | |
| 32 | + "contentPath" : "xml/InfraStudentMapper", | |
| 33 | + "filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml" | |
| 34 | +}, { | |
| 35 | + "contentPath" : "java/InfraStudentServiceImpl", | |
| 36 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" | |
| 37 | +}, { | |
| 38 | + "contentPath" : "java/InfraStudentService", | |
| 39 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" | |
| 40 | +}, { | |
| 41 | + "contentPath" : "java/InfraStudentServiceImplTest", | |
| 42 | + "filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" | |
| 43 | +}, { | |
| 44 | + "contentPath" : "java/ErrorCodeConstants_手动操作", | |
| 45 | + "filePath" : "yudao-module-infra/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" : "ts/index", | |
| 54 | + "filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts" | |
| 55 | +}, { | |
| 56 | + "contentPath" : "vue/index", | |
| 57 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue" | |
| 58 | +}, { | |
| 59 | + "contentPath" : "vue/search-form", | |
| 60 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue" | |
| 61 | +}, { | |
| 62 | + "contentPath" : "vue/form/index", | |
| 63 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue" | |
| 64 | +}, { | |
| 65 | + "contentPath" : "vue/detail/index", | |
| 66 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue" | |
| 67 | +} ] | |
| 0 | 68 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/java/ErrorCodeConstants_手动操作
0 → 100644
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 72 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + default int deleteByStudentIds(List<Long> studentIds) { | |
| 29 | + return deleteBatch(InfraStudentContactDO::getStudentId, studentIds); | |
| 30 | + } | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.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 jakarta.validation.constraints.*; | |
| 12 | +import jakarta.validation.*; | |
| 13 | +import jakarta.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.apilog.core.annotation.ApiAccessLog; | |
| 26 | +import static cn.iocoder.yudao.framework.apilog.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 | + @DeleteMapping("/delete-list") | |
| 68 | + @Parameter(name = "ids", description = "编号", required = true) | |
| 69 | + @Operation(summary = "批量删除学生") | |
| 70 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 71 | + public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) { | |
| 72 | + studentService.deleteStudentListByIds(ids); | |
| 73 | + return success(true); | |
| 74 | + } | |
| 75 | + | |
| 76 | + @GetMapping("/get") | |
| 77 | + @Operation(summary = "获得学生") | |
| 78 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 79 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 80 | + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { | |
| 81 | + InfraStudentDO student = studentService.getStudent(id); | |
| 82 | + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @GetMapping("/page") | |
| 86 | + @Operation(summary = "获得学生分页") | |
| 87 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 88 | + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { | |
| 89 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); | |
| 90 | + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); | |
| 91 | + } | |
| 92 | + | |
| 93 | + @GetMapping("/export-excel") | |
| 94 | + @Operation(summary = "导出学生 Excel") | |
| 95 | + @PreAuthorize("@ss.hasPermission('infra:student:export')") | |
| 96 | + @ApiAccessLog(operateType = EXPORT) | |
| 97 | + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, | |
| 98 | + HttpServletResponse response) throws IOException { | |
| 99 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 100 | + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); | |
| 101 | + // 导出 Excel | |
| 102 | + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, | |
| 103 | + BeanUtils.toBean(list, InfraStudentRespVO.class)); | |
| 104 | + } | |
| 105 | + | |
| 106 | + // ==================== 子表(学生联系人) ==================== | |
| 107 | + | |
| 108 | + @GetMapping("/student-contact/list-by-student-id") | |
| 109 | + @Operation(summary = "获得学生联系人列表") | |
| 110 | + @Parameter(name = "studentId", description = "学生编号") | |
| 111 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 112 | + public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { | |
| 113 | + return success(studentService.getStudentContactListByStudentId(studentId)); | |
| 114 | + } | |
| 115 | + | |
| 116 | + // ==================== 子表(学生班主任) ==================== | |
| 117 | + | |
| 118 | + @GetMapping("/student-teacher/get-by-student-id") | |
| 119 | + @Operation(summary = "获得学生班主任") | |
| 120 | + @Parameter(name = "studentId", description = "学生编号") | |
| 121 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 122 | + public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { | |
| 123 | + return success(studentService.getStudentTeacherByStudentId(studentId)); | |
| 124 | + } | |
| 125 | + | |
| 126 | +} | |
| 0 | 127 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 68 | +} | |
| 0 | 69 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 31 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +public class InfraStudentPageReqVO extends PageParam { | |
| 15 | + | |
| 16 | + @Schema(description = "名字", example = "芋头") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "出生日期") | |
| 20 | + private LocalDateTime birthday; | |
| 21 | + | |
| 22 | + @Schema(description = "性别", example = "1") | |
| 23 | + private Integer sex; | |
| 24 | + | |
| 25 | + @Schema(description = "是否有效", example = "true") | |
| 26 | + private Boolean enabled; | |
| 27 | + | |
| 28 | + @Schema(description = "创建时间") | |
| 29 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 30 | + private LocalDateTime[] createTime; | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 org.springframework.format.annotation.DateTimeFormat; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import cn.idev.excel.annotation.*; | |
| 9 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 10 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生 Response VO") | |
| 13 | +@Data | |
| 14 | +@ExcelIgnoreUnannotated | |
| 15 | +public class InfraStudentRespVO { | |
| 16 | + | |
| 17 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @ExcelProperty("编号") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 22 | + @ExcelProperty("名字") | |
| 23 | + private String name; | |
| 24 | + | |
| 25 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 26 | + @ExcelProperty("简介") | |
| 27 | + private String description; | |
| 28 | + | |
| 29 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 30 | + @ExcelProperty("出生日期") | |
| 31 | + private LocalDateTime birthday; | |
| 32 | + | |
| 33 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 34 | + @ExcelProperty(value = "性别", converter = DictConvert.class) | |
| 35 | + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 | |
| 36 | + private Integer sex; | |
| 37 | + | |
| 38 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 39 | + @ExcelProperty(value = "是否有效", converter = DictConvert.class) | |
| 40 | + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 | |
| 41 | + private Boolean enabled; | |
| 42 | + | |
| 43 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 44 | + @ExcelProperty("头像") | |
| 45 | + private String avatar; | |
| 46 | + | |
| 47 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 48 | + @ExcelProperty("附件") | |
| 49 | + private String video; | |
| 50 | + | |
| 51 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 52 | + @ExcelProperty("备注") | |
| 53 | + private String memo; | |
| 54 | + | |
| 55 | + @Schema(description = "创建时间") | |
| 56 | + @ExcelProperty("创建时间") | |
| 57 | + private LocalDateTime createTime; | |
| 58 | + | |
| 59 | +} | |
| 0 | 60 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.validation.constraints.*; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; | |
| 10 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生新增/修改 Request VO") | |
| 13 | +@Data | |
| 14 | +public class InfraStudentSaveReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 20 | + @NotEmpty(message = "名字不能为空") | |
| 21 | + private String name; | |
| 22 | + | |
| 23 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 24 | + @NotEmpty(message = "简介不能为空") | |
| 25 | + private String description; | |
| 26 | + | |
| 27 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 28 | + @NotNull(message = "出生日期不能为空") | |
| 29 | + private LocalDateTime birthday; | |
| 30 | + | |
| 31 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 32 | + @NotNull(message = "性别不能为空") | |
| 33 | + private Integer sex; | |
| 34 | + | |
| 35 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 36 | + @NotNull(message = "是否有效不能为空") | |
| 37 | + private Boolean enabled; | |
| 38 | + | |
| 39 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 40 | + @NotEmpty(message = "头像不能为空") | |
| 41 | + private String avatar; | |
| 42 | + | |
| 43 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 44 | + @NotEmpty(message = "附件不能为空") | |
| 45 | + private String video; | |
| 46 | + | |
| 47 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 48 | + @NotEmpty(message = "备注不能为空") | |
| 49 | + private String memo; | |
| 50 | + | |
| 51 | + @Schema(description = "学生联系人列表") | |
| 52 | + private List<InfraStudentContactDO> studentContacts; | |
| 53 | + | |
| 54 | + @Schema(description = "学生班主任") | |
| 55 | + private InfraStudentTeacherDO studentTeacher; | |
| 56 | + | |
| 57 | +} | |
| 0 | 58 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/java/InfraStudentService
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | +import jakarta.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 ids 编号 | |
| 45 | + */ | |
| 46 | + void deleteStudentListByIds(List<Long> ids); | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 获得学生 | |
| 50 | + * | |
| 51 | + * @param id 编号 | |
| 52 | + * @return 学生 | |
| 53 | + */ | |
| 54 | + InfraStudentDO getStudent(Long id); | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 获得学生分页 | |
| 58 | + * | |
| 59 | + * @param pageReqVO 分页查询 | |
| 60 | + * @return 学生分页 | |
| 61 | + */ | |
| 62 | + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO); | |
| 63 | + | |
| 64 | + // ==================== 子表(学生联系人) ==================== | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 获得学生联系人列表 | |
| 68 | + * | |
| 69 | + * @param studentId 学生编号 | |
| 70 | + * @return 学生联系人列表 | |
| 71 | + */ | |
| 72 | + List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId); | |
| 73 | + | |
| 74 | + // ==================== 子表(学生班主任) ==================== | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * 获得学生班主任 | |
| 78 | + * | |
| 79 | + * @param studentId 学生编号 | |
| 80 | + * @return 学生班主任 | |
| 81 | + */ | |
| 82 | + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); | |
| 83 | + | |
| 84 | +} | |
| 0 | 85 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/java/InfraStudentServiceImpl
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | +import org.springframework.stereotype.Service; | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | +import org.springframework.validation.annotation.Validated; | |
| 8 | +import org.springframework.transaction.annotation.Transactional; | |
| 9 | + | |
| 10 | +import java.util.*; | |
| 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.dataobject.demo.InfraStudentContactDO; | |
| 14 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; | |
| 15 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 16 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 17 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 18 | + | |
| 19 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; | |
| 20 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; | |
| 21 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; | |
| 22 | + | |
| 23 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; | |
| 26 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * 学生 Service 实现类 | |
| 30 | + * | |
| 31 | + * @author 芋道源码 | |
| 32 | + */ | |
| 33 | +@Service | |
| 34 | +@Validated | |
| 35 | +public class InfraStudentServiceImpl implements InfraStudentService { | |
| 36 | + | |
| 37 | + @Resource | |
| 38 | + private InfraStudentMapper studentMapper; | |
| 39 | + @Resource | |
| 40 | + private InfraStudentContactMapper studentContactMapper; | |
| 41 | + @Resource | |
| 42 | + private InfraStudentTeacherMapper studentTeacherMapper; | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + @Transactional(rollbackFor = Exception.class) | |
| 46 | + public Long createStudent(InfraStudentSaveReqVO createReqVO) { | |
| 47 | + // 插入 | |
| 48 | + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); | |
| 49 | + studentMapper.insert(student); | |
| 50 | + | |
| 51 | + | |
| 52 | + // 插入子表 | |
| 53 | + createStudentContactList(student.getId(), createReqVO.getStudentContacts()); | |
| 54 | + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); | |
| 55 | + // 返回 | |
| 56 | + return student.getId(); | |
| 57 | + } | |
| 58 | + | |
| 59 | + @Override | |
| 60 | + @Transactional(rollbackFor = Exception.class) | |
| 61 | + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { | |
| 62 | + // 校验存在 | |
| 63 | + validateStudentExists(updateReqVO.getId()); | |
| 64 | + // 更新 | |
| 65 | + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); | |
| 66 | + studentMapper.updateById(updateObj); | |
| 67 | + | |
| 68 | + // 更新子表 | |
| 69 | + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); | |
| 70 | + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); | |
| 71 | + } | |
| 72 | + | |
| 73 | + @Override | |
| 74 | + @Transactional(rollbackFor = Exception.class) | |
| 75 | + public void deleteStudent(Long id) { | |
| 76 | + // 校验存在 | |
| 77 | + validateStudentExists(id); | |
| 78 | + // 删除 | |
| 79 | + studentMapper.deleteById(id); | |
| 80 | + | |
| 81 | + // 删除子表 | |
| 82 | + deleteStudentContactByStudentId(id); | |
| 83 | + deleteStudentTeacherByStudentId(id); | |
| 84 | + } | |
| 85 | + | |
| 86 | + @Override | |
| 87 | + @Transactional(rollbackFor = Exception.class) | |
| 88 | + public void deleteStudentListByIds(List<Long> ids) { | |
| 89 | + // 删除 | |
| 90 | + studentMapper.deleteByIds(ids); | |
| 91 | + | |
| 92 | + // 删除子表 | |
| 93 | + deleteStudentContactByStudentIds(ids); | |
| 94 | + deleteStudentTeacherByStudentIds(ids); | |
| 95 | + } | |
| 96 | + | |
| 97 | + | |
| 98 | + private void validateStudentExists(Long id) { | |
| 99 | + if (studentMapper.selectById(id) == null) { | |
| 100 | + throw exception(STUDENT_NOT_EXISTS); | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + @Override | |
| 105 | + public InfraStudentDO getStudent(Long id) { | |
| 106 | + return studentMapper.selectById(id); | |
| 107 | + } | |
| 108 | + | |
| 109 | + @Override | |
| 110 | + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) { | |
| 111 | + return studentMapper.selectPage(pageReqVO); | |
| 112 | + } | |
| 113 | + | |
| 114 | + // ==================== 子表(学生联系人) ==================== | |
| 115 | + | |
| 116 | + @Override | |
| 117 | + public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) { | |
| 118 | + return studentContactMapper.selectListByStudentId(studentId); | |
| 119 | + } | |
| 120 | + | |
| 121 | + private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) { | |
| 122 | + if (CollUtil.isEmpty(list)) { | |
| 123 | + return; | |
| 124 | + } | |
| 125 | + list.forEach(o -> o.setStudentId(studentId).clean()); | |
| 126 | + studentContactMapper.insertBatch(list); | |
| 127 | + } | |
| 128 | + | |
| 129 | + private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) { | |
| 130 | + list.forEach(o -> o.setStudentId(studentId).clean()); | |
| 131 | + List<InfraStudentContactDO> oldList = studentContactMapper.selectListByStudentId(studentId); | |
| 132 | + List<List<InfraStudentContactDO>> diffList = diffList(oldList, list, (oldVal, newVal) -> { | |
| 133 | + boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId()); | |
| 134 | + if (same) { | |
| 135 | + newVal.setId(oldVal.getId()).clean(); // 解决更新情况下:updateTime 不更新 | |
| 136 | + } | |
| 137 | + return same; | |
| 138 | + }); | |
| 139 | + | |
| 140 | + // 第二步,批量添加、修改、删除 | |
| 141 | + if (CollUtil.isNotEmpty(diffList.get(0))) { | |
| 142 | + studentContactMapper.insertBatch(diffList.get(0)); | |
| 143 | + } | |
| 144 | + if (CollUtil.isNotEmpty(diffList.get(1))) { | |
| 145 | + studentContactMapper.updateBatch(diffList.get(1)); | |
| 146 | + } | |
| 147 | + if (CollUtil.isNotEmpty(diffList.get(2))) { | |
| 148 | + studentContactMapper.deleteByIds(convertList(diffList.get(2), InfraStudentContactDO::getId)); | |
| 149 | + } | |
| 150 | + } | |
| 151 | + | |
| 152 | + private void deleteStudentContactByStudentId(Long studentId) { | |
| 153 | + studentContactMapper.deleteByStudentId(studentId); | |
| 154 | + } | |
| 155 | + | |
| 156 | + private void deleteStudentContactByStudentIds(List<Long> studentIds) { | |
| 157 | + studentContactMapper.deleteByStudentIds(studentIds); | |
| 158 | + } | |
| 159 | + | |
| 160 | + // ==================== 子表(学生班主任) ==================== | |
| 161 | + | |
| 162 | + @Override | |
| 163 | + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { | |
| 164 | + return studentTeacherMapper.selectByStudentId(studentId); | |
| 165 | + } | |
| 166 | + | |
| 167 | + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { | |
| 168 | + if (studentTeacher == null) { | |
| 169 | + return; | |
| 170 | + } | |
| 171 | + studentTeacher.setStudentId(studentId); | |
| 172 | + studentTeacherMapper.insert(studentTeacher); | |
| 173 | + } | |
| 174 | + | |
| 175 | + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { | |
| 176 | + if (studentTeacher == null) { | |
| 177 | + return; | |
| 178 | + } | |
| 179 | + studentTeacher.setStudentId(studentId).clean();// 解决更新情况下:updateTime 不更新 | |
| 180 | + studentTeacherMapper.insertOrUpdate(studentTeacher); | |
| 181 | + } | |
| 182 | + | |
| 183 | + private void deleteStudentTeacherByStudentId(Long studentId) { | |
| 184 | + studentTeacherMapper.deleteByStudentId(studentId); | |
| 185 | + } | |
| 186 | + | |
| 187 | + private void deleteStudentTeacherByStudentIds(List<Long> studentIds) { | |
| 188 | + studentTeacherMapper.deleteByStudentIds(studentIds); | |
| 189 | + } | |
| 190 | + | |
| 191 | +} | |
| 0 | 192 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | + | |
| 8 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 9 | + | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; | |
| 12 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; | |
| 13 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 14 | + | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import org.springframework.context.annotation.Import; | |
| 17 | +import java.util.*; | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | + | |
| 20 | +import static cn.hutool.core.util.RandomUtil.*; | |
| 21 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 22 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; | |
| 23 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; | |
| 26 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; | |
| 27 | +import static org.junit.jupiter.api.Assertions.*; | |
| 28 | +import static org.mockito.Mockito.*; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + * {@link InfraStudentServiceImpl} 的单元测试类 | |
| 32 | + * | |
| 33 | + * @author 芋道源码 | |
| 34 | + */ | |
| 35 | +@Import(InfraStudentServiceImpl.class) | |
| 36 | +public class InfraStudentServiceImplTest extends BaseDbUnitTest { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private InfraStudentServiceImpl studentService; | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private InfraStudentMapper studentMapper; | |
| 43 | + | |
| 44 | + @Test | |
| 45 | + public void testCreateStudent_success() { | |
| 46 | + // 准备参数 | |
| 47 | + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); | |
| 48 | + | |
| 49 | + // 调用 | |
| 50 | + Long studentId = studentService.createStudent(createReqVO); | |
| 51 | + // 断言 | |
| 52 | + assertNotNull(studentId); | |
| 53 | + // 校验记录的属性是否正确 | |
| 54 | + InfraStudentDO student = studentMapper.selectById(studentId); | |
| 55 | + assertPojoEquals(createReqVO, student, "id"); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Test | |
| 59 | + public void testUpdateStudent_success() { | |
| 60 | + // mock 数据 | |
| 61 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); | |
| 62 | + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 | |
| 63 | + // 准备参数 | |
| 64 | + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { | |
| 65 | + o.setId(dbStudent.getId()); // 设置更新的 ID | |
| 66 | + }); | |
| 67 | + | |
| 68 | + // 调用 | |
| 69 | + studentService.updateStudent(updateReqVO); | |
| 70 | + // 校验是否更新正确 | |
| 71 | + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 | |
| 72 | + assertPojoEquals(updateReqVO, student); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Test | |
| 76 | + public void testUpdateStudent_notExists() { | |
| 77 | + // 准备参数 | |
| 78 | + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); | |
| 79 | + | |
| 80 | + // 调用, 并断言异常 | |
| 81 | + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Test | |
| 85 | + public void testDeleteStudent_success() { | |
| 86 | + // mock 数据 | |
| 87 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); | |
| 88 | + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 | |
| 89 | + // 准备参数 | |
| 90 | + Long id = dbStudent.getId(); | |
| 91 | + | |
| 92 | + // 调用 | |
| 93 | + studentService.deleteStudent(id); | |
| 94 | + // 校验数据不存在了 | |
| 95 | + assertNull(studentMapper.selectById(id)); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Test | |
| 99 | + public void testDeleteStudent_notExists() { | |
| 100 | + // 准备参数 | |
| 101 | + Long id = randomLongId(); | |
| 102 | + | |
| 103 | + // 调用, 并断言异常 | |
| 104 | + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); | |
| 105 | + } | |
| 106 | + | |
| 107 | + @Test | |
| 108 | + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 109 | + public void testGetStudentPage() { | |
| 110 | + // mock 数据 | |
| 111 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 | |
| 112 | + o.setName(null); | |
| 113 | + o.setBirthday(null); | |
| 114 | + o.setSex(null); | |
| 115 | + o.setEnabled(null); | |
| 116 | + o.setCreateTime(null); | |
| 117 | + }); | |
| 118 | + studentMapper.insert(dbStudent); | |
| 119 | + // 测试 name 不匹配 | |
| 120 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); | |
| 121 | + // 测试 birthday 不匹配 | |
| 122 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); | |
| 123 | + // 测试 sex 不匹配 | |
| 124 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); | |
| 125 | + // 测试 enabled 不匹配 | |
| 126 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); | |
| 127 | + // 测试 createTime 不匹配 | |
| 128 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); | |
| 129 | + // 准备参数 | |
| 130 | + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); | |
| 131 | + reqVO.setName(null); | |
| 132 | + reqVO.setBirthday(null); | |
| 133 | + reqVO.setSex(null); | |
| 134 | + reqVO.setEnabled(null); | |
| 135 | + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | |
| 136 | + | |
| 137 | + // 调用 | |
| 138 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO); | |
| 139 | + // 断言 | |
| 140 | + assertEquals(1, pageResult.getTotal()); | |
| 141 | + assertEquals(1, pageResult.getList().size()); | |
| 142 | + assertPojoEquals(dbStudent, pageResult.getList().get(0)); | |
| 143 | + } | |
| 144 | + | |
| 145 | +} | |
| 0 | 146 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 72 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + default int deleteByStudentIds(List<Long> studentIds) { | |
| 29 | + return deleteBatch(InfraStudentTeacherDO::getStudentId, studentIds); | |
| 30 | + } | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 18 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +SELECT @parentId := LAST_INSERT_ID(); | |
| 13 | + | |
| 14 | +-- 按钮 SQL | |
| 15 | +INSERT INTO system_menu( | |
| 16 | + name, permission, type, sort, parent_id, | |
| 17 | + path, icon, component, status | |
| 18 | +) | |
| 19 | +VALUES ( | |
| 20 | + '学生查询', 'infra:student:query', 3, 1, @parentId, | |
| 21 | + '', '', '', 0 | |
| 22 | +); | |
| 23 | +INSERT INTO system_menu( | |
| 24 | + name, permission, type, sort, parent_id, | |
| 25 | + path, icon, component, status | |
| 26 | +) | |
| 27 | +VALUES ( | |
| 28 | + '学生创建', 'infra:student:create', 3, 2, @parentId, | |
| 29 | + '', '', '', 0 | |
| 30 | +); | |
| 31 | +INSERT INTO system_menu( | |
| 32 | + name, permission, type, sort, parent_id, | |
| 33 | + path, icon, component, status | |
| 34 | +) | |
| 35 | +VALUES ( | |
| 36 | + '学生更新', 'infra:student:update', 3, 3, @parentId, | |
| 37 | + '', '', '', 0 | |
| 38 | +); | |
| 39 | +INSERT INTO system_menu( | |
| 40 | + name, permission, type, sort, parent_id, | |
| 41 | + path, icon, component, status | |
| 42 | +) | |
| 43 | +VALUES ( | |
| 44 | + '学生删除', 'infra:student:delete', 3, 4, @parentId, | |
| 45 | + '', '', '', 0 | |
| 46 | +); | |
| 47 | +INSERT INTO system_menu( | |
| 48 | + name, permission, type, sort, parent_id, | |
| 49 | + path, icon, component, status | |
| 50 | +) | |
| 51 | +VALUES ( | |
| 52 | + '学生导出', 'infra:student:export', 3, 5, @parentId, | |
| 53 | + '', '', '', 0 | |
| 54 | +); | |
| 0 | 55 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/ts/index
0 → 100644
| 1 | +import type { PageParam, PageResult } from '@/http/types' | |
| 2 | +import { http } from '@/http/http' | |
| 3 | + | |
| 4 | +/** 学生联系人信息 */ | |
| 5 | +export interface StudentContact { | |
| 6 | + id?: number | |
| 7 | + studentId?: number | |
| 8 | + name: string | |
| 9 | + description: string | |
| 10 | + birthday: number | |
| 11 | + sex: number | |
| 12 | + enabled: boolean | |
| 13 | + avatar: string | |
| 14 | + video?: string | |
| 15 | + memo: string | |
| 16 | + createTime?: number | |
| 17 | +} | |
| 18 | + | |
| 19 | +/** 学生班主任信息 */ | |
| 20 | +export interface StudentTeacher { | |
| 21 | + id?: number | |
| 22 | + studentId?: number | |
| 23 | + name: string | |
| 24 | + description: string | |
| 25 | + birthday: number | |
| 26 | + sex: number | |
| 27 | + enabled: boolean | |
| 28 | + avatar: string | |
| 29 | + video?: string | |
| 30 | + memo: string | |
| 31 | + createTime?: number | |
| 32 | +} | |
| 33 | + | |
| 34 | +/** 学生信息 */ | |
| 35 | +export interface Student { | |
| 36 | + id?: number | |
| 37 | + name: string | |
| 38 | + description: string | |
| 39 | + birthday: number | |
| 40 | + sex: number | |
| 41 | + enabled: boolean | |
| 42 | + avatar: string | |
| 43 | + video: string | |
| 44 | + memo: string | |
| 45 | + createTime?: number | |
| 46 | + studentContacts?: StudentContact[] | |
| 47 | + studentTeacher?: StudentTeacher | null | |
| 48 | +} | |
| 49 | + | |
| 50 | +/** 获取学生分页列表 */ | |
| 51 | +export function getStudentPage(params: PageParam) { | |
| 52 | + return http.get<PageResult<Student>>('/infra/student/page', params) | |
| 53 | +} | |
| 54 | + | |
| 55 | +/** 获取学生详情 */ | |
| 56 | +export function getStudent(id: number) { | |
| 57 | + return http.get<Student>(`/infra/student/get?id=${id}`) | |
| 58 | +} | |
| 59 | + | |
| 60 | +/** 创建学生 */ | |
| 61 | +export function createStudent(data: Student) { | |
| 62 | + return http.post<number>('/infra/student/create', data) | |
| 63 | +} | |
| 64 | + | |
| 65 | +/** 更新学生 */ | |
| 66 | +export function updateStudent(data: Student) { | |
| 67 | + return http.put<boolean>('/infra/student/update', data) | |
| 68 | +} | |
| 69 | + | |
| 70 | +/** 删除学生 */ | |
| 71 | +export function deleteStudent(id: number) { | |
| 72 | + return http.delete<boolean>(`/infra/student/delete?id=${id}`) | |
| 73 | +} | |
| 74 | + | |
| 75 | +// ==================== 子表:学生联系人 ==================== | |
| 76 | + | |
| 77 | +/** 获取学生联系人列表 */ | |
| 78 | +export function getStudentContactListByStudentId(studentId: number) { | |
| 79 | + return http.get<StudentContact[]>(`/infra/student/student-contact/list-by-student-id?studentId=${studentId}`) | |
| 80 | +} | |
| 81 | + | |
| 82 | +// ==================== 子表:学生班主任 ==================== | |
| 83 | + | |
| 84 | +/** 获取学生班主任 */ | |
| 85 | +export function getStudentTeacherByStudentId(studentId: number) { | |
| 86 | + return http.get<StudentTeacher | null>(`/infra/student/student-teacher/get-by-student-id?studentId=${studentId}`) | |
| 87 | +} | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/vue/detail/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="学生详情" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <view class="pb-160rpx"> | |
| 11 | + <!-- 主表详情 --> | |
| 12 | + <wd-cell-group border title="学生信息"> | |
| 13 | + <wd-cell title="编号" :value="formData?.id ?? '-'" /> | |
| 14 | + <wd-cell title="名字" :value="formData?.name ?? '-'" /> | |
| 15 | + <wd-cell title="简介" :value="formData?.description ?? '-'" /> | |
| 16 | + <wd-cell title="出生日期" :value="formatDateTime(formData?.birthday) || '-'" /> | |
| 17 | + <wd-cell title="性别"> | |
| 18 | + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="formData?.sex" /> | |
| 19 | + </wd-cell> | |
| 20 | + <wd-cell title="是否有效"> | |
| 21 | + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="formData?.enabled" /> | |
| 22 | + </wd-cell> | |
| 23 | + <wd-cell title="头像" :value="formData?.avatar ?? '-'" /> | |
| 24 | + <wd-cell title="附件" :value="formData?.video ?? '-'" /> | |
| 25 | + <wd-cell title="备注" :value="formData?.memo ?? '-'" /> | |
| 26 | + <wd-cell title="创建时间" :value="formatDateTime(formData?.createTime) || '-'" /> | |
| 27 | + </wd-cell-group> | |
| 28 | + | |
| 29 | + <!-- 子表:学生联系人 --> | |
| 30 | + <view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]"> | |
| 31 | + 学生联系人({{ studentContacts.length }}) | |
| 32 | + </view> | |
| 33 | + <view class="px-24rpx"> | |
| 34 | + <view v-if="!studentContacts.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 35 | + 暂无学生联系人 | |
| 36 | + </view> | |
| 37 | + <view | |
| 38 | + v-for="item in studentContacts" | |
| 39 | + :key="item.id" | |
| 40 | + class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 41 | + > | |
| 42 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 43 | + 学生编号:{{ item.studentId ?? '-' }} | |
| 44 | + </view> | |
| 45 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 46 | + 名字:{{ item.name ?? '-' }} | |
| 47 | + </view> | |
| 48 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 49 | + 简介:{{ item.description ?? '-' }} | |
| 50 | + </view> | |
| 51 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 52 | + 出生日期:{{ item.birthday ?? '-' }} | |
| 53 | + </view> | |
| 54 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 55 | + 性别:{{ item.sex ?? '-' }} | |
| 56 | + </view> | |
| 57 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 58 | + 是否有效:{{ item.enabled ?? '-' }} | |
| 59 | + </view> | |
| 60 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 61 | + 头像:{{ item.avatar ?? '-' }} | |
| 62 | + </view> | |
| 63 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 64 | + 附件:{{ item.video ?? '-' }} | |
| 65 | + </view> | |
| 66 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 67 | + 备注:{{ item.memo ?? '-' }} | |
| 68 | + </view> | |
| 69 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 70 | + 创建时间:{{ item.createTime ?? '-' }} | |
| 71 | + </view> | |
| 72 | + </view> | |
| 73 | + </view> | |
| 74 | + <!-- 子表:学生班主任 --> | |
| 75 | + <view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]"> | |
| 76 | + 学生班主任 | |
| 77 | + </view> | |
| 78 | + <view class="px-24rpx"> | |
| 79 | + <view class="rounded-12rpx bg-white p-24rpx shadow-sm"> | |
| 80 | + <template v-if="studentTeacher"> | |
| 81 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 82 | + 学生编号:{{ studentTeacher.studentId ?? '-' }} | |
| 83 | + </view> | |
| 84 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 85 | + 名字:{{ studentTeacher.name ?? '-' }} | |
| 86 | + </view> | |
| 87 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 88 | + 简介:{{ studentTeacher.description ?? '-' }} | |
| 89 | + </view> | |
| 90 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 91 | + 出生日期:{{ studentTeacher.birthday ?? '-' }} | |
| 92 | + </view> | |
| 93 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 94 | + 性别:{{ studentTeacher.sex ?? '-' }} | |
| 95 | + </view> | |
| 96 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 97 | + 是否有效:{{ studentTeacher.enabled ?? '-' }} | |
| 98 | + </view> | |
| 99 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 100 | + 头像:{{ studentTeacher.avatar ?? '-' }} | |
| 101 | + </view> | |
| 102 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 103 | + 附件:{{ studentTeacher.video ?? '-' }} | |
| 104 | + </view> | |
| 105 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 106 | + 备注:{{ studentTeacher.memo ?? '-' }} | |
| 107 | + </view> | |
| 108 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 109 | + 创建时间:{{ studentTeacher.createTime ?? '-' }} | |
| 110 | + </view> | |
| 111 | + </template> | |
| 112 | + <view v-else class="text-center text-26rpx text-[#999]"> | |
| 113 | + 暂无学生班主任 | |
| 114 | + </view> | |
| 115 | + </view> | |
| 116 | + </view> | |
| 117 | + </view> | |
| 118 | + | |
| 119 | + <!-- 底部操作按钮 --> | |
| 120 | + <view class="yd-detail-footer"> | |
| 121 | + <view class="yd-detail-footer-actions"> | |
| 122 | + <wd-button | |
| 123 | + v-if="hasAccessByCodes(['infra:student:update'])" | |
| 124 | + class="flex-1" type="warning" @click="handleEdit" | |
| 125 | + > | |
| 126 | + 编辑 | |
| 127 | + </wd-button> | |
| 128 | + <wd-button | |
| 129 | + v-if="hasAccessByCodes(['infra:student:delete'])" | |
| 130 | + class="flex-1" type="danger" :loading="deleting" @click="handleDelete" | |
| 131 | + > | |
| 132 | + 删除 | |
| 133 | + </wd-button> | |
| 134 | + </view> | |
| 135 | + </view> | |
| 136 | + </view> | |
| 137 | +</template> | |
| 138 | + | |
| 139 | +<script lang="ts" setup> | |
| 140 | +import type { | |
| 141 | + Student, | |
| 142 | + StudentContact, | |
| 143 | + StudentTeacher, | |
| 144 | +} from '@/api/infra/demo' | |
| 145 | +import { onShow } from '@dcloudio/uni-app' | |
| 146 | +import { ref } from 'vue' | |
| 147 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 148 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 149 | +import { | |
| 150 | + deleteStudent, | |
| 151 | + getStudent, | |
| 152 | + getStudentContactListByStudentId, | |
| 153 | + getStudentTeacherByStudentId, | |
| 154 | +} from '@/api/infra/demo' | |
| 155 | +import { useAccess } from '@/hooks/useAccess' | |
| 156 | +import { delay, navigateBackPlus } from '@/utils' | |
| 157 | +import { DICT_TYPE } from '@/utils/constants' | |
| 158 | +import { formatDateTime } from '@/utils/date' | |
| 159 | + | |
| 160 | +const props = defineProps<{ | |
| 161 | + id?: number | |
| 162 | +}>() | |
| 163 | + | |
| 164 | +definePage({ | |
| 165 | + style: { | |
| 166 | + navigationBarTitleText: '', | |
| 167 | + navigationStyle: 'custom', | |
| 168 | + }, | |
| 169 | +}) | |
| 170 | + | |
| 171 | +const { hasAccessByCodes } = useAccess() | |
| 172 | +const dialog = useDialog() | |
| 173 | +const toast = useToast() | |
| 174 | +const formData = ref<Student>() // 主表数据 | |
| 175 | +const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据 | |
| 176 | +const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据 | |
| 177 | +const deleting = ref(false) // 删除状态 | |
| 178 | + | |
| 179 | +/** 返回上一页 */ | |
| 180 | +function handleBack() { | |
| 181 | + navigateBackPlus('/pages-infra/demo/index') | |
| 182 | +} | |
| 183 | + | |
| 184 | +/** 加载主表和子表详情 */ | |
| 185 | +async function getDetail() { | |
| 186 | + if (!props.id || deleting.value) { | |
| 187 | + return | |
| 188 | + } | |
| 189 | + const [data, studentContactData, studentTeacherData] = await Promise.all([ | |
| 190 | + getStudent(props.id), | |
| 191 | + getStudentContactListByStudentId(props.id), | |
| 192 | + getStudentTeacherByStudentId(props.id), | |
| 193 | + ]) | |
| 194 | + formData.value = data | |
| 195 | + studentContacts.value = studentContactData || [] | |
| 196 | + studentTeacher.value = studentTeacherData || null | |
| 197 | +} | |
| 198 | + | |
| 199 | +/** 编辑学生 */ | |
| 200 | +function handleEdit() { | |
| 201 | + uni.navigateTo({ url: `/pages-infra/demo/form/index?id=${props.id}` }) | |
| 202 | +} | |
| 203 | + | |
| 204 | +/** 删除学生 */ | |
| 205 | +async function handleDelete() { | |
| 206 | + if (!props.id) { | |
| 207 | + return | |
| 208 | + } | |
| 209 | + try { | |
| 210 | + await dialog.confirm({ title: '提示', msg: '确定要删除该学生吗?' }) | |
| 211 | + } catch { | |
| 212 | + return | |
| 213 | + } | |
| 214 | + deleting.value = true | |
| 215 | + try { | |
| 216 | + await deleteStudent(props.id) | |
| 217 | + toast.success('删除成功') | |
| 218 | + uni.$emit('infra:demo:reload') | |
| 219 | + delay(handleBack) | |
| 220 | + } finally { | |
| 221 | + deleting.value = false | |
| 222 | + } | |
| 223 | +} | |
| 224 | + | |
| 225 | +/** 进入页面加载(含编辑返回后刷新) */ | |
| 226 | +onShow(() => { | |
| 227 | + getDetail() | |
| 228 | +}) | |
| 229 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/vue/form/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + :title="getTitle" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 表单区域 --> | |
| 11 | + <view> | |
| 12 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 13 | + <wd-cell-group border> | |
| 14 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 15 | + <wd-input | |
| 16 | + v-model="formData.name" | |
| 17 | + clearable | |
| 18 | + placeholder="请输入名字" | |
| 19 | + /> | |
| 20 | + </wd-form-item> | |
| 21 | + <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 22 | + <wd-textarea | |
| 23 | + v-model="formData.description" | |
| 24 | + clearable | |
| 25 | + placeholder="请输入简介" | |
| 26 | + :maxlength="200" | |
| 27 | + show-word-limit | |
| 28 | + /> | |
| 29 | + </wd-form-item> | |
| 30 | + <wd-form-item | |
| 31 | + title="出生日期" | |
| 32 | + title-width="180rpx" | |
| 33 | + prop="birthday" | |
| 34 | + is-link | |
| 35 | + :value="formData.birthday ? formatDateTime(formData.birthday) : ''" | |
| 36 | + placeholder="请选择出生日期" | |
| 37 | + @click="birthdayVisible = true" | |
| 38 | + /> | |
| 39 | + <wd-datetime-picker | |
| 40 | + v-model:visible="birthdayVisible" | |
| 41 | + :model-value="formData.birthday || Date.now()" | |
| 42 | + type="datetime" | |
| 43 | + @update:model-value="value => formData.birthday = value" | |
| 44 | + /> | |
| 45 | + <yd-form-picker | |
| 46 | + v-model="formData.sex" | |
| 47 | + label="性别" | |
| 48 | + label-width="180rpx" | |
| 49 | + prop="sex" | |
| 50 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 51 | + placeholder="请选择性别" | |
| 52 | + /> | |
| 53 | + <wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center> | |
| 54 | + <wd-radio-group v-model="formData.enabled" type="button"> | |
| 55 | + <wd-radio | |
| 56 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 57 | + :key="dict.value" | |
| 58 | + :value="dict.value" | |
| 59 | + > | |
| 60 | + {{ dict.label }} | |
| 61 | + </wd-radio> | |
| 62 | + </wd-radio-group> | |
| 63 | + </wd-form-item> | |
| 64 | + <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 65 | + <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> | |
| 66 | + </wd-form-item> | |
| 67 | + <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 68 | + <yd-upload-file v-model="formData.video" directory="infra/demo" /> | |
| 69 | + </wd-form-item> | |
| 70 | + <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 71 | + <wd-input | |
| 72 | + v-model="formData.memo" | |
| 73 | + clearable | |
| 74 | + placeholder="请输入备注" | |
| 75 | + /> | |
| 76 | + </wd-form-item> | |
| 77 | + </wd-cell-group> | |
| 78 | + </wd-form> | |
| 79 | + | |
| 80 | + <!-- 子表:学生联系人 --> | |
| 81 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 82 | + <text class="text-28rpx text-[#333] font-semibold">学生联系人</text> | |
| 83 | + <wd-button size="small" type="primary" variant="plain" @click="addStudentContact"> | |
| 84 | + 添加 | |
| 85 | + </wd-button> | |
| 86 | + </view> | |
| 87 | + <view class="px-24rpx"> | |
| 88 | + <view v-if="!studentContacts.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 89 | + 暂无学生联系人 | |
| 90 | + </view> | |
| 91 | + <view | |
| 92 | + v-for="(item, index) in studentContacts" | |
| 93 | + :key="index" | |
| 94 | + class="mb-20rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 95 | + > | |
| 96 | + <view class="mb-16rpx flex justify-end"> | |
| 97 | + <wd-button size="small" type="danger" variant="plain" @click="removeStudentContact(index)"> | |
| 98 | + 删除 | |
| 99 | + </wd-button> | |
| 100 | + </view> | |
| 101 | + <wd-form-item title="名字" title-width="180rpx"> | |
| 102 | + <wd-input v-model="item.name" clearable placeholder="请输入名字" /> | |
| 103 | + </wd-form-item> | |
| 104 | + <wd-form-item title="简介" title-width="180rpx"> | |
| 105 | + <wd-textarea v-model="item.description" clearable placeholder="请输入简介" /> | |
| 106 | + </wd-form-item> | |
| 107 | + <wd-form-item | |
| 108 | + title="出生日期" | |
| 109 | + title-width="180rpx" | |
| 110 | + is-link | |
| 111 | + :value="item.birthday ? formatDateTime(item.birthday) : ''" | |
| 112 | + placeholder="请选择出生日期" | |
| 113 | + @click="subDateVisible[`studentContacts.${index}.birthday`] = true" | |
| 114 | + /> | |
| 115 | + <wd-datetime-picker | |
| 116 | + v-model:visible="subDateVisible[`studentContacts.${index}.birthday`]" | |
| 117 | + :model-value="item.birthday || Date.now()" | |
| 118 | + type="datetime" | |
| 119 | + @update:model-value="value => item.birthday = value" | |
| 120 | + /> | |
| 121 | + <yd-form-picker | |
| 122 | + v-model="item.sex" | |
| 123 | + label="性别" | |
| 124 | + label-width="180rpx" | |
| 125 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 126 | + placeholder="请选择性别" | |
| 127 | + /> | |
| 128 | + <wd-form-item title="是否有效" title-width="180rpx" center> | |
| 129 | + <wd-radio-group v-model="item.enabled" type="button"> | |
| 130 | + <wd-radio | |
| 131 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 132 | + :key="dict.value" | |
| 133 | + :value="dict.value" | |
| 134 | + > | |
| 135 | + {{ dict.label }} | |
| 136 | + </wd-radio> | |
| 137 | + </wd-radio-group> | |
| 138 | + </wd-form-item> | |
| 139 | + <wd-form-item title="头像" title-width="180rpx"> | |
| 140 | + <yd-upload-img v-model="item.avatar" directory="infra/demo" /> | |
| 141 | + </wd-form-item> | |
| 142 | + <wd-form-item title="附件" title-width="180rpx"> | |
| 143 | + <yd-upload-file v-model="item.video" directory="infra/demo" /> | |
| 144 | + </wd-form-item> | |
| 145 | + <wd-form-item title="备注" title-width="180rpx"> | |
| 146 | + <wd-input v-model="item.memo" clearable placeholder="请输入备注" /> | |
| 147 | + </wd-form-item> | |
| 148 | + </view> | |
| 149 | + </view> | |
| 150 | + | |
| 151 | + <!-- 子表:学生班主任 --> | |
| 152 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 153 | + <text class="text-28rpx text-[#333] font-semibold">学生班主任</text> | |
| 154 | + <wd-button | |
| 155 | + v-if="!studentTeacher" | |
| 156 | + size="small" | |
| 157 | + type="primary" | |
| 158 | + variant="plain" | |
| 159 | + @click="addStudentTeacher()" | |
| 160 | + > | |
| 161 | + 添加 | |
| 162 | + </wd-button> | |
| 163 | + </view> | |
| 164 | + <view class="px-24rpx"> | |
| 165 | + <view v-if="!studentTeacher" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 166 | + 暂无学生班主任 | |
| 167 | + </view> | |
| 168 | + <view v-else class="rounded-12rpx bg-white p-24rpx shadow-sm"> | |
| 169 | + <wd-form-item title="名字" title-width="180rpx"> | |
| 170 | + <wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" /> | |
| 171 | + </wd-form-item> | |
| 172 | + <wd-form-item title="简介" title-width="180rpx"> | |
| 173 | + <wd-textarea v-model="studentTeacher.description" clearable placeholder="请输入简介" /> | |
| 174 | + </wd-form-item> | |
| 175 | + <wd-form-item | |
| 176 | + title="出生日期" | |
| 177 | + title-width="180rpx" | |
| 178 | + is-link | |
| 179 | + :value="studentTeacher.birthday ? formatDateTime(studentTeacher.birthday) : ''" | |
| 180 | + placeholder="请选择出生日期" | |
| 181 | + @click="subDateVisible['studentTeacher.birthday'] = true" | |
| 182 | + /> | |
| 183 | + <wd-datetime-picker | |
| 184 | + v-model:visible="subDateVisible['studentTeacher.birthday']" | |
| 185 | + :model-value="studentTeacher.birthday || Date.now()" | |
| 186 | + type="datetime" | |
| 187 | + @update:model-value="value => studentTeacher.birthday = value" | |
| 188 | + /> | |
| 189 | + <yd-form-picker | |
| 190 | + v-model="studentTeacher.sex" | |
| 191 | + label="性别" | |
| 192 | + label-width="180rpx" | |
| 193 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 194 | + placeholder="请选择性别" | |
| 195 | + /> | |
| 196 | + <wd-form-item title="是否有效" title-width="180rpx" center> | |
| 197 | + <wd-radio-group v-model="studentTeacher.enabled" type="button"> | |
| 198 | + <wd-radio | |
| 199 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 200 | + :key="dict.value" | |
| 201 | + :value="dict.value" | |
| 202 | + > | |
| 203 | + {{ dict.label }} | |
| 204 | + </wd-radio> | |
| 205 | + </wd-radio-group> | |
| 206 | + </wd-form-item> | |
| 207 | + <wd-form-item title="头像" title-width="180rpx"> | |
| 208 | + <yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" /> | |
| 209 | + </wd-form-item> | |
| 210 | + <wd-form-item title="附件" title-width="180rpx"> | |
| 211 | + <yd-upload-file v-model="studentTeacher.video" directory="infra/demo" /> | |
| 212 | + </wd-form-item> | |
| 213 | + <wd-form-item title="备注" title-width="180rpx"> | |
| 214 | + <wd-input v-model="studentTeacher.memo" clearable placeholder="请输入备注" /> | |
| 215 | + </wd-form-item> | |
| 216 | + </view> | |
| 217 | + </view> | |
| 218 | + </view> | |
| 219 | + | |
| 220 | + <!-- 底部保存按钮 --> | |
| 221 | + <view class="yd-detail-footer"> | |
| 222 | + <wd-button | |
| 223 | + type="primary" | |
| 224 | + block | |
| 225 | + :loading="formLoading" | |
| 226 | + @click="handleSubmit" | |
| 227 | + > | |
| 228 | + 保存 | |
| 229 | + </wd-button> | |
| 230 | + </view> | |
| 231 | + </view> | |
| 232 | +</template> | |
| 233 | + | |
| 234 | +<script lang="ts" setup> | |
| 235 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 236 | +import type { | |
| 237 | + Student, | |
| 238 | + StudentContact, | |
| 239 | + StudentTeacher, | |
| 240 | +} from '@/api/infra/demo' | |
| 241 | +import { computed, onMounted, ref } from 'vue' | |
| 242 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 243 | +import { | |
| 244 | + createStudent, | |
| 245 | + getStudent, | |
| 246 | + getStudentContactListByStudentId, | |
| 247 | + getStudentTeacherByStudentId, | |
| 248 | + updateStudent, | |
| 249 | +} from '@/api/infra/demo' | |
| 250 | +import { getBoolDictOptions } from '@/hooks/useDict' | |
| 251 | +import { delay, navigateBackPlus } from '@/utils' | |
| 252 | +import { DICT_TYPE } from '@/utils/constants' | |
| 253 | +import { formatDateTime } from '@/utils/date' | |
| 254 | +import { createFormSchema } from '@/utils/wot' | |
| 255 | + | |
| 256 | +const props = defineProps<{ | |
| 257 | + id?: number | |
| 258 | +}>() | |
| 259 | + | |
| 260 | +definePage({ | |
| 261 | + style: { | |
| 262 | + navigationBarTitleText: '', | |
| 263 | + navigationStyle: 'custom', | |
| 264 | + }, | |
| 265 | +}) | |
| 266 | + | |
| 267 | +const toast = useToast() | |
| 268 | +const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') | |
| 269 | +const formLoading = ref(false) // 表单提交状态 | |
| 270 | +const formData = ref<Student>({ | |
| 271 | + id: undefined, | |
| 272 | + name: '', | |
| 273 | + description: '', | |
| 274 | + birthday: 0, | |
| 275 | + sex: 0, | |
| 276 | + enabled: false, | |
| 277 | + avatar: '', | |
| 278 | + video: '', | |
| 279 | + memo: '', | |
| 280 | +}) // 表单数据 | |
| 281 | +const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据 | |
| 282 | +const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据 | |
| 283 | +const birthdayVisible = ref(false) // 出生日期选择器状态 | |
| 284 | +const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态 | |
| 285 | +const formSchema = createFormSchema({ | |
| 286 | + name: [{ required: true, message: '名字不能为空' }], | |
| 287 | + description: [{ required: true, message: '简介不能为空' }], | |
| 288 | + birthday: [{ required: true, message: '出生日期不能为空' }], | |
| 289 | + sex: [{ required: true, message: '性别不能为空' }], | |
| 290 | + enabled: [{ required: true, message: '是否有效不能为空' }], | |
| 291 | + avatar: [{ required: true, message: '头像不能为空' }], | |
| 292 | + video: [{ required: true, message: '附件不能为空' }], | |
| 293 | + memo: [{ required: true, message: '备注不能为空' }], | |
| 294 | +}) // 表单校验规则 | |
| 295 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 296 | + | |
| 297 | +/** 添加学生联系人 */ | |
| 298 | +function addStudentContact() { | |
| 299 | + studentContacts.value.push({ | |
| 300 | + name: '', | |
| 301 | + description: '', | |
| 302 | + birthday: 0, | |
| 303 | + sex: 0, | |
| 304 | + enabled: false, | |
| 305 | + avatar: '', | |
| 306 | + video: undefined, | |
| 307 | + memo: '', | |
| 308 | + }) | |
| 309 | +} | |
| 310 | + | |
| 311 | +/** 删除学生联系人 */ | |
| 312 | +function removeStudentContact(index: number) { | |
| 313 | + studentContacts.value.splice(index, 1) | |
| 314 | +} | |
| 315 | + | |
| 316 | +/** 添加学生班主任 */ | |
| 317 | +function addStudentTeacher() { | |
| 318 | + studentTeacher.value = { | |
| 319 | + name: '', | |
| 320 | + description: '', | |
| 321 | + birthday: 0, | |
| 322 | + sex: 0, | |
| 323 | + enabled: false, | |
| 324 | + avatar: '', | |
| 325 | + video: undefined, | |
| 326 | + memo: '', | |
| 327 | + } | |
| 328 | +} | |
| 329 | + | |
| 330 | +/** 校验主子表数据 */ | |
| 331 | +function isEmptySubValue(value: unknown) { | |
| 332 | + return value === undefined || value === null || value === '' | |
| 333 | +} | |
| 334 | + | |
| 335 | +function validateSubTables() { | |
| 336 | + for (let index = 0; index < studentContacts.value.length; index++) { | |
| 337 | + const item = studentContacts.value[index]! | |
| 338 | + if (isEmptySubValue(item.name)) { | |
| 339 | + toast.warning(`学生联系人第${index + 1}项的名字不能为空`) | |
| 340 | + return false | |
| 341 | + } | |
| 342 | + if (isEmptySubValue(item.description)) { | |
| 343 | + toast.warning(`学生联系人第${index + 1}项的简介不能为空`) | |
| 344 | + return false | |
| 345 | + } | |
| 346 | + if (isEmptySubValue(item.birthday)) { | |
| 347 | + toast.warning(`学生联系人第${index + 1}项的出生日期不能为空`) | |
| 348 | + return false | |
| 349 | + } | |
| 350 | + if (isEmptySubValue(item.sex)) { | |
| 351 | + toast.warning(`学生联系人第${index + 1}项的性别不能为空`) | |
| 352 | + return false | |
| 353 | + } | |
| 354 | + if (isEmptySubValue(item.enabled)) { | |
| 355 | + toast.warning(`学生联系人第${index + 1}项的是否有效不能为空`) | |
| 356 | + return false | |
| 357 | + } | |
| 358 | + if (isEmptySubValue(item.avatar)) { | |
| 359 | + toast.warning(`学生联系人第${index + 1}项的头像不能为空`) | |
| 360 | + return false | |
| 361 | + } | |
| 362 | + if (isEmptySubValue(item.memo)) { | |
| 363 | + toast.warning(`学生联系人第${index + 1}项的备注不能为空`) | |
| 364 | + return false | |
| 365 | + } | |
| 366 | + } | |
| 367 | + if (studentTeacher.value) { | |
| 368 | + if (isEmptySubValue(studentTeacher.value.name)) { | |
| 369 | + toast.warning('学生班主任的名字不能为空') | |
| 370 | + return false | |
| 371 | + } | |
| 372 | + if (isEmptySubValue(studentTeacher.value.description)) { | |
| 373 | + toast.warning('学生班主任的简介不能为空') | |
| 374 | + return false | |
| 375 | + } | |
| 376 | + if (isEmptySubValue(studentTeacher.value.birthday)) { | |
| 377 | + toast.warning('学生班主任的出生日期不能为空') | |
| 378 | + return false | |
| 379 | + } | |
| 380 | + if (isEmptySubValue(studentTeacher.value.sex)) { | |
| 381 | + toast.warning('学生班主任的性别不能为空') | |
| 382 | + return false | |
| 383 | + } | |
| 384 | + if (isEmptySubValue(studentTeacher.value.enabled)) { | |
| 385 | + toast.warning('学生班主任的是否有效不能为空') | |
| 386 | + return false | |
| 387 | + } | |
| 388 | + if (isEmptySubValue(studentTeacher.value.avatar)) { | |
| 389 | + toast.warning('学生班主任的头像不能为空') | |
| 390 | + return false | |
| 391 | + } | |
| 392 | + if (isEmptySubValue(studentTeacher.value.memo)) { | |
| 393 | + toast.warning('学生班主任的备注不能为空') | |
| 394 | + return false | |
| 395 | + } | |
| 396 | + } | |
| 397 | + return true | |
| 398 | +} | |
| 399 | + | |
| 400 | +/** 返回上一页 */ | |
| 401 | +function handleBack() { | |
| 402 | + navigateBackPlus('/pages-infra/demo/index') | |
| 403 | +} | |
| 404 | + | |
| 405 | +/** 加载学生详情 */ | |
| 406 | +async function getDetail() { | |
| 407 | + if (!props.id) { | |
| 408 | + return | |
| 409 | + } | |
| 410 | + const [data, studentContactData, studentTeacherData] = await Promise.all([ | |
| 411 | + getStudent(props.id), | |
| 412 | + getStudentContactListByStudentId(props.id), | |
| 413 | + getStudentTeacherByStudentId(props.id), | |
| 414 | + ]) | |
| 415 | + formData.value = data | |
| 416 | + studentContacts.value = studentContactData || [] | |
| 417 | + studentTeacher.value = studentTeacherData || null | |
| 418 | +} | |
| 419 | + | |
| 420 | +/** 提交表单 */ | |
| 421 | +async function handleSubmit() { | |
| 422 | + const { valid } = await formRef.value.validate() | |
| 423 | + if (!valid) { | |
| 424 | + return | |
| 425 | + } | |
| 426 | + if (!validateSubTables()) { | |
| 427 | + return | |
| 428 | + } | |
| 429 | + | |
| 430 | + formLoading.value = true | |
| 431 | + try { | |
| 432 | + const data: Student = { | |
| 433 | + ...formData.value, | |
| 434 | + studentContacts: studentContacts.value, | |
| 435 | + studentTeacher: studentTeacher.value, | |
| 436 | + } | |
| 437 | + if (props.id) { | |
| 438 | + await updateStudent(data) | |
| 439 | + toast.success('修改成功') | |
| 440 | + } else { | |
| 441 | + await createStudent(data) | |
| 442 | + toast.success('新增成功') | |
| 443 | + } | |
| 444 | + uni.$emit('infra:demo:reload') | |
| 445 | + delay(handleBack) | |
| 446 | + } finally { | |
| 447 | + formLoading.value = false | |
| 448 | + } | |
| 449 | +} | |
| 450 | + | |
| 451 | +/** 初始化 */ | |
| 452 | +onMounted(async () => { | |
| 453 | + getDetail() | |
| 454 | +}) | |
| 455 | +</script> | |
| 456 | + | |
| 457 | +<style lang="scss" scoped> | |
| 458 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/vue/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container yd-page-container-paging"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="学生管理" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 搜索组件 --> | |
| 11 | + <SearchForm @search="handleQuery" @reset="handleReset" /> | |
| 12 | + | |
| 13 | + <!-- 学生列表 --> | |
| 14 | + <z-paging | |
| 15 | + ref="pagingRef" | |
| 16 | + v-model="list" | |
| 17 | + :fixed="false" | |
| 18 | + class="min-h-0 flex-1" | |
| 19 | + :default-page-size="10" | |
| 20 | + :refresher-enabled="true" | |
| 21 | + :inside-more="true" | |
| 22 | + :loading-more-default-as-loading="true" | |
| 23 | + empty-view-text="暂无学生数据" | |
| 24 | + @query="queryList" | |
| 25 | + > | |
| 26 | + <view class="p-24rpx"> | |
| 27 | + <view | |
| 28 | + v-for="item in list" | |
| 29 | + :key="item.id" | |
| 30 | + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 31 | + @click="handleDetail(item)" | |
| 32 | + > | |
| 33 | + <view class="p-24rpx"> | |
| 34 | + <view class="mb-16rpx flex items-center justify-between"> | |
| 35 | + <view class="text-32rpx text-[#333] font-semibold"> | |
| 36 | + {{ item.name }} | |
| 37 | + </view> | |
| 38 | + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="item.sex" /> | |
| 39 | + </view> | |
| 40 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 41 | + <text class="mr-8rpx text-[#999]">简介:</text> | |
| 42 | + <text class="line-clamp-1">{{ item.description }}</text> | |
| 43 | + </view> | |
| 44 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 45 | + <text class="mr-8rpx text-[#999]">出生日期:</text> | |
| 46 | + <text class="line-clamp-1">{{ formatDateTime(item.birthday) || '-' }}</text> | |
| 47 | + </view> | |
| 48 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 49 | + <text class="mr-8rpx text-[#999]">是否有效:</text> | |
| 50 | + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="item.enabled" /> | |
| 51 | + </view> | |
| 52 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 53 | + <text class="mr-8rpx text-[#999]">头像:</text> | |
| 54 | + <text class="line-clamp-1">{{ item.avatar }}</text> | |
| 55 | + </view> | |
| 56 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 57 | + <text class="mr-8rpx text-[#999]">附件:</text> | |
| 58 | + <text class="line-clamp-1">{{ item.video }}</text> | |
| 59 | + </view> | |
| 60 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 61 | + <text class="mr-8rpx text-[#999]">备注:</text> | |
| 62 | + <text class="line-clamp-1">{{ item.memo }}</text> | |
| 63 | + </view> | |
| 64 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 65 | + <text class="mr-8rpx text-[#999]">创建时间:</text> | |
| 66 | + <text class="line-clamp-1">{{ formatDateTime(item.createTime) || '-' }}</text> | |
| 67 | + </view> | |
| 68 | + </view> | |
| 69 | + </view> | |
| 70 | + </view> | |
| 71 | + </z-paging> | |
| 72 | + | |
| 73 | + <!-- 新增按钮 --> | |
| 74 | + <wd-fab | |
| 75 | + v-if="hasAccessByCodes(['infra:student:create'])" | |
| 76 | + position="right-bottom" | |
| 77 | + type="primary" | |
| 78 | + :expandable="false" | |
| 79 | + @click="handleAdd" | |
| 80 | + /> | |
| 81 | + </view> | |
| 82 | +</template> | |
| 83 | + | |
| 84 | +<script lang="ts" setup> | |
| 85 | +import type { Student } from '@/api/infra/demo' | |
| 86 | +import { onUnload } from '@dcloudio/uni-app' | |
| 87 | +import { onMounted, ref } from 'vue' | |
| 88 | +import { getStudentPage } from '@/api/infra/demo' | |
| 89 | +import { useAccess } from '@/hooks/useAccess' | |
| 90 | +import { navigateBackPlus } from '@/utils' | |
| 91 | +import { DICT_TYPE } from '@/utils/constants' | |
| 92 | +import { formatDateTime } from '@/utils/date' | |
| 93 | +import SearchForm from './components/search-form.vue' | |
| 94 | + | |
| 95 | +definePage({ | |
| 96 | + style: { | |
| 97 | + navigationBarTitleText: '', | |
| 98 | + navigationStyle: 'custom', | |
| 99 | + }, | |
| 100 | +}) | |
| 101 | + | |
| 102 | +const { hasAccessByCodes } = useAccess() | |
| 103 | +const list = ref<Student[]>([]) // 列表数据 | |
| 104 | +const pagingRef = ref<any>() // 分页组件引用 | |
| 105 | +const queryParams = ref<Record<string, any>>({}) // 查询参数 | |
| 106 | + | |
| 107 | +/** 返回上一页 */ | |
| 108 | +function handleBack() { | |
| 109 | + navigateBackPlus() | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** 查询学生列表 */ | |
| 113 | +async function queryList(pageNo: number, pageSize: number) { | |
| 114 | + try { | |
| 115 | + const params = { | |
| 116 | + ...queryParams.value, | |
| 117 | + pageNo, | |
| 118 | + pageSize, | |
| 119 | + } | |
| 120 | + const data = await getStudentPage(params) | |
| 121 | + pagingRef.value?.completeByTotal(data.list, data.total) | |
| 122 | + } catch { | |
| 123 | + pagingRef.value?.complete(false) | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** 搜索按钮操作 */ | |
| 128 | +function handleQuery(data?: Record<string, any>) { | |
| 129 | + queryParams.value = { ...data } | |
| 130 | + reload() | |
| 131 | +} | |
| 132 | + | |
| 133 | +/** 重置按钮操作 */ | |
| 134 | +function handleReset() { | |
| 135 | + handleQuery() | |
| 136 | +} | |
| 137 | + | |
| 138 | +/** 重新加载 */ | |
| 139 | +function reload() { | |
| 140 | + pagingRef.value?.reload() | |
| 141 | +} | |
| 142 | + | |
| 143 | +/** 新增学生 */ | |
| 144 | +function handleAdd() { | |
| 145 | + uni.navigateTo({ | |
| 146 | + url: '/pages-infra/demo/form/index', | |
| 147 | + }) | |
| 148 | +} | |
| 149 | + | |
| 150 | +/** 查看详情 */ | |
| 151 | +function handleDetail(item: Student) { | |
| 152 | + uni.navigateTo({ | |
| 153 | + url: `/pages-infra/demo/detail/index?id=${item.id}`, | |
| 154 | + }) | |
| 155 | +} | |
| 156 | + | |
| 157 | +/** 初始化 */ | |
| 158 | +onMounted(() => { | |
| 159 | + uni.$on('infra:demo:reload', reload) | |
| 160 | +}) | |
| 161 | + | |
| 162 | +/** 卸载 */ | |
| 163 | +onUnload(() => { | |
| 164 | + uni.$off('infra:demo:reload', reload) | |
| 165 | +}) | |
| 166 | +</script> | |
| 167 | + | |
| 168 | +<style lang="scss" scoped> | |
| 169 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_inner/vue/search-form
0 → 100644
| 1 | +<template> | |
| 2 | + <!-- 搜索框入口 --> | |
| 3 | + <view @click="visible = true"> | |
| 4 | + <wd-search :placeholder="placeholder" hide-cancel disabled /> | |
| 5 | + </view> | |
| 6 | + | |
| 7 | + <!-- 搜索弹窗 --> | |
| 8 | + <wd-popup v-model="visible" position="top" @close="visible = false"> | |
| 9 | + <view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }"> | |
| 10 | + <view class="yd-search-form-item"> | |
| 11 | + <view class="yd-search-form-label"> | |
| 12 | + 名字 | |
| 13 | + </view> | |
| 14 | + <wd-input | |
| 15 | + v-model="formData.name" | |
| 16 | + placeholder="请输入名字" | |
| 17 | + clearable | |
| 18 | + /> | |
| 19 | + </view> | |
| 20 | + <view class="yd-search-form-item"> | |
| 21 | + <view class="yd-search-form-label"> | |
| 22 | + 出生日期 | |
| 23 | + </view> | |
| 24 | + <wd-input | |
| 25 | + v-model="formData.birthday" | |
| 26 | + placeholder="请输入出生日期" | |
| 27 | + clearable | |
| 28 | + /> | |
| 29 | + </view> | |
| 30 | + <yd-search-picker | |
| 31 | + v-model="formData.sex" | |
| 32 | + label="性别" | |
| 33 | + :columns="getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" | |
| 34 | + all-option | |
| 35 | + /> | |
| 36 | + <yd-search-picker | |
| 37 | + v-model="formData.enabled" | |
| 38 | + label="是否有效" | |
| 39 | + :columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 40 | + all-option | |
| 41 | + /> | |
| 42 | + <yd-search-date-range v-model="formData.createTime" label="创建时间" /> | |
| 43 | + <view class="yd-search-form-actions"> | |
| 44 | + <wd-button class="flex-1" plain @click="handleReset"> | |
| 45 | + 重置 | |
| 46 | + </wd-button> | |
| 47 | + <wd-button class="flex-1" type="primary" @click="handleSearch"> | |
| 48 | + 搜索 | |
| 49 | + </wd-button> | |
| 50 | + </view> | |
| 51 | + </view> | |
| 52 | + </wd-popup> | |
| 53 | +</template> | |
| 54 | + | |
| 55 | +<script lang="ts" setup> | |
| 56 | +import { computed, reactive, ref } from 'vue' | |
| 57 | +import { getNavbarHeight } from '@/utils' | |
| 58 | +import { formatDate, formatDateRange } from '@/utils/date' | |
| 59 | +import { getDictLabel, getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict' | |
| 60 | +import { DICT_TYPE } from '@/utils/constants' | |
| 61 | + | |
| 62 | +const emit = defineEmits<{ | |
| 63 | + search: [data: Record<string, any>] | |
| 64 | + reset: [] | |
| 65 | +}>() | |
| 66 | + | |
| 67 | +const visible = ref(false) // 搜索弹窗显示状态 | |
| 68 | +const formData = reactive({ | |
| 69 | + name: undefined as string | undefined, | |
| 70 | + birthday: undefined as string | undefined, | |
| 71 | + sex: undefined as number | undefined, | |
| 72 | + enabled: undefined as boolean | undefined, | |
| 73 | + createTime: [undefined, undefined] as [number | undefined, number | undefined], | |
| 74 | +}) // 搜索表单数据 | |
| 75 | + | |
| 76 | +/** 搜索条件 placeholder 拼接 */ | |
| 77 | +const placeholder = computed(() => { | |
| 78 | + const conditions: string[] = [] | |
| 79 | + if (formData.name) { | |
| 80 | + conditions.push(`名字:${formData.name}`) | |
| 81 | + } | |
| 82 | + if (formData.birthday) { | |
| 83 | + conditions.push(`出生日期:${formData.birthday}`) | |
| 84 | + } | |
| 85 | + if (formData.sex !== undefined) { | |
| 86 | + conditions.push(`性别:${getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, formData.sex)}`) | |
| 87 | + } | |
| 88 | + if (formData.enabled !== undefined) { | |
| 89 | + conditions.push(`是否有效:${getDictLabel(DICT_TYPE.INFRA_BOOLEAN_STRING, formData.enabled)}`) | |
| 90 | + } | |
| 91 | + if (formData.createTime?.[0] && formData.createTime?.[1]) { | |
| 92 | + conditions.push(`创建时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`) | |
| 93 | + } | |
| 94 | + return conditions.length > 0 ? conditions.join(' | ') : '搜索学生' | |
| 95 | +}) | |
| 96 | + | |
| 97 | +/** 搜索按钮操作 */ | |
| 98 | +function handleSearch() { | |
| 99 | + visible.value = false | |
| 100 | + emit('search', { | |
| 101 | + ...formData, | |
| 102 | + createTime: formatDateRange(formData.createTime), | |
| 103 | + }) | |
| 104 | +} | |
| 105 | + | |
| 106 | +/** 重置按钮操作 */ | |
| 107 | +function handleReset() { | |
| 108 | + formData.name = undefined | |
| 109 | + formData.birthday = undefined | |
| 110 | + formData.sex = undefined | |
| 111 | + formData.enabled = undefined | |
| 112 | + formData.createTime = [undefined, undefined] | |
| 113 | + visible.value = false | |
| 114 | + emit('reset') | |
| 115 | +} | |
| 116 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 13 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/assert.json
0 → 100644
| 1 | +[ { | |
| 2 | + "contentPath" : "java/InfraStudentPageReqVO", | |
| 3 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" | |
| 4 | +}, { | |
| 5 | + "contentPath" : "java/InfraStudentRespVO", | |
| 6 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" | |
| 7 | +}, { | |
| 8 | + "contentPath" : "java/InfraStudentSaveReqVO", | |
| 9 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" | |
| 10 | +}, { | |
| 11 | + "contentPath" : "java/InfraStudentController", | |
| 12 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" | |
| 13 | +}, { | |
| 14 | + "contentPath" : "java/InfraStudentDO", | |
| 15 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" | |
| 16 | +}, { | |
| 17 | + "contentPath" : "java/InfraStudentContactDO", | |
| 18 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" | |
| 19 | +}, { | |
| 20 | + "contentPath" : "java/InfraStudentTeacherDO", | |
| 21 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" | |
| 22 | +}, { | |
| 23 | + "contentPath" : "java/InfraStudentMapper", | |
| 24 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" | |
| 25 | +}, { | |
| 26 | + "contentPath" : "java/InfraStudentContactMapper", | |
| 27 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" | |
| 28 | +}, { | |
| 29 | + "contentPath" : "java/InfraStudentTeacherMapper", | |
| 30 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" | |
| 31 | +}, { | |
| 32 | + "contentPath" : "xml/InfraStudentMapper", | |
| 33 | + "filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml" | |
| 34 | +}, { | |
| 35 | + "contentPath" : "java/InfraStudentServiceImpl", | |
| 36 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" | |
| 37 | +}, { | |
| 38 | + "contentPath" : "java/InfraStudentService", | |
| 39 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" | |
| 40 | +}, { | |
| 41 | + "contentPath" : "java/InfraStudentServiceImplTest", | |
| 42 | + "filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" | |
| 43 | +}, { | |
| 44 | + "contentPath" : "java/ErrorCodeConstants_手动操作", | |
| 45 | + "filePath" : "yudao-module-infra/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" : "ts/index", | |
| 54 | + "filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts" | |
| 55 | +}, { | |
| 56 | + "contentPath" : "vue/index", | |
| 57 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue" | |
| 58 | +}, { | |
| 59 | + "contentPath" : "vue/search-form", | |
| 60 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue" | |
| 61 | +}, { | |
| 62 | + "contentPath" : "vue/form/index", | |
| 63 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue" | |
| 64 | +}, { | |
| 65 | + "contentPath" : "vue/detail/index", | |
| 66 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue" | |
| 67 | +} ] | |
| 0 | 68 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/java/ErrorCodeConstants_手动操作
0 → 100644
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 72 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + default int deleteByStudentIds(List<Long> studentIds) { | |
| 29 | + return deleteBatch(InfraStudentContactDO::getStudentId, studentIds); | |
| 30 | + } | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.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 jakarta.validation.constraints.*; | |
| 12 | +import jakarta.validation.*; | |
| 13 | +import jakarta.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.apilog.core.annotation.ApiAccessLog; | |
| 26 | +import static cn.iocoder.yudao.framework.apilog.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 | + @DeleteMapping("/delete-list") | |
| 68 | + @Parameter(name = "ids", description = "编号", required = true) | |
| 69 | + @Operation(summary = "批量删除学生") | |
| 70 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 71 | + public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) { | |
| 72 | + studentService.deleteStudentListByIds(ids); | |
| 73 | + return success(true); | |
| 74 | + } | |
| 75 | + | |
| 76 | + @GetMapping("/get") | |
| 77 | + @Operation(summary = "获得学生") | |
| 78 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 79 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 80 | + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { | |
| 81 | + InfraStudentDO student = studentService.getStudent(id); | |
| 82 | + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); | |
| 83 | + } | |
| 84 | + | |
| 85 | + @GetMapping("/page") | |
| 86 | + @Operation(summary = "获得学生分页") | |
| 87 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 88 | + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { | |
| 89 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); | |
| 90 | + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); | |
| 91 | + } | |
| 92 | + | |
| 93 | + @GetMapping("/export-excel") | |
| 94 | + @Operation(summary = "导出学生 Excel") | |
| 95 | + @PreAuthorize("@ss.hasPermission('infra:student:export')") | |
| 96 | + @ApiAccessLog(operateType = EXPORT) | |
| 97 | + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, | |
| 98 | + HttpServletResponse response) throws IOException { | |
| 99 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 100 | + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); | |
| 101 | + // 导出 Excel | |
| 102 | + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, | |
| 103 | + BeanUtils.toBean(list, InfraStudentRespVO.class)); | |
| 104 | + } | |
| 105 | + | |
| 106 | + // ==================== 子表(学生联系人) ==================== | |
| 107 | + | |
| 108 | + @GetMapping("/student-contact/list-by-student-id") | |
| 109 | + @Operation(summary = "获得学生联系人列表") | |
| 110 | + @Parameter(name = "studentId", description = "学生编号") | |
| 111 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 112 | + public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { | |
| 113 | + return success(studentService.getStudentContactListByStudentId(studentId)); | |
| 114 | + } | |
| 115 | + | |
| 116 | + // ==================== 子表(学生班主任) ==================== | |
| 117 | + | |
| 118 | + @GetMapping("/student-teacher/get-by-student-id") | |
| 119 | + @Operation(summary = "获得学生班主任") | |
| 120 | + @Parameter(name = "studentId", description = "学生编号") | |
| 121 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 122 | + public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { | |
| 123 | + return success(studentService.getStudentTeacherByStudentId(studentId)); | |
| 124 | + } | |
| 125 | + | |
| 126 | +} | |
| 0 | 127 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 68 | +} | |
| 0 | 69 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 31 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +public class InfraStudentPageReqVO extends PageParam { | |
| 15 | + | |
| 16 | + @Schema(description = "名字", example = "芋头") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "出生日期") | |
| 20 | + private LocalDateTime birthday; | |
| 21 | + | |
| 22 | + @Schema(description = "性别", example = "1") | |
| 23 | + private Integer sex; | |
| 24 | + | |
| 25 | + @Schema(description = "是否有效", example = "true") | |
| 26 | + private Boolean enabled; | |
| 27 | + | |
| 28 | + @Schema(description = "创建时间") | |
| 29 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 30 | + private LocalDateTime[] createTime; | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 org.springframework.format.annotation.DateTimeFormat; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | +import cn.idev.excel.annotation.*; | |
| 9 | +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | |
| 10 | +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生 Response VO") | |
| 13 | +@Data | |
| 14 | +@ExcelIgnoreUnannotated | |
| 15 | +public class InfraStudentRespVO { | |
| 16 | + | |
| 17 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 18 | + @ExcelProperty("编号") | |
| 19 | + private Long id; | |
| 20 | + | |
| 21 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 22 | + @ExcelProperty("名字") | |
| 23 | + private String name; | |
| 24 | + | |
| 25 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 26 | + @ExcelProperty("简介") | |
| 27 | + private String description; | |
| 28 | + | |
| 29 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 30 | + @ExcelProperty("出生日期") | |
| 31 | + private LocalDateTime birthday; | |
| 32 | + | |
| 33 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 34 | + @ExcelProperty(value = "性别", converter = DictConvert.class) | |
| 35 | + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 | |
| 36 | + private Integer sex; | |
| 37 | + | |
| 38 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 39 | + @ExcelProperty(value = "是否有效", converter = DictConvert.class) | |
| 40 | + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 | |
| 41 | + private Boolean enabled; | |
| 42 | + | |
| 43 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 44 | + @ExcelProperty("头像") | |
| 45 | + private String avatar; | |
| 46 | + | |
| 47 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 48 | + @ExcelProperty("附件") | |
| 49 | + private String video; | |
| 50 | + | |
| 51 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 52 | + @ExcelProperty("备注") | |
| 53 | + private String memo; | |
| 54 | + | |
| 55 | + @Schema(description = "创建时间") | |
| 56 | + @ExcelProperty("创建时间") | |
| 57 | + private LocalDateTime createTime; | |
| 58 | + | |
| 59 | +} | |
| 0 | 60 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.validation.constraints.*; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; | |
| 10 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生新增/修改 Request VO") | |
| 13 | +@Data | |
| 14 | +public class InfraStudentSaveReqVO { | |
| 15 | + | |
| 16 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 20 | + @NotEmpty(message = "名字不能为空") | |
| 21 | + private String name; | |
| 22 | + | |
| 23 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 24 | + @NotEmpty(message = "简介不能为空") | |
| 25 | + private String description; | |
| 26 | + | |
| 27 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 28 | + @NotNull(message = "出生日期不能为空") | |
| 29 | + private LocalDateTime birthday; | |
| 30 | + | |
| 31 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 32 | + @NotNull(message = "性别不能为空") | |
| 33 | + private Integer sex; | |
| 34 | + | |
| 35 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 36 | + @NotNull(message = "是否有效不能为空") | |
| 37 | + private Boolean enabled; | |
| 38 | + | |
| 39 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 40 | + @NotEmpty(message = "头像不能为空") | |
| 41 | + private String avatar; | |
| 42 | + | |
| 43 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 44 | + @NotEmpty(message = "附件不能为空") | |
| 45 | + private String video; | |
| 46 | + | |
| 47 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 48 | + @NotEmpty(message = "备注不能为空") | |
| 49 | + private String memo; | |
| 50 | + | |
| 51 | + @Schema(description = "学生联系人列表") | |
| 52 | + private List<InfraStudentContactDO> studentContacts; | |
| 53 | + | |
| 54 | + @Schema(description = "学生班主任") | |
| 55 | + private InfraStudentTeacherDO studentTeacher; | |
| 56 | + | |
| 57 | +} | |
| 0 | 58 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/java/InfraStudentService
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | +import jakarta.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 ids 编号 | |
| 45 | + */ | |
| 46 | + void deleteStudentListByIds(List<Long> ids); | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 获得学生 | |
| 50 | + * | |
| 51 | + * @param id 编号 | |
| 52 | + * @return 学生 | |
| 53 | + */ | |
| 54 | + InfraStudentDO getStudent(Long id); | |
| 55 | + | |
| 56 | + /** | |
| 57 | + * 获得学生分页 | |
| 58 | + * | |
| 59 | + * @param pageReqVO 分页查询 | |
| 60 | + * @return 学生分页 | |
| 61 | + */ | |
| 62 | + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO); | |
| 63 | + | |
| 64 | + // ==================== 子表(学生联系人) ==================== | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 获得学生联系人列表 | |
| 68 | + * | |
| 69 | + * @param studentId 学生编号 | |
| 70 | + * @return 学生联系人列表 | |
| 71 | + */ | |
| 72 | + List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId); | |
| 73 | + | |
| 74 | + // ==================== 子表(学生班主任) ==================== | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * 获得学生班主任 | |
| 78 | + * | |
| 79 | + * @param studentId 学生编号 | |
| 80 | + * @return 学生班主任 | |
| 81 | + */ | |
| 82 | + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); | |
| 83 | + | |
| 84 | +} | |
| 0 | 85 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/java/InfraStudentServiceImpl
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | +import org.springframework.stereotype.Service; | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | +import org.springframework.validation.annotation.Validated; | |
| 8 | +import org.springframework.transaction.annotation.Transactional; | |
| 9 | + | |
| 10 | +import java.util.*; | |
| 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.dataobject.demo.InfraStudentContactDO; | |
| 14 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; | |
| 15 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 16 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 17 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 18 | + | |
| 19 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; | |
| 20 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; | |
| 21 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; | |
| 22 | + | |
| 23 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; | |
| 26 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * 学生 Service 实现类 | |
| 30 | + * | |
| 31 | + * @author 芋道源码 | |
| 32 | + */ | |
| 33 | +@Service | |
| 34 | +@Validated | |
| 35 | +public class InfraStudentServiceImpl implements InfraStudentService { | |
| 36 | + | |
| 37 | + @Resource | |
| 38 | + private InfraStudentMapper studentMapper; | |
| 39 | + @Resource | |
| 40 | + private InfraStudentContactMapper studentContactMapper; | |
| 41 | + @Resource | |
| 42 | + private InfraStudentTeacherMapper studentTeacherMapper; | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + @Transactional(rollbackFor = Exception.class) | |
| 46 | + public Long createStudent(InfraStudentSaveReqVO createReqVO) { | |
| 47 | + // 插入 | |
| 48 | + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); | |
| 49 | + studentMapper.insert(student); | |
| 50 | + | |
| 51 | + | |
| 52 | + // 插入子表 | |
| 53 | + createStudentContactList(student.getId(), createReqVO.getStudentContacts()); | |
| 54 | + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); | |
| 55 | + // 返回 | |
| 56 | + return student.getId(); | |
| 57 | + } | |
| 58 | + | |
| 59 | + @Override | |
| 60 | + @Transactional(rollbackFor = Exception.class) | |
| 61 | + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { | |
| 62 | + // 校验存在 | |
| 63 | + validateStudentExists(updateReqVO.getId()); | |
| 64 | + // 更新 | |
| 65 | + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); | |
| 66 | + studentMapper.updateById(updateObj); | |
| 67 | + | |
| 68 | + // 更新子表 | |
| 69 | + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); | |
| 70 | + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); | |
| 71 | + } | |
| 72 | + | |
| 73 | + @Override | |
| 74 | + @Transactional(rollbackFor = Exception.class) | |
| 75 | + public void deleteStudent(Long id) { | |
| 76 | + // 校验存在 | |
| 77 | + validateStudentExists(id); | |
| 78 | + // 删除 | |
| 79 | + studentMapper.deleteById(id); | |
| 80 | + | |
| 81 | + // 删除子表 | |
| 82 | + deleteStudentContactByStudentId(id); | |
| 83 | + deleteStudentTeacherByStudentId(id); | |
| 84 | + } | |
| 85 | + | |
| 86 | + @Override | |
| 87 | + @Transactional(rollbackFor = Exception.class) | |
| 88 | + public void deleteStudentListByIds(List<Long> ids) { | |
| 89 | + // 删除 | |
| 90 | + studentMapper.deleteByIds(ids); | |
| 91 | + | |
| 92 | + // 删除子表 | |
| 93 | + deleteStudentContactByStudentIds(ids); | |
| 94 | + deleteStudentTeacherByStudentIds(ids); | |
| 95 | + } | |
| 96 | + | |
| 97 | + | |
| 98 | + private void validateStudentExists(Long id) { | |
| 99 | + if (studentMapper.selectById(id) == null) { | |
| 100 | + throw exception(STUDENT_NOT_EXISTS); | |
| 101 | + } | |
| 102 | + } | |
| 103 | + | |
| 104 | + @Override | |
| 105 | + public InfraStudentDO getStudent(Long id) { | |
| 106 | + return studentMapper.selectById(id); | |
| 107 | + } | |
| 108 | + | |
| 109 | + @Override | |
| 110 | + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) { | |
| 111 | + return studentMapper.selectPage(pageReqVO); | |
| 112 | + } | |
| 113 | + | |
| 114 | + // ==================== 子表(学生联系人) ==================== | |
| 115 | + | |
| 116 | + @Override | |
| 117 | + public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) { | |
| 118 | + return studentContactMapper.selectListByStudentId(studentId); | |
| 119 | + } | |
| 120 | + | |
| 121 | + private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) { | |
| 122 | + if (CollUtil.isEmpty(list)) { | |
| 123 | + return; | |
| 124 | + } | |
| 125 | + list.forEach(o -> o.setStudentId(studentId).clean()); | |
| 126 | + studentContactMapper.insertBatch(list); | |
| 127 | + } | |
| 128 | + | |
| 129 | + private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) { | |
| 130 | + list.forEach(o -> o.setStudentId(studentId).clean()); | |
| 131 | + List<InfraStudentContactDO> oldList = studentContactMapper.selectListByStudentId(studentId); | |
| 132 | + List<List<InfraStudentContactDO>> diffList = diffList(oldList, list, (oldVal, newVal) -> { | |
| 133 | + boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId()); | |
| 134 | + if (same) { | |
| 135 | + newVal.setId(oldVal.getId()).clean(); // 解决更新情况下:updateTime 不更新 | |
| 136 | + } | |
| 137 | + return same; | |
| 138 | + }); | |
| 139 | + | |
| 140 | + // 第二步,批量添加、修改、删除 | |
| 141 | + if (CollUtil.isNotEmpty(diffList.get(0))) { | |
| 142 | + studentContactMapper.insertBatch(diffList.get(0)); | |
| 143 | + } | |
| 144 | + if (CollUtil.isNotEmpty(diffList.get(1))) { | |
| 145 | + studentContactMapper.updateBatch(diffList.get(1)); | |
| 146 | + } | |
| 147 | + if (CollUtil.isNotEmpty(diffList.get(2))) { | |
| 148 | + studentContactMapper.deleteByIds(convertList(diffList.get(2), InfraStudentContactDO::getId)); | |
| 149 | + } | |
| 150 | + } | |
| 151 | + | |
| 152 | + private void deleteStudentContactByStudentId(Long studentId) { | |
| 153 | + studentContactMapper.deleteByStudentId(studentId); | |
| 154 | + } | |
| 155 | + | |
| 156 | + private void deleteStudentContactByStudentIds(List<Long> studentIds) { | |
| 157 | + studentContactMapper.deleteByStudentIds(studentIds); | |
| 158 | + } | |
| 159 | + | |
| 160 | + // ==================== 子表(学生班主任) ==================== | |
| 161 | + | |
| 162 | + @Override | |
| 163 | + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { | |
| 164 | + return studentTeacherMapper.selectByStudentId(studentId); | |
| 165 | + } | |
| 166 | + | |
| 167 | + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { | |
| 168 | + if (studentTeacher == null) { | |
| 169 | + return; | |
| 170 | + } | |
| 171 | + studentTeacher.setStudentId(studentId); | |
| 172 | + studentTeacherMapper.insert(studentTeacher); | |
| 173 | + } | |
| 174 | + | |
| 175 | + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { | |
| 176 | + if (studentTeacher == null) { | |
| 177 | + return; | |
| 178 | + } | |
| 179 | + studentTeacher.setStudentId(studentId).clean();// 解决更新情况下:updateTime 不更新 | |
| 180 | + studentTeacherMapper.insertOrUpdate(studentTeacher); | |
| 181 | + } | |
| 182 | + | |
| 183 | + private void deleteStudentTeacherByStudentId(Long studentId) { | |
| 184 | + studentTeacherMapper.deleteByStudentId(studentId); | |
| 185 | + } | |
| 186 | + | |
| 187 | + private void deleteStudentTeacherByStudentIds(List<Long> studentIds) { | |
| 188 | + studentTeacherMapper.deleteByStudentIds(studentIds); | |
| 189 | + } | |
| 190 | + | |
| 191 | +} | |
| 0 | 192 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | + | |
| 8 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 9 | + | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; | |
| 12 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; | |
| 13 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 14 | + | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import org.springframework.context.annotation.Import; | |
| 17 | +import java.util.*; | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | + | |
| 20 | +import static cn.hutool.core.util.RandomUtil.*; | |
| 21 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 22 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; | |
| 23 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; | |
| 26 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; | |
| 27 | +import static org.junit.jupiter.api.Assertions.*; | |
| 28 | +import static org.mockito.Mockito.*; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + * {@link InfraStudentServiceImpl} 的单元测试类 | |
| 32 | + * | |
| 33 | + * @author 芋道源码 | |
| 34 | + */ | |
| 35 | +@Import(InfraStudentServiceImpl.class) | |
| 36 | +public class InfraStudentServiceImplTest extends BaseDbUnitTest { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private InfraStudentServiceImpl studentService; | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private InfraStudentMapper studentMapper; | |
| 43 | + | |
| 44 | + @Test | |
| 45 | + public void testCreateStudent_success() { | |
| 46 | + // 准备参数 | |
| 47 | + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); | |
| 48 | + | |
| 49 | + // 调用 | |
| 50 | + Long studentId = studentService.createStudent(createReqVO); | |
| 51 | + // 断言 | |
| 52 | + assertNotNull(studentId); | |
| 53 | + // 校验记录的属性是否正确 | |
| 54 | + InfraStudentDO student = studentMapper.selectById(studentId); | |
| 55 | + assertPojoEquals(createReqVO, student, "id"); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Test | |
| 59 | + public void testUpdateStudent_success() { | |
| 60 | + // mock 数据 | |
| 61 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); | |
| 62 | + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 | |
| 63 | + // 准备参数 | |
| 64 | + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { | |
| 65 | + o.setId(dbStudent.getId()); // 设置更新的 ID | |
| 66 | + }); | |
| 67 | + | |
| 68 | + // 调用 | |
| 69 | + studentService.updateStudent(updateReqVO); | |
| 70 | + // 校验是否更新正确 | |
| 71 | + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 | |
| 72 | + assertPojoEquals(updateReqVO, student); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Test | |
| 76 | + public void testUpdateStudent_notExists() { | |
| 77 | + // 准备参数 | |
| 78 | + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); | |
| 79 | + | |
| 80 | + // 调用, 并断言异常 | |
| 81 | + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Test | |
| 85 | + public void testDeleteStudent_success() { | |
| 86 | + // mock 数据 | |
| 87 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); | |
| 88 | + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 | |
| 89 | + // 准备参数 | |
| 90 | + Long id = dbStudent.getId(); | |
| 91 | + | |
| 92 | + // 调用 | |
| 93 | + studentService.deleteStudent(id); | |
| 94 | + // 校验数据不存在了 | |
| 95 | + assertNull(studentMapper.selectById(id)); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Test | |
| 99 | + public void testDeleteStudent_notExists() { | |
| 100 | + // 准备参数 | |
| 101 | + Long id = randomLongId(); | |
| 102 | + | |
| 103 | + // 调用, 并断言异常 | |
| 104 | + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); | |
| 105 | + } | |
| 106 | + | |
| 107 | + @Test | |
| 108 | + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 109 | + public void testGetStudentPage() { | |
| 110 | + // mock 数据 | |
| 111 | + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 | |
| 112 | + o.setName(null); | |
| 113 | + o.setBirthday(null); | |
| 114 | + o.setSex(null); | |
| 115 | + o.setEnabled(null); | |
| 116 | + o.setCreateTime(null); | |
| 117 | + }); | |
| 118 | + studentMapper.insert(dbStudent); | |
| 119 | + // 测试 name 不匹配 | |
| 120 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); | |
| 121 | + // 测试 birthday 不匹配 | |
| 122 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); | |
| 123 | + // 测试 sex 不匹配 | |
| 124 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); | |
| 125 | + // 测试 enabled 不匹配 | |
| 126 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); | |
| 127 | + // 测试 createTime 不匹配 | |
| 128 | + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); | |
| 129 | + // 准备参数 | |
| 130 | + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); | |
| 131 | + reqVO.setName(null); | |
| 132 | + reqVO.setBirthday(null); | |
| 133 | + reqVO.setSex(null); | |
| 134 | + reqVO.setEnabled(null); | |
| 135 | + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | |
| 136 | + | |
| 137 | + // 调用 | |
| 138 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO); | |
| 139 | + // 断言 | |
| 140 | + assertEquals(1, pageResult.getTotal()); | |
| 141 | + assertEquals(1, pageResult.getList().size()); | |
| 142 | + assertPojoEquals(dbStudent, pageResult.getList().get(0)); | |
| 143 | + } | |
| 144 | + | |
| 145 | +} | |
| 0 | 146 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 72 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + default int deleteByStudentIds(List<Long> studentIds) { | |
| 29 | + return deleteBatch(InfraStudentTeacherDO::getStudentId, studentIds); | |
| 30 | + } | |
| 31 | + | |
| 32 | +} | |
| 0 | 33 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 18 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +SELECT @parentId := LAST_INSERT_ID(); | |
| 13 | + | |
| 14 | +-- 按钮 SQL | |
| 15 | +INSERT INTO system_menu( | |
| 16 | + name, permission, type, sort, parent_id, | |
| 17 | + path, icon, component, status | |
| 18 | +) | |
| 19 | +VALUES ( | |
| 20 | + '学生查询', 'infra:student:query', 3, 1, @parentId, | |
| 21 | + '', '', '', 0 | |
| 22 | +); | |
| 23 | +INSERT INTO system_menu( | |
| 24 | + name, permission, type, sort, parent_id, | |
| 25 | + path, icon, component, status | |
| 26 | +) | |
| 27 | +VALUES ( | |
| 28 | + '学生创建', 'infra:student:create', 3, 2, @parentId, | |
| 29 | + '', '', '', 0 | |
| 30 | +); | |
| 31 | +INSERT INTO system_menu( | |
| 32 | + name, permission, type, sort, parent_id, | |
| 33 | + path, icon, component, status | |
| 34 | +) | |
| 35 | +VALUES ( | |
| 36 | + '学生更新', 'infra:student:update', 3, 3, @parentId, | |
| 37 | + '', '', '', 0 | |
| 38 | +); | |
| 39 | +INSERT INTO system_menu( | |
| 40 | + name, permission, type, sort, parent_id, | |
| 41 | + path, icon, component, status | |
| 42 | +) | |
| 43 | +VALUES ( | |
| 44 | + '学生删除', 'infra:student:delete', 3, 4, @parentId, | |
| 45 | + '', '', '', 0 | |
| 46 | +); | |
| 47 | +INSERT INTO system_menu( | |
| 48 | + name, permission, type, sort, parent_id, | |
| 49 | + path, icon, component, status | |
| 50 | +) | |
| 51 | +VALUES ( | |
| 52 | + '学生导出', 'infra:student:export', 3, 5, @parentId, | |
| 53 | + '', '', '', 0 | |
| 54 | +); | |
| 0 | 55 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/ts/index
0 → 100644
| 1 | +import type { PageParam, PageResult } from '@/http/types' | |
| 2 | +import { http } from '@/http/http' | |
| 3 | + | |
| 4 | +/** 学生联系人信息 */ | |
| 5 | +export interface StudentContact { | |
| 6 | + id?: number | |
| 7 | + studentId?: number | |
| 8 | + name: string | |
| 9 | + description: string | |
| 10 | + birthday: number | |
| 11 | + sex: number | |
| 12 | + enabled: boolean | |
| 13 | + avatar: string | |
| 14 | + video?: string | |
| 15 | + memo: string | |
| 16 | + createTime?: number | |
| 17 | +} | |
| 18 | + | |
| 19 | +/** 学生班主任信息 */ | |
| 20 | +export interface StudentTeacher { | |
| 21 | + id?: number | |
| 22 | + studentId?: number | |
| 23 | + name: string | |
| 24 | + description: string | |
| 25 | + birthday: number | |
| 26 | + sex: number | |
| 27 | + enabled: boolean | |
| 28 | + avatar: string | |
| 29 | + video?: string | |
| 30 | + memo: string | |
| 31 | + createTime?: number | |
| 32 | +} | |
| 33 | + | |
| 34 | +/** 学生信息 */ | |
| 35 | +export interface Student { | |
| 36 | + id?: number | |
| 37 | + name: string | |
| 38 | + description: string | |
| 39 | + birthday: number | |
| 40 | + sex: number | |
| 41 | + enabled: boolean | |
| 42 | + avatar: string | |
| 43 | + video: string | |
| 44 | + memo: string | |
| 45 | + createTime?: number | |
| 46 | + studentContacts?: StudentContact[] | |
| 47 | + studentTeacher?: StudentTeacher | null | |
| 48 | +} | |
| 49 | + | |
| 50 | +/** 获取学生分页列表 */ | |
| 51 | +export function getStudentPage(params: PageParam) { | |
| 52 | + return http.get<PageResult<Student>>('/infra/student/page', params) | |
| 53 | +} | |
| 54 | + | |
| 55 | +/** 获取学生详情 */ | |
| 56 | +export function getStudent(id: number) { | |
| 57 | + return http.get<Student>(`/infra/student/get?id=${id}`) | |
| 58 | +} | |
| 59 | + | |
| 60 | +/** 创建学生 */ | |
| 61 | +export function createStudent(data: Student) { | |
| 62 | + return http.post<number>('/infra/student/create', data) | |
| 63 | +} | |
| 64 | + | |
| 65 | +/** 更新学生 */ | |
| 66 | +export function updateStudent(data: Student) { | |
| 67 | + return http.put<boolean>('/infra/student/update', data) | |
| 68 | +} | |
| 69 | + | |
| 70 | +/** 删除学生 */ | |
| 71 | +export function deleteStudent(id: number) { | |
| 72 | + return http.delete<boolean>(`/infra/student/delete?id=${id}`) | |
| 73 | +} | |
| 74 | + | |
| 75 | +// ==================== 子表:学生联系人 ==================== | |
| 76 | + | |
| 77 | +/** 获取学生联系人列表 */ | |
| 78 | +export function getStudentContactListByStudentId(studentId: number) { | |
| 79 | + return http.get<StudentContact[]>(`/infra/student/student-contact/list-by-student-id?studentId=${studentId}`) | |
| 80 | +} | |
| 81 | + | |
| 82 | +// ==================== 子表:学生班主任 ==================== | |
| 83 | + | |
| 84 | +/** 获取学生班主任 */ | |
| 85 | +export function getStudentTeacherByStudentId(studentId: number) { | |
| 86 | + return http.get<StudentTeacher | null>(`/infra/student/student-teacher/get-by-student-id?studentId=${studentId}`) | |
| 87 | +} | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/vue/detail/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="学生详情" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <view class="pb-160rpx"> | |
| 11 | + <!-- 主表详情 --> | |
| 12 | + <wd-cell-group border title="学生信息"> | |
| 13 | + <wd-cell title="编号" :value="formData?.id ?? '-'" /> | |
| 14 | + <wd-cell title="名字" :value="formData?.name ?? '-'" /> | |
| 15 | + <wd-cell title="简介" :value="formData?.description ?? '-'" /> | |
| 16 | + <wd-cell title="出生日期" :value="formatDateTime(formData?.birthday) || '-'" /> | |
| 17 | + <wd-cell title="性别"> | |
| 18 | + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="formData?.sex" /> | |
| 19 | + </wd-cell> | |
| 20 | + <wd-cell title="是否有效"> | |
| 21 | + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="formData?.enabled" /> | |
| 22 | + </wd-cell> | |
| 23 | + <wd-cell title="头像" :value="formData?.avatar ?? '-'" /> | |
| 24 | + <wd-cell title="附件" :value="formData?.video ?? '-'" /> | |
| 25 | + <wd-cell title="备注" :value="formData?.memo ?? '-'" /> | |
| 26 | + <wd-cell title="创建时间" :value="formatDateTime(formData?.createTime) || '-'" /> | |
| 27 | + </wd-cell-group> | |
| 28 | + | |
| 29 | + <!-- 子表:学生联系人 --> | |
| 30 | + <view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]"> | |
| 31 | + 学生联系人({{ studentContacts.length }}) | |
| 32 | + </view> | |
| 33 | + <view class="px-24rpx"> | |
| 34 | + <view v-if="!studentContacts.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 35 | + 暂无学生联系人 | |
| 36 | + </view> | |
| 37 | + <view | |
| 38 | + v-for="item in studentContacts" | |
| 39 | + :key="item.id" | |
| 40 | + class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 41 | + > | |
| 42 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 43 | + 学生编号:{{ item.studentId ?? '-' }} | |
| 44 | + </view> | |
| 45 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 46 | + 名字:{{ item.name ?? '-' }} | |
| 47 | + </view> | |
| 48 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 49 | + 简介:{{ item.description ?? '-' }} | |
| 50 | + </view> | |
| 51 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 52 | + 出生日期:{{ item.birthday ?? '-' }} | |
| 53 | + </view> | |
| 54 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 55 | + 性别:{{ item.sex ?? '-' }} | |
| 56 | + </view> | |
| 57 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 58 | + 是否有效:{{ item.enabled ?? '-' }} | |
| 59 | + </view> | |
| 60 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 61 | + 头像:{{ item.avatar ?? '-' }} | |
| 62 | + </view> | |
| 63 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 64 | + 附件:{{ item.video ?? '-' }} | |
| 65 | + </view> | |
| 66 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 67 | + 备注:{{ item.memo ?? '-' }} | |
| 68 | + </view> | |
| 69 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 70 | + 创建时间:{{ item.createTime ?? '-' }} | |
| 71 | + </view> | |
| 72 | + </view> | |
| 73 | + </view> | |
| 74 | + <!-- 子表:学生班主任 --> | |
| 75 | + <view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]"> | |
| 76 | + 学生班主任 | |
| 77 | + </view> | |
| 78 | + <view class="px-24rpx"> | |
| 79 | + <view class="rounded-12rpx bg-white p-24rpx shadow-sm"> | |
| 80 | + <template v-if="studentTeacher"> | |
| 81 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 82 | + 学生编号:{{ studentTeacher.studentId ?? '-' }} | |
| 83 | + </view> | |
| 84 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 85 | + 名字:{{ studentTeacher.name ?? '-' }} | |
| 86 | + </view> | |
| 87 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 88 | + 简介:{{ studentTeacher.description ?? '-' }} | |
| 89 | + </view> | |
| 90 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 91 | + 出生日期:{{ studentTeacher.birthday ?? '-' }} | |
| 92 | + </view> | |
| 93 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 94 | + 性别:{{ studentTeacher.sex ?? '-' }} | |
| 95 | + </view> | |
| 96 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 97 | + 是否有效:{{ studentTeacher.enabled ?? '-' }} | |
| 98 | + </view> | |
| 99 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 100 | + 头像:{{ studentTeacher.avatar ?? '-' }} | |
| 101 | + </view> | |
| 102 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 103 | + 附件:{{ studentTeacher.video ?? '-' }} | |
| 104 | + </view> | |
| 105 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 106 | + 备注:{{ studentTeacher.memo ?? '-' }} | |
| 107 | + </view> | |
| 108 | + <view class="mb-8rpx text-28rpx text-[#666]"> | |
| 109 | + 创建时间:{{ studentTeacher.createTime ?? '-' }} | |
| 110 | + </view> | |
| 111 | + </template> | |
| 112 | + <view v-else class="text-center text-26rpx text-[#999]"> | |
| 113 | + 暂无学生班主任 | |
| 114 | + </view> | |
| 115 | + </view> | |
| 116 | + </view> | |
| 117 | + </view> | |
| 118 | + | |
| 119 | + <!-- 底部操作按钮 --> | |
| 120 | + <view class="yd-detail-footer"> | |
| 121 | + <view class="yd-detail-footer-actions"> | |
| 122 | + <wd-button | |
| 123 | + v-if="hasAccessByCodes(['infra:student:update'])" | |
| 124 | + class="flex-1" type="warning" @click="handleEdit" | |
| 125 | + > | |
| 126 | + 编辑 | |
| 127 | + </wd-button> | |
| 128 | + <wd-button | |
| 129 | + v-if="hasAccessByCodes(['infra:student:delete'])" | |
| 130 | + class="flex-1" type="danger" :loading="deleting" @click="handleDelete" | |
| 131 | + > | |
| 132 | + 删除 | |
| 133 | + </wd-button> | |
| 134 | + </view> | |
| 135 | + </view> | |
| 136 | + </view> | |
| 137 | +</template> | |
| 138 | + | |
| 139 | +<script lang="ts" setup> | |
| 140 | +import type { | |
| 141 | + Student, | |
| 142 | + StudentContact, | |
| 143 | + StudentTeacher, | |
| 144 | +} from '@/api/infra/demo' | |
| 145 | +import { onShow } from '@dcloudio/uni-app' | |
| 146 | +import { ref } from 'vue' | |
| 147 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 148 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 149 | +import { | |
| 150 | + deleteStudent, | |
| 151 | + getStudent, | |
| 152 | + getStudentContactListByStudentId, | |
| 153 | + getStudentTeacherByStudentId, | |
| 154 | +} from '@/api/infra/demo' | |
| 155 | +import { useAccess } from '@/hooks/useAccess' | |
| 156 | +import { delay, navigateBackPlus } from '@/utils' | |
| 157 | +import { DICT_TYPE } from '@/utils/constants' | |
| 158 | +import { formatDateTime } from '@/utils/date' | |
| 159 | + | |
| 160 | +const props = defineProps<{ | |
| 161 | + id?: number | |
| 162 | +}>() | |
| 163 | + | |
| 164 | +definePage({ | |
| 165 | + style: { | |
| 166 | + navigationBarTitleText: '', | |
| 167 | + navigationStyle: 'custom', | |
| 168 | + }, | |
| 169 | +}) | |
| 170 | + | |
| 171 | +const { hasAccessByCodes } = useAccess() | |
| 172 | +const dialog = useDialog() | |
| 173 | +const toast = useToast() | |
| 174 | +const formData = ref<Student>() // 主表数据 | |
| 175 | +const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据 | |
| 176 | +const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据 | |
| 177 | +const deleting = ref(false) // 删除状态 | |
| 178 | + | |
| 179 | +/** 返回上一页 */ | |
| 180 | +function handleBack() { | |
| 181 | + navigateBackPlus('/pages-infra/demo/index') | |
| 182 | +} | |
| 183 | + | |
| 184 | +/** 加载主表和子表详情 */ | |
| 185 | +async function getDetail() { | |
| 186 | + if (!props.id || deleting.value) { | |
| 187 | + return | |
| 188 | + } | |
| 189 | + const [data, studentContactData, studentTeacherData] = await Promise.all([ | |
| 190 | + getStudent(props.id), | |
| 191 | + getStudentContactListByStudentId(props.id), | |
| 192 | + getStudentTeacherByStudentId(props.id), | |
| 193 | + ]) | |
| 194 | + formData.value = data | |
| 195 | + studentContacts.value = studentContactData || [] | |
| 196 | + studentTeacher.value = studentTeacherData || null | |
| 197 | +} | |
| 198 | + | |
| 199 | +/** 编辑学生 */ | |
| 200 | +function handleEdit() { | |
| 201 | + uni.navigateTo({ url: `/pages-infra/demo/form/index?id=${props.id}` }) | |
| 202 | +} | |
| 203 | + | |
| 204 | +/** 删除学生 */ | |
| 205 | +async function handleDelete() { | |
| 206 | + if (!props.id) { | |
| 207 | + return | |
| 208 | + } | |
| 209 | + try { | |
| 210 | + await dialog.confirm({ title: '提示', msg: '确定要删除该学生吗?' }) | |
| 211 | + } catch { | |
| 212 | + return | |
| 213 | + } | |
| 214 | + deleting.value = true | |
| 215 | + try { | |
| 216 | + await deleteStudent(props.id) | |
| 217 | + toast.success('删除成功') | |
| 218 | + uni.$emit('infra:demo:reload') | |
| 219 | + delay(handleBack) | |
| 220 | + } finally { | |
| 221 | + deleting.value = false | |
| 222 | + } | |
| 223 | +} | |
| 224 | + | |
| 225 | +/** 进入页面加载(含编辑返回后刷新) */ | |
| 226 | +onShow(() => { | |
| 227 | + getDetail() | |
| 228 | +}) | |
| 229 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/vue/form/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + :title="getTitle" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 表单区域 --> | |
| 11 | + <view> | |
| 12 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 13 | + <wd-cell-group border> | |
| 14 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 15 | + <wd-input | |
| 16 | + v-model="formData.name" | |
| 17 | + clearable | |
| 18 | + placeholder="请输入名字" | |
| 19 | + /> | |
| 20 | + </wd-form-item> | |
| 21 | + <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 22 | + <wd-textarea | |
| 23 | + v-model="formData.description" | |
| 24 | + clearable | |
| 25 | + placeholder="请输入简介" | |
| 26 | + :maxlength="200" | |
| 27 | + show-word-limit | |
| 28 | + /> | |
| 29 | + </wd-form-item> | |
| 30 | + <wd-form-item | |
| 31 | + title="出生日期" | |
| 32 | + title-width="180rpx" | |
| 33 | + prop="birthday" | |
| 34 | + is-link | |
| 35 | + :value="formData.birthday ? formatDateTime(formData.birthday) : ''" | |
| 36 | + placeholder="请选择出生日期" | |
| 37 | + @click="birthdayVisible = true" | |
| 38 | + /> | |
| 39 | + <wd-datetime-picker | |
| 40 | + v-model:visible="birthdayVisible" | |
| 41 | + :model-value="formData.birthday || Date.now()" | |
| 42 | + type="datetime" | |
| 43 | + @update:model-value="value => formData.birthday = value" | |
| 44 | + /> | |
| 45 | + <yd-form-picker | |
| 46 | + v-model="formData.sex" | |
| 47 | + label="性别" | |
| 48 | + label-width="180rpx" | |
| 49 | + prop="sex" | |
| 50 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 51 | + placeholder="请选择性别" | |
| 52 | + /> | |
| 53 | + <wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center> | |
| 54 | + <wd-radio-group v-model="formData.enabled" type="button"> | |
| 55 | + <wd-radio | |
| 56 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 57 | + :key="dict.value" | |
| 58 | + :value="dict.value" | |
| 59 | + > | |
| 60 | + {{ dict.label }} | |
| 61 | + </wd-radio> | |
| 62 | + </wd-radio-group> | |
| 63 | + </wd-form-item> | |
| 64 | + <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 65 | + <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> | |
| 66 | + </wd-form-item> | |
| 67 | + <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 68 | + <yd-upload-file v-model="formData.video" directory="infra/demo" /> | |
| 69 | + </wd-form-item> | |
| 70 | + <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 71 | + <wd-input | |
| 72 | + v-model="formData.memo" | |
| 73 | + clearable | |
| 74 | + placeholder="请输入备注" | |
| 75 | + /> | |
| 76 | + </wd-form-item> | |
| 77 | + </wd-cell-group> | |
| 78 | + </wd-form> | |
| 79 | + | |
| 80 | + <!-- 子表:学生联系人 --> | |
| 81 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 82 | + <text class="text-28rpx text-[#333] font-semibold">学生联系人</text> | |
| 83 | + <wd-button size="small" type="primary" variant="plain" @click="addStudentContact"> | |
| 84 | + 添加 | |
| 85 | + </wd-button> | |
| 86 | + </view> | |
| 87 | + <view class="px-24rpx"> | |
| 88 | + <view v-if="!studentContacts.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 89 | + 暂无学生联系人 | |
| 90 | + </view> | |
| 91 | + <view | |
| 92 | + v-for="(item, index) in studentContacts" | |
| 93 | + :key="index" | |
| 94 | + class="mb-20rpx rounded-12rpx bg-white p-24rpx shadow-sm" | |
| 95 | + > | |
| 96 | + <view class="mb-16rpx flex justify-end"> | |
| 97 | + <wd-button size="small" type="danger" variant="plain" @click="removeStudentContact(index)"> | |
| 98 | + 删除 | |
| 99 | + </wd-button> | |
| 100 | + </view> | |
| 101 | + <wd-form-item title="名字" title-width="180rpx"> | |
| 102 | + <wd-input v-model="item.name" clearable placeholder="请输入名字" /> | |
| 103 | + </wd-form-item> | |
| 104 | + <wd-form-item title="简介" title-width="180rpx"> | |
| 105 | + <wd-textarea v-model="item.description" clearable placeholder="请输入简介" /> | |
| 106 | + </wd-form-item> | |
| 107 | + <wd-form-item | |
| 108 | + title="出生日期" | |
| 109 | + title-width="180rpx" | |
| 110 | + is-link | |
| 111 | + :value="item.birthday ? formatDateTime(item.birthday) : ''" | |
| 112 | + placeholder="请选择出生日期" | |
| 113 | + @click="subDateVisible[`studentContacts.${index}.birthday`] = true" | |
| 114 | + /> | |
| 115 | + <wd-datetime-picker | |
| 116 | + v-model:visible="subDateVisible[`studentContacts.${index}.birthday`]" | |
| 117 | + :model-value="item.birthday || Date.now()" | |
| 118 | + type="datetime" | |
| 119 | + @update:model-value="value => item.birthday = value" | |
| 120 | + /> | |
| 121 | + <yd-form-picker | |
| 122 | + v-model="item.sex" | |
| 123 | + label="性别" | |
| 124 | + label-width="180rpx" | |
| 125 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 126 | + placeholder="请选择性别" | |
| 127 | + /> | |
| 128 | + <wd-form-item title="是否有效" title-width="180rpx" center> | |
| 129 | + <wd-radio-group v-model="item.enabled" type="button"> | |
| 130 | + <wd-radio | |
| 131 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 132 | + :key="dict.value" | |
| 133 | + :value="dict.value" | |
| 134 | + > | |
| 135 | + {{ dict.label }} | |
| 136 | + </wd-radio> | |
| 137 | + </wd-radio-group> | |
| 138 | + </wd-form-item> | |
| 139 | + <wd-form-item title="头像" title-width="180rpx"> | |
| 140 | + <yd-upload-img v-model="item.avatar" directory="infra/demo" /> | |
| 141 | + </wd-form-item> | |
| 142 | + <wd-form-item title="附件" title-width="180rpx"> | |
| 143 | + <yd-upload-file v-model="item.video" directory="infra/demo" /> | |
| 144 | + </wd-form-item> | |
| 145 | + <wd-form-item title="备注" title-width="180rpx"> | |
| 146 | + <wd-input v-model="item.memo" clearable placeholder="请输入备注" /> | |
| 147 | + </wd-form-item> | |
| 148 | + </view> | |
| 149 | + </view> | |
| 150 | + | |
| 151 | + <!-- 子表:学生班主任 --> | |
| 152 | + <view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx"> | |
| 153 | + <text class="text-28rpx text-[#333] font-semibold">学生班主任</text> | |
| 154 | + <wd-button | |
| 155 | + v-if="!studentTeacher" | |
| 156 | + size="small" | |
| 157 | + type="primary" | |
| 158 | + variant="plain" | |
| 159 | + @click="addStudentTeacher()" | |
| 160 | + > | |
| 161 | + 添加 | |
| 162 | + </wd-button> | |
| 163 | + </view> | |
| 164 | + <view class="px-24rpx"> | |
| 165 | + <view v-if="!studentTeacher" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]"> | |
| 166 | + 暂无学生班主任 | |
| 167 | + </view> | |
| 168 | + <view v-else class="rounded-12rpx bg-white p-24rpx shadow-sm"> | |
| 169 | + <wd-form-item title="名字" title-width="180rpx"> | |
| 170 | + <wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" /> | |
| 171 | + </wd-form-item> | |
| 172 | + <wd-form-item title="简介" title-width="180rpx"> | |
| 173 | + <wd-textarea v-model="studentTeacher.description" clearable placeholder="请输入简介" /> | |
| 174 | + </wd-form-item> | |
| 175 | + <wd-form-item | |
| 176 | + title="出生日期" | |
| 177 | + title-width="180rpx" | |
| 178 | + is-link | |
| 179 | + :value="studentTeacher.birthday ? formatDateTime(studentTeacher.birthday) : ''" | |
| 180 | + placeholder="请选择出生日期" | |
| 181 | + @click="subDateVisible['studentTeacher.birthday'] = true" | |
| 182 | + /> | |
| 183 | + <wd-datetime-picker | |
| 184 | + v-model:visible="subDateVisible['studentTeacher.birthday']" | |
| 185 | + :model-value="studentTeacher.birthday || Date.now()" | |
| 186 | + type="datetime" | |
| 187 | + @update:model-value="value => studentTeacher.birthday = value" | |
| 188 | + /> | |
| 189 | + <yd-form-picker | |
| 190 | + v-model="studentTeacher.sex" | |
| 191 | + label="性别" | |
| 192 | + label-width="180rpx" | |
| 193 | + :dict-type="DICT_TYPE.SYSTEM_USER_SEX" | |
| 194 | + placeholder="请选择性别" | |
| 195 | + /> | |
| 196 | + <wd-form-item title="是否有效" title-width="180rpx" center> | |
| 197 | + <wd-radio-group v-model="studentTeacher.enabled" type="button"> | |
| 198 | + <wd-radio | |
| 199 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 200 | + :key="dict.value" | |
| 201 | + :value="dict.value" | |
| 202 | + > | |
| 203 | + {{ dict.label }} | |
| 204 | + </wd-radio> | |
| 205 | + </wd-radio-group> | |
| 206 | + </wd-form-item> | |
| 207 | + <wd-form-item title="头像" title-width="180rpx"> | |
| 208 | + <yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" /> | |
| 209 | + </wd-form-item> | |
| 210 | + <wd-form-item title="附件" title-width="180rpx"> | |
| 211 | + <yd-upload-file v-model="studentTeacher.video" directory="infra/demo" /> | |
| 212 | + </wd-form-item> | |
| 213 | + <wd-form-item title="备注" title-width="180rpx"> | |
| 214 | + <wd-input v-model="studentTeacher.memo" clearable placeholder="请输入备注" /> | |
| 215 | + </wd-form-item> | |
| 216 | + </view> | |
| 217 | + </view> | |
| 218 | + </view> | |
| 219 | + | |
| 220 | + <!-- 底部保存按钮 --> | |
| 221 | + <view class="yd-detail-footer"> | |
| 222 | + <wd-button | |
| 223 | + type="primary" | |
| 224 | + block | |
| 225 | + :loading="formLoading" | |
| 226 | + @click="handleSubmit" | |
| 227 | + > | |
| 228 | + 保存 | |
| 229 | + </wd-button> | |
| 230 | + </view> | |
| 231 | + </view> | |
| 232 | +</template> | |
| 233 | + | |
| 234 | +<script lang="ts" setup> | |
| 235 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 236 | +import type { | |
| 237 | + Student, | |
| 238 | + StudentContact, | |
| 239 | + StudentTeacher, | |
| 240 | +} from '@/api/infra/demo' | |
| 241 | +import { computed, onMounted, ref } from 'vue' | |
| 242 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 243 | +import { | |
| 244 | + createStudent, | |
| 245 | + getStudent, | |
| 246 | + getStudentContactListByStudentId, | |
| 247 | + getStudentTeacherByStudentId, | |
| 248 | + updateStudent, | |
| 249 | +} from '@/api/infra/demo' | |
| 250 | +import { getBoolDictOptions } from '@/hooks/useDict' | |
| 251 | +import { delay, navigateBackPlus } from '@/utils' | |
| 252 | +import { DICT_TYPE } from '@/utils/constants' | |
| 253 | +import { formatDateTime } from '@/utils/date' | |
| 254 | +import { createFormSchema } from '@/utils/wot' | |
| 255 | + | |
| 256 | +const props = defineProps<{ | |
| 257 | + id?: number | |
| 258 | +}>() | |
| 259 | + | |
| 260 | +definePage({ | |
| 261 | + style: { | |
| 262 | + navigationBarTitleText: '', | |
| 263 | + navigationStyle: 'custom', | |
| 264 | + }, | |
| 265 | +}) | |
| 266 | + | |
| 267 | +const toast = useToast() | |
| 268 | +const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') | |
| 269 | +const formLoading = ref(false) // 表单提交状态 | |
| 270 | +const formData = ref<Student>({ | |
| 271 | + id: undefined, | |
| 272 | + name: '', | |
| 273 | + description: '', | |
| 274 | + birthday: 0, | |
| 275 | + sex: 0, | |
| 276 | + enabled: false, | |
| 277 | + avatar: '', | |
| 278 | + video: '', | |
| 279 | + memo: '', | |
| 280 | +}) // 表单数据 | |
| 281 | +const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据 | |
| 282 | +const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据 | |
| 283 | +const birthdayVisible = ref(false) // 出生日期选择器状态 | |
| 284 | +const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态 | |
| 285 | +const formSchema = createFormSchema({ | |
| 286 | + name: [{ required: true, message: '名字不能为空' }], | |
| 287 | + description: [{ required: true, message: '简介不能为空' }], | |
| 288 | + birthday: [{ required: true, message: '出生日期不能为空' }], | |
| 289 | + sex: [{ required: true, message: '性别不能为空' }], | |
| 290 | + enabled: [{ required: true, message: '是否有效不能为空' }], | |
| 291 | + avatar: [{ required: true, message: '头像不能为空' }], | |
| 292 | + video: [{ required: true, message: '附件不能为空' }], | |
| 293 | + memo: [{ required: true, message: '备注不能为空' }], | |
| 294 | +}) // 表单校验规则 | |
| 295 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 296 | + | |
| 297 | +/** 添加学生联系人 */ | |
| 298 | +function addStudentContact() { | |
| 299 | + studentContacts.value.push({ | |
| 300 | + name: '', | |
| 301 | + description: '', | |
| 302 | + birthday: 0, | |
| 303 | + sex: 0, | |
| 304 | + enabled: false, | |
| 305 | + avatar: '', | |
| 306 | + video: undefined, | |
| 307 | + memo: '', | |
| 308 | + }) | |
| 309 | +} | |
| 310 | + | |
| 311 | +/** 删除学生联系人 */ | |
| 312 | +function removeStudentContact(index: number) { | |
| 313 | + studentContacts.value.splice(index, 1) | |
| 314 | +} | |
| 315 | + | |
| 316 | +/** 添加学生班主任 */ | |
| 317 | +function addStudentTeacher() { | |
| 318 | + studentTeacher.value = { | |
| 319 | + name: '', | |
| 320 | + description: '', | |
| 321 | + birthday: 0, | |
| 322 | + sex: 0, | |
| 323 | + enabled: false, | |
| 324 | + avatar: '', | |
| 325 | + video: undefined, | |
| 326 | + memo: '', | |
| 327 | + } | |
| 328 | +} | |
| 329 | + | |
| 330 | +/** 校验主子表数据 */ | |
| 331 | +function isEmptySubValue(value: unknown) { | |
| 332 | + return value === undefined || value === null || value === '' | |
| 333 | +} | |
| 334 | + | |
| 335 | +function validateSubTables() { | |
| 336 | + for (let index = 0; index < studentContacts.value.length; index++) { | |
| 337 | + const item = studentContacts.value[index]! | |
| 338 | + if (isEmptySubValue(item.name)) { | |
| 339 | + toast.warning(`学生联系人第${index + 1}项的名字不能为空`) | |
| 340 | + return false | |
| 341 | + } | |
| 342 | + if (isEmptySubValue(item.description)) { | |
| 343 | + toast.warning(`学生联系人第${index + 1}项的简介不能为空`) | |
| 344 | + return false | |
| 345 | + } | |
| 346 | + if (isEmptySubValue(item.birthday)) { | |
| 347 | + toast.warning(`学生联系人第${index + 1}项的出生日期不能为空`) | |
| 348 | + return false | |
| 349 | + } | |
| 350 | + if (isEmptySubValue(item.sex)) { | |
| 351 | + toast.warning(`学生联系人第${index + 1}项的性别不能为空`) | |
| 352 | + return false | |
| 353 | + } | |
| 354 | + if (isEmptySubValue(item.enabled)) { | |
| 355 | + toast.warning(`学生联系人第${index + 1}项的是否有效不能为空`) | |
| 356 | + return false | |
| 357 | + } | |
| 358 | + if (isEmptySubValue(item.avatar)) { | |
| 359 | + toast.warning(`学生联系人第${index + 1}项的头像不能为空`) | |
| 360 | + return false | |
| 361 | + } | |
| 362 | + if (isEmptySubValue(item.memo)) { | |
| 363 | + toast.warning(`学生联系人第${index + 1}项的备注不能为空`) | |
| 364 | + return false | |
| 365 | + } | |
| 366 | + } | |
| 367 | + if (studentTeacher.value) { | |
| 368 | + if (isEmptySubValue(studentTeacher.value.name)) { | |
| 369 | + toast.warning('学生班主任的名字不能为空') | |
| 370 | + return false | |
| 371 | + } | |
| 372 | + if (isEmptySubValue(studentTeacher.value.description)) { | |
| 373 | + toast.warning('学生班主任的简介不能为空') | |
| 374 | + return false | |
| 375 | + } | |
| 376 | + if (isEmptySubValue(studentTeacher.value.birthday)) { | |
| 377 | + toast.warning('学生班主任的出生日期不能为空') | |
| 378 | + return false | |
| 379 | + } | |
| 380 | + if (isEmptySubValue(studentTeacher.value.sex)) { | |
| 381 | + toast.warning('学生班主任的性别不能为空') | |
| 382 | + return false | |
| 383 | + } | |
| 384 | + if (isEmptySubValue(studentTeacher.value.enabled)) { | |
| 385 | + toast.warning('学生班主任的是否有效不能为空') | |
| 386 | + return false | |
| 387 | + } | |
| 388 | + if (isEmptySubValue(studentTeacher.value.avatar)) { | |
| 389 | + toast.warning('学生班主任的头像不能为空') | |
| 390 | + return false | |
| 391 | + } | |
| 392 | + if (isEmptySubValue(studentTeacher.value.memo)) { | |
| 393 | + toast.warning('学生班主任的备注不能为空') | |
| 394 | + return false | |
| 395 | + } | |
| 396 | + } | |
| 397 | + return true | |
| 398 | +} | |
| 399 | + | |
| 400 | +/** 返回上一页 */ | |
| 401 | +function handleBack() { | |
| 402 | + navigateBackPlus('/pages-infra/demo/index') | |
| 403 | +} | |
| 404 | + | |
| 405 | +/** 加载学生详情 */ | |
| 406 | +async function getDetail() { | |
| 407 | + if (!props.id) { | |
| 408 | + return | |
| 409 | + } | |
| 410 | + const [data, studentContactData, studentTeacherData] = await Promise.all([ | |
| 411 | + getStudent(props.id), | |
| 412 | + getStudentContactListByStudentId(props.id), | |
| 413 | + getStudentTeacherByStudentId(props.id), | |
| 414 | + ]) | |
| 415 | + formData.value = data | |
| 416 | + studentContacts.value = studentContactData || [] | |
| 417 | + studentTeacher.value = studentTeacherData || null | |
| 418 | +} | |
| 419 | + | |
| 420 | +/** 提交表单 */ | |
| 421 | +async function handleSubmit() { | |
| 422 | + const { valid } = await formRef.value.validate() | |
| 423 | + if (!valid) { | |
| 424 | + return | |
| 425 | + } | |
| 426 | + if (!validateSubTables()) { | |
| 427 | + return | |
| 428 | + } | |
| 429 | + | |
| 430 | + formLoading.value = true | |
| 431 | + try { | |
| 432 | + const data: Student = { | |
| 433 | + ...formData.value, | |
| 434 | + studentContacts: studentContacts.value, | |
| 435 | + studentTeacher: studentTeacher.value, | |
| 436 | + } | |
| 437 | + if (props.id) { | |
| 438 | + await updateStudent(data) | |
| 439 | + toast.success('修改成功') | |
| 440 | + } else { | |
| 441 | + await createStudent(data) | |
| 442 | + toast.success('新增成功') | |
| 443 | + } | |
| 444 | + uni.$emit('infra:demo:reload') | |
| 445 | + delay(handleBack) | |
| 446 | + } finally { | |
| 447 | + formLoading.value = false | |
| 448 | + } | |
| 449 | +} | |
| 450 | + | |
| 451 | +/** 初始化 */ | |
| 452 | +onMounted(async () => { | |
| 453 | + getDetail() | |
| 454 | +}) | |
| 455 | +</script> | |
| 456 | + | |
| 457 | +<style lang="scss" scoped> | |
| 458 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/vue/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container yd-page-container-paging"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="学生管理" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 搜索组件 --> | |
| 11 | + <SearchForm @search="handleQuery" @reset="handleReset" /> | |
| 12 | + | |
| 13 | + <!-- 学生列表 --> | |
| 14 | + <z-paging | |
| 15 | + ref="pagingRef" | |
| 16 | + v-model="list" | |
| 17 | + :fixed="false" | |
| 18 | + class="min-h-0 flex-1" | |
| 19 | + :default-page-size="10" | |
| 20 | + :refresher-enabled="true" | |
| 21 | + :inside-more="true" | |
| 22 | + :loading-more-default-as-loading="true" | |
| 23 | + empty-view-text="暂无学生数据" | |
| 24 | + @query="queryList" | |
| 25 | + > | |
| 26 | + <view class="p-24rpx"> | |
| 27 | + <view | |
| 28 | + v-for="item in list" | |
| 29 | + :key="item.id" | |
| 30 | + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 31 | + @click="handleDetail(item)" | |
| 32 | + > | |
| 33 | + <view class="p-24rpx"> | |
| 34 | + <view class="mb-16rpx flex items-center justify-between"> | |
| 35 | + <view class="text-32rpx text-[#333] font-semibold"> | |
| 36 | + {{ item.name }} | |
| 37 | + </view> | |
| 38 | + <dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="item.sex" /> | |
| 39 | + </view> | |
| 40 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 41 | + <text class="mr-8rpx text-[#999]">简介:</text> | |
| 42 | + <text class="line-clamp-1">{{ item.description }}</text> | |
| 43 | + </view> | |
| 44 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 45 | + <text class="mr-8rpx text-[#999]">出生日期:</text> | |
| 46 | + <text class="line-clamp-1">{{ formatDateTime(item.birthday) || '-' }}</text> | |
| 47 | + </view> | |
| 48 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 49 | + <text class="mr-8rpx text-[#999]">是否有效:</text> | |
| 50 | + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="item.enabled" /> | |
| 51 | + </view> | |
| 52 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 53 | + <text class="mr-8rpx text-[#999]">头像:</text> | |
| 54 | + <text class="line-clamp-1">{{ item.avatar }}</text> | |
| 55 | + </view> | |
| 56 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 57 | + <text class="mr-8rpx text-[#999]">附件:</text> | |
| 58 | + <text class="line-clamp-1">{{ item.video }}</text> | |
| 59 | + </view> | |
| 60 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 61 | + <text class="mr-8rpx text-[#999]">备注:</text> | |
| 62 | + <text class="line-clamp-1">{{ item.memo }}</text> | |
| 63 | + </view> | |
| 64 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 65 | + <text class="mr-8rpx text-[#999]">创建时间:</text> | |
| 66 | + <text class="line-clamp-1">{{ formatDateTime(item.createTime) || '-' }}</text> | |
| 67 | + </view> | |
| 68 | + </view> | |
| 69 | + </view> | |
| 70 | + </view> | |
| 71 | + </z-paging> | |
| 72 | + | |
| 73 | + <!-- 新增按钮 --> | |
| 74 | + <wd-fab | |
| 75 | + v-if="hasAccessByCodes(['infra:student:create'])" | |
| 76 | + position="right-bottom" | |
| 77 | + type="primary" | |
| 78 | + :expandable="false" | |
| 79 | + @click="handleAdd" | |
| 80 | + /> | |
| 81 | + </view> | |
| 82 | +</template> | |
| 83 | + | |
| 84 | +<script lang="ts" setup> | |
| 85 | +import type { Student } from '@/api/infra/demo' | |
| 86 | +import { onUnload } from '@dcloudio/uni-app' | |
| 87 | +import { onMounted, ref } from 'vue' | |
| 88 | +import { getStudentPage } from '@/api/infra/demo' | |
| 89 | +import { useAccess } from '@/hooks/useAccess' | |
| 90 | +import { navigateBackPlus } from '@/utils' | |
| 91 | +import { DICT_TYPE } from '@/utils/constants' | |
| 92 | +import { formatDateTime } from '@/utils/date' | |
| 93 | +import SearchForm from './components/search-form.vue' | |
| 94 | + | |
| 95 | +definePage({ | |
| 96 | + style: { | |
| 97 | + navigationBarTitleText: '', | |
| 98 | + navigationStyle: 'custom', | |
| 99 | + }, | |
| 100 | +}) | |
| 101 | + | |
| 102 | +const { hasAccessByCodes } = useAccess() | |
| 103 | +const list = ref<Student[]>([]) // 列表数据 | |
| 104 | +const pagingRef = ref<any>() // 分页组件引用 | |
| 105 | +const queryParams = ref<Record<string, any>>({}) // 查询参数 | |
| 106 | + | |
| 107 | +/** 返回上一页 */ | |
| 108 | +function handleBack() { | |
| 109 | + navigateBackPlus() | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** 查询学生列表 */ | |
| 113 | +async function queryList(pageNo: number, pageSize: number) { | |
| 114 | + try { | |
| 115 | + const params = { | |
| 116 | + ...queryParams.value, | |
| 117 | + pageNo, | |
| 118 | + pageSize, | |
| 119 | + } | |
| 120 | + const data = await getStudentPage(params) | |
| 121 | + pagingRef.value?.completeByTotal(data.list, data.total) | |
| 122 | + } catch { | |
| 123 | + pagingRef.value?.complete(false) | |
| 124 | + } | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** 搜索按钮操作 */ | |
| 128 | +function handleQuery(data?: Record<string, any>) { | |
| 129 | + queryParams.value = { ...data } | |
| 130 | + reload() | |
| 131 | +} | |
| 132 | + | |
| 133 | +/** 重置按钮操作 */ | |
| 134 | +function handleReset() { | |
| 135 | + handleQuery() | |
| 136 | +} | |
| 137 | + | |
| 138 | +/** 重新加载 */ | |
| 139 | +function reload() { | |
| 140 | + pagingRef.value?.reload() | |
| 141 | +} | |
| 142 | + | |
| 143 | +/** 新增学生 */ | |
| 144 | +function handleAdd() { | |
| 145 | + uni.navigateTo({ | |
| 146 | + url: '/pages-infra/demo/form/index', | |
| 147 | + }) | |
| 148 | +} | |
| 149 | + | |
| 150 | +/** 查看详情 */ | |
| 151 | +function handleDetail(item: Student) { | |
| 152 | + uni.navigateTo({ | |
| 153 | + url: `/pages-infra/demo/detail/index?id=${item.id}`, | |
| 154 | + }) | |
| 155 | +} | |
| 156 | + | |
| 157 | +/** 初始化 */ | |
| 158 | +onMounted(() => { | |
| 159 | + uni.$on('infra:demo:reload', reload) | |
| 160 | +}) | |
| 161 | + | |
| 162 | +/** 卸载 */ | |
| 163 | +onUnload(() => { | |
| 164 | + uni.$off('infra:demo:reload', reload) | |
| 165 | +}) | |
| 166 | +</script> | |
| 167 | + | |
| 168 | +<style lang="scss" scoped> | |
| 169 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_master_normal/vue/search-form
0 → 100644
| 1 | +<template> | |
| 2 | + <!-- 搜索框入口 --> | |
| 3 | + <view @click="visible = true"> | |
| 4 | + <wd-search :placeholder="placeholder" hide-cancel disabled /> | |
| 5 | + </view> | |
| 6 | + | |
| 7 | + <!-- 搜索弹窗 --> | |
| 8 | + <wd-popup v-model="visible" position="top" @close="visible = false"> | |
| 9 | + <view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }"> | |
| 10 | + <view class="yd-search-form-item"> | |
| 11 | + <view class="yd-search-form-label"> | |
| 12 | + 名字 | |
| 13 | + </view> | |
| 14 | + <wd-input | |
| 15 | + v-model="formData.name" | |
| 16 | + placeholder="请输入名字" | |
| 17 | + clearable | |
| 18 | + /> | |
| 19 | + </view> | |
| 20 | + <view class="yd-search-form-item"> | |
| 21 | + <view class="yd-search-form-label"> | |
| 22 | + 出生日期 | |
| 23 | + </view> | |
| 24 | + <wd-input | |
| 25 | + v-model="formData.birthday" | |
| 26 | + placeholder="请输入出生日期" | |
| 27 | + clearable | |
| 28 | + /> | |
| 29 | + </view> | |
| 30 | + <yd-search-picker | |
| 31 | + v-model="formData.sex" | |
| 32 | + label="性别" | |
| 33 | + :columns="getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" | |
| 34 | + all-option | |
| 35 | + /> | |
| 36 | + <yd-search-picker | |
| 37 | + v-model="formData.enabled" | |
| 38 | + label="是否有效" | |
| 39 | + :columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 40 | + all-option | |
| 41 | + /> | |
| 42 | + <yd-search-date-range v-model="formData.createTime" label="创建时间" /> | |
| 43 | + <view class="yd-search-form-actions"> | |
| 44 | + <wd-button class="flex-1" plain @click="handleReset"> | |
| 45 | + 重置 | |
| 46 | + </wd-button> | |
| 47 | + <wd-button class="flex-1" type="primary" @click="handleSearch"> | |
| 48 | + 搜索 | |
| 49 | + </wd-button> | |
| 50 | + </view> | |
| 51 | + </view> | |
| 52 | + </wd-popup> | |
| 53 | +</template> | |
| 54 | + | |
| 55 | +<script lang="ts" setup> | |
| 56 | +import { computed, reactive, ref } from 'vue' | |
| 57 | +import { getNavbarHeight } from '@/utils' | |
| 58 | +import { formatDate, formatDateRange } from '@/utils/date' | |
| 59 | +import { getDictLabel, getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict' | |
| 60 | +import { DICT_TYPE } from '@/utils/constants' | |
| 61 | + | |
| 62 | +const emit = defineEmits<{ | |
| 63 | + search: [data: Record<string, any>] | |
| 64 | + reset: [] | |
| 65 | +}>() | |
| 66 | + | |
| 67 | +const visible = ref(false) // 搜索弹窗显示状态 | |
| 68 | +const formData = reactive({ | |
| 69 | + name: undefined as string | undefined, | |
| 70 | + birthday: undefined as string | undefined, | |
| 71 | + sex: undefined as number | undefined, | |
| 72 | + enabled: undefined as boolean | undefined, | |
| 73 | + createTime: [undefined, undefined] as [number | undefined, number | undefined], | |
| 74 | +}) // 搜索表单数据 | |
| 75 | + | |
| 76 | +/** 搜索条件 placeholder 拼接 */ | |
| 77 | +const placeholder = computed(() => { | |
| 78 | + const conditions: string[] = [] | |
| 79 | + if (formData.name) { | |
| 80 | + conditions.push(`名字:${formData.name}`) | |
| 81 | + } | |
| 82 | + if (formData.birthday) { | |
| 83 | + conditions.push(`出生日期:${formData.birthday}`) | |
| 84 | + } | |
| 85 | + if (formData.sex !== undefined) { | |
| 86 | + conditions.push(`性别:${getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, formData.sex)}`) | |
| 87 | + } | |
| 88 | + if (formData.enabled !== undefined) { | |
| 89 | + conditions.push(`是否有效:${getDictLabel(DICT_TYPE.INFRA_BOOLEAN_STRING, formData.enabled)}`) | |
| 90 | + } | |
| 91 | + if (formData.createTime?.[0] && formData.createTime?.[1]) { | |
| 92 | + conditions.push(`创建时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`) | |
| 93 | + } | |
| 94 | + return conditions.length > 0 ? conditions.join(' | ') : '搜索学生' | |
| 95 | +}) | |
| 96 | + | |
| 97 | +/** 搜索按钮操作 */ | |
| 98 | +function handleSearch() { | |
| 99 | + visible.value = false | |
| 100 | + emit('search', { | |
| 101 | + ...formData, | |
| 102 | + createTime: formatDateRange(formData.createTime), | |
| 103 | + }) | |
| 104 | +} | |
| 105 | + | |
| 106 | +/** 重置按钮操作 */ | |
| 107 | +function handleReset() { | |
| 108 | + formData.name = undefined | |
| 109 | + formData.birthday = undefined | |
| 110 | + formData.sex = undefined | |
| 111 | + formData.enabled = undefined | |
| 112 | + formData.createTime = [undefined, undefined] | |
| 113 | + visible.value = false | |
| 114 | + emit('reset') | |
| 115 | +} | |
| 116 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 13 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/ts/index
| ... | ... | @@ -6,13 +6,13 @@ export interface Student { |
| 6 | 6 | id?: number |
| 7 | 7 | name: string |
| 8 | 8 | description: string |
| 9 | - birthday: Date | |
| 9 | + birthday: number | |
| 10 | 10 | sex: number |
| 11 | 11 | enabled: boolean |
| 12 | 12 | avatar: string |
| 13 | 13 | video: string |
| 14 | 14 | memo: string |
| 15 | - createTime?: Date | |
| 15 | + createTime?: number | |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | 18 | /** 获取学生分页列表 */ |
| ... | ... | @@ -38,4 +38,4 @@ export function updateStudent(data: Student) { |
| 38 | 38 | /** 删除学生 */ |
| 39 | 39 | export function deleteStudent(id: number) { |
| 40 | 40 | return http.delete<boolean>(`/infra/student/delete?id=${id}`) |
| 41 | -} | |
| 42 | 41 | \ No newline at end of file |
| 42 | +} | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/detail/index
| ... | ... | @@ -38,7 +38,7 @@ |
| 38 | 38 | </wd-button> |
| 39 | 39 | <wd-button |
| 40 | 40 | v-if="hasAccessByCodes(['infra:student:delete'])" |
| 41 | - class="flex-1" type="error" :loading="deleting" @click="handleDelete" | |
| 41 | + class="flex-1" type="danger" :loading="deleting" @click="handleDelete" | |
| 42 | 42 | > |
| 43 | 43 | 删除 |
| 44 | 44 | </wd-button> |
| ... | ... | @@ -51,6 +51,7 @@ |
| 51 | 51 | import type { Student } from '@/api/infra/demo' |
| 52 | 52 | import { onUnload } from '@dcloudio/uni-app' |
| 53 | 53 | import { onMounted, ref } from 'vue' |
| 54 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 54 | 55 | import { useToast } from '@wot-ui/ui/components/wd-toast' |
| 55 | 56 | import { deleteStudent, getStudent } from '@/api/infra/demo' |
| 56 | 57 | import { useAccess } from '@/hooks/useAccess' |
| ... | ... | @@ -59,7 +60,7 @@ import { DICT_TYPE } from '@/utils/constants' |
| 59 | 60 | import { formatDateTime } from '@/utils/date' |
| 60 | 61 | |
| 61 | 62 | const props = defineProps<{ |
| 62 | - id?: number | any | |
| 63 | + id?: number | |
| 63 | 64 | }>() |
| 64 | 65 | |
| 65 | 66 | definePage({ |
| ... | ... | @@ -70,6 +71,7 @@ definePage({ |
| 70 | 71 | }) |
| 71 | 72 | |
| 72 | 73 | const { hasAccessByCodes } = useAccess() |
| 74 | +const dialog = useDialog() | |
| 73 | 75 | const toast = useToast() |
| 74 | 76 | const formData = ref<Student>() // 详情数据 |
| 75 | 77 | const deleting = ref(false) // 删除状态 |
| ... | ... | @@ -100,28 +102,27 @@ function handleEdit() { |
| 100 | 102 | } |
| 101 | 103 | |
| 102 | 104 | /** 删除学生 */ |
| 103 | -function handleDelete() { | |
| 105 | +async function handleDelete() { | |
| 104 | 106 | if (!props.id) { |
| 105 | 107 | return |
| 106 | 108 | } |
| 107 | - uni.showModal({ | |
| 108 | - title: '提示', | |
| 109 | - content: '确定要删除该学生吗?', | |
| 110 | - success: async (res) => { | |
| 111 | - if (!res.confirm) { | |
| 112 | - return | |
| 113 | - } | |
| 114 | - deleting.value = true | |
| 115 | - try { | |
| 116 | - await deleteStudent(props.id) | |
| 117 | - toast.success('删除成功') | |
| 118 | - uni.$emit('infra:demo:reload') | |
| 119 | - delay(handleBack) | |
| 120 | - } finally { | |
| 121 | - deleting.value = false | |
| 122 | - } | |
| 123 | - }, | |
| 124 | - }) | |
| 109 | + try { | |
| 110 | + await dialog.confirm({ | |
| 111 | + title: '提示', | |
| 112 | + msg: '确定要删除该学生吗?', | |
| 113 | + }) | |
| 114 | + } catch { | |
| 115 | + return | |
| 116 | + } | |
| 117 | + deleting.value = true | |
| 118 | + try { | |
| 119 | + await deleteStudent(props.id) | |
| 120 | + toast.success('删除成功') | |
| 121 | + uni.$emit('infra:demo:reload') | |
| 122 | + delay(handleBack) | |
| 123 | + } finally { | |
| 124 | + deleting.value = false | |
| 125 | + } | |
| 125 | 126 | } |
| 126 | 127 | |
| 127 | 128 | /** 初始化 */ | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/form/index
| ... | ... | @@ -11,14 +11,14 @@ |
| 11 | 11 | <view> |
| 12 | 12 | <wd-form ref="formRef" :model="formData" :schema="formSchema"> |
| 13 | 13 | <wd-cell-group border> |
| 14 | - <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 14 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 15 | 15 | <wd-input |
| 16 | 16 | v-model="formData.name" |
| 17 | 17 | clearable |
| 18 | 18 | placeholder="请输入名字" |
| 19 | 19 | /> |
| 20 | 20 | </wd-form-item> |
| 21 | - <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 21 | + <wd-form-item title="简介" title-width="180rpx" prop="description"> | |
| 22 | 22 | <wd-textarea |
| 23 | 23 | v-model="formData.description" |
| 24 | 24 | clearable |
| ... | ... | @@ -27,21 +27,22 @@ |
| 27 | 27 | show-word-limit |
| 28 | 28 | /> |
| 29 | 29 | </wd-form-item> |
| 30 | - <wd-form-item | |
| 30 | + <wd-form-item | |
| 31 | 31 | title="出生日期" |
| 32 | 32 | title-width="180rpx" |
| 33 | 33 | prop="birthday" |
| 34 | 34 | is-link |
| 35 | - :value="formatDateTime(formData.birthday)" | |
| 35 | + :value="formData.birthday ? formatDateTime(formData.birthday) : ''" | |
| 36 | 36 | placeholder="请选择出生日期" |
| 37 | 37 | @click="birthdayVisible = true" |
| 38 | 38 | /> |
| 39 | 39 | <wd-datetime-picker |
| 40 | - v-model="formData.birthday" | |
| 41 | 40 | v-model:visible="birthdayVisible" |
| 41 | + :model-value="formData.birthday || Date.now()" | |
| 42 | 42 | type="datetime" |
| 43 | + @update:model-value="value => formData.birthday = value" | |
| 43 | 44 | /> |
| 44 | - <yd-form-picker | |
| 45 | + <yd-form-picker | |
| 45 | 46 | v-model="formData.sex" |
| 46 | 47 | label="性别" |
| 47 | 48 | label-width="180rpx" |
| ... | ... | @@ -49,7 +50,7 @@ |
| 49 | 50 | :dict-type="DICT_TYPE.SYSTEM_USER_SEX" |
| 50 | 51 | placeholder="请选择性别" |
| 51 | 52 | /> |
| 52 | - <wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center> | |
| 53 | + <wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center> | |
| 53 | 54 | <wd-radio-group v-model="formData.enabled" type="button"> |
| 54 | 55 | <wd-radio |
| 55 | 56 | v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" |
| ... | ... | @@ -60,13 +61,13 @@ |
| 60 | 61 | </wd-radio> |
| 61 | 62 | </wd-radio-group> |
| 62 | 63 | </wd-form-item> |
| 63 | - <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 64 | + <wd-form-item title="头像" title-width="180rpx" prop="avatar"> | |
| 64 | 65 | <yd-upload-img v-model="formData.avatar" directory="infra/demo" /> |
| 65 | 66 | </wd-form-item> |
| 66 | - <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 67 | + <wd-form-item title="附件" title-width="180rpx" prop="video"> | |
| 67 | 68 | <yd-upload-file v-model="formData.video" directory="infra/demo" /> |
| 68 | 69 | </wd-form-item> |
| 69 | - <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 70 | + <wd-form-item title="备注" title-width="180rpx" prop="memo"> | |
| 70 | 71 | <wd-input |
| 71 | 72 | v-model="formData.memo" |
| 72 | 73 | clearable |
| ... | ... | @@ -93,7 +94,9 @@ |
| 93 | 94 | |
| 94 | 95 | <script lang="ts" setup> |
| 95 | 96 | import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' |
| 96 | -import type { Student } from '@/api/infra/demo' | |
| 97 | +import type { | |
| 98 | + Student, | |
| 99 | +} from '@/api/infra/demo' | |
| 97 | 100 | import { computed, onMounted, ref } from 'vue' |
| 98 | 101 | import { useToast } from '@wot-ui/ui/components/wd-toast' |
| 99 | 102 | import { createStudent, getStudent, updateStudent } from '@/api/infra/demo' |
| ... | ... | @@ -104,7 +107,7 @@ import { formatDateTime } from '@/utils/date' |
| 104 | 107 | import { createFormSchema } from '@/utils/wot' |
| 105 | 108 | |
| 106 | 109 | const props = defineProps<{ |
| 107 | - id?: number | any | |
| 110 | + id?: number | |
| 108 | 111 | }>() |
| 109 | 112 | |
| 110 | 113 | definePage({ |
| ... | ... | @@ -121,7 +124,7 @@ const formData = ref<Student>({ |
| 121 | 124 | id: undefined, |
| 122 | 125 | name: '', |
| 123 | 126 | description: '', |
| 124 | - birthday: undefined, | |
| 127 | + birthday: 0, | |
| 125 | 128 | sex: 0, |
| 126 | 129 | enabled: false, |
| 127 | 130 | avatar: '', |
| ... | ... | @@ -163,11 +166,12 @@ async function handleSubmit() { |
| 163 | 166 | |
| 164 | 167 | formLoading.value = true |
| 165 | 168 | try { |
| 169 | + const data = formData.value | |
| 166 | 170 | if (props.id) { |
| 167 | - await updateStudent(formData.value) | |
| 171 | + await updateStudent(data) | |
| 168 | 172 | toast.success('修改成功') |
| 169 | 173 | } else { |
| 170 | - await createStudent(formData.value) | |
| 174 | + await createStudent(data) | |
| 171 | 175 | toast.success('新增成功') |
| 172 | 176 | } |
| 173 | 177 | uni.$emit('infra:demo:reload') |
| ... | ... | @@ -178,7 +182,7 @@ async function handleSubmit() { |
| 178 | 182 | } |
| 179 | 183 | |
| 180 | 184 | /** 初始化 */ |
| 181 | -onMounted(() => { | |
| 185 | +onMounted(async () => { | |
| 182 | 186 | getDetail() |
| 183 | 187 | }) |
| 184 | 188 | </script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/assert.json
0 → 100644
| 1 | +[ { | |
| 2 | + "contentPath" : "java/InfraCategoryListReqVO", | |
| 3 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java" | |
| 4 | +}, { | |
| 5 | + "contentPath" : "java/InfraCategoryRespVO", | |
| 6 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java" | |
| 7 | +}, { | |
| 8 | + "contentPath" : "java/InfraCategorySaveReqVO", | |
| 9 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java" | |
| 10 | +}, { | |
| 11 | + "contentPath" : "java/InfraCategoryController", | |
| 12 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java" | |
| 13 | +}, { | |
| 14 | + "contentPath" : "java/InfraCategoryDO", | |
| 15 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java" | |
| 16 | +}, { | |
| 17 | + "contentPath" : "java/InfraCategoryMapper", | |
| 18 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java" | |
| 19 | +}, { | |
| 20 | + "contentPath" : "xml/InfraCategoryMapper", | |
| 21 | + "filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraCategoryMapper.xml" | |
| 22 | +}, { | |
| 23 | + "contentPath" : "java/InfraCategoryServiceImpl", | |
| 24 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java" | |
| 25 | +}, { | |
| 26 | + "contentPath" : "java/InfraCategoryService", | |
| 27 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java" | |
| 28 | +}, { | |
| 29 | + "contentPath" : "java/InfraCategoryServiceImplTest", | |
| 30 | + "filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java" | |
| 31 | +}, { | |
| 32 | + "contentPath" : "java/ErrorCodeConstants_手动操作", | |
| 33 | + "filePath" : "yudao-module-infra/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" : "ts/index", | |
| 42 | + "filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts" | |
| 43 | +}, { | |
| 44 | + "contentPath" : "vue/search-form", | |
| 45 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue" | |
| 46 | +}, { | |
| 47 | + "contentPath" : "vue/form/index", | |
| 48 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue" | |
| 49 | +}, { | |
| 50 | + "contentPath" : "vue/detail/index", | |
| 51 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue" | |
| 52 | +}, { | |
| 53 | + "contentPath" : "vue/index", | |
| 54 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue" | |
| 55 | +}, { | |
| 56 | + "contentPath" : "vue/breadcrumb", | |
| 57 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/breadcrumb.vue" | |
| 58 | +} ] | |
| 0 | 59 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/java/ErrorCodeConstants_手动操作
0 → 100644
| 1 | +// TODO 待办:请将下面的错误码复制到 yudao-module-infra 模块的 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 | 9 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.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 jakarta.validation.constraints.*; | |
| 12 | +import jakarta.validation.*; | |
| 13 | +import jakarta.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.apilog.core.annotation.ApiAccessLog; | |
| 26 | +import static cn.iocoder.yudao.framework.apilog.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 | + | |
| 66 | + @GetMapping("/get") | |
| 67 | + @Operation(summary = "获得分类") | |
| 68 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 69 | + @PreAuthorize("@ss.hasPermission('infra:category:query')") | |
| 70 | + public CommonResult<InfraCategoryRespVO> getCategory(@RequestParam("id") Long id) { | |
| 71 | + InfraCategoryDO category = categoryService.getCategory(id); | |
| 72 | + return success(BeanUtils.toBean(category, InfraCategoryRespVO.class)); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @GetMapping("/list") | |
| 76 | + @Operation(summary = "获得分类列表") | |
| 77 | + @PreAuthorize("@ss.hasPermission('infra:category:query')") | |
| 78 | + public CommonResult<List<InfraCategoryRespVO>> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) { | |
| 79 | + List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO); | |
| 80 | + return success(BeanUtils.toBean(list, InfraCategoryRespVO.class)); | |
| 81 | + } | |
| 82 | + | |
| 83 | + @GetMapping("/export-excel") | |
| 84 | + @Operation(summary = "导出分类 Excel") | |
| 85 | + @PreAuthorize("@ss.hasPermission('infra:category:export')") | |
| 86 | + @ApiAccessLog(operateType = EXPORT) | |
| 87 | + public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO, | |
| 88 | + HttpServletResponse response) throws IOException { | |
| 89 | + List<InfraCategoryDO> list = categoryService.getCategoryList(listReqVO); | |
| 90 | + // 导出 Excel | |
| 91 | + ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class, | |
| 92 | + BeanUtils.toBean(list, InfraCategoryRespVO.class)); | |
| 93 | + } | |
| 94 | + | |
| 95 | +} | |
| 0 | 96 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 40 | +} | |
| 0 | 41 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 16 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 35 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 cn.idev.excel.annotation.*; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - 分类 Response VO") | |
| 9 | +@Data | |
| 10 | +@ExcelIgnoreUnannotated | |
| 11 | +public class InfraCategoryRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 14 | + @ExcelProperty("编号") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 18 | + @ExcelProperty("名字") | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | |
| 22 | + @ExcelProperty("父编号") | |
| 23 | + private Long parentId; | |
| 24 | + | |
| 25 | +} | |
| 0 | 26 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 jakarta.validation.constraints.*; | |
| 7 | + | |
| 8 | +@Schema(description = "管理后台 - 分类新增/修改 Request VO") | |
| 9 | +@Data | |
| 10 | +public class InfraCategorySaveReqVO { | |
| 11 | + | |
| 12 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 16 | + @NotEmpty(message = "名字不能为空") | |
| 17 | + private String name; | |
| 18 | + | |
| 19 | + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | |
| 20 | + @NotNull(message = "父编号不能为空") | |
| 21 | + private Long parentId; | |
| 22 | + | |
| 23 | +} | |
| 0 | 24 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/java/InfraCategoryService
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | +import jakarta.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 | + * | |
| 43 | + * @param id 编号 | |
| 44 | + * @return 分类 | |
| 45 | + */ | |
| 46 | + InfraCategoryDO getCategory(Long id); | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 获得分类列表 | |
| 50 | + * | |
| 51 | + * @param listReqVO 查询条件 | |
| 52 | + * @return 分类列表 | |
| 53 | + */ | |
| 54 | + List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO); | |
| 55 | + | |
| 56 | +} | |
| 0 | 57 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/java/InfraCategoryServiceImpl
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import org.springframework.stereotype.Service; | |
| 5 | +import jakarta.annotation.Resource; | |
| 6 | +import org.springframework.validation.annotation.Validated; | |
| 7 | +import org.springframework.transaction.annotation.Transactional; | |
| 8 | + | |
| 9 | +import java.util.*; | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; | |
| 12 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 13 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 14 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 15 | + | |
| 16 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; | |
| 17 | + | |
| 18 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 19 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 20 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; | |
| 21 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * 分类 Service 实现类 | |
| 25 | + * | |
| 26 | + * @author 芋道源码 | |
| 27 | + */ | |
| 28 | +@Service | |
| 29 | +@Validated | |
| 30 | +public class InfraCategoryServiceImpl implements InfraCategoryService { | |
| 31 | + | |
| 32 | + @Resource | |
| 33 | + private InfraCategoryMapper categoryMapper; | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public Long createCategory(InfraCategorySaveReqVO createReqVO) { | |
| 37 | + // 校验父编号的有效性 | |
| 38 | + validateParentCategory(null, createReqVO.getParentId()); | |
| 39 | + // 校验名字的唯一性 | |
| 40 | + validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); | |
| 41 | + | |
| 42 | + // 插入 | |
| 43 | + InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class); | |
| 44 | + categoryMapper.insert(category); | |
| 45 | + | |
| 46 | + // 返回 | |
| 47 | + return category.getId(); | |
| 48 | + } | |
| 49 | + | |
| 50 | + @Override | |
| 51 | + public void updateCategory(InfraCategorySaveReqVO updateReqVO) { | |
| 52 | + // 校验存在 | |
| 53 | + validateCategoryExists(updateReqVO.getId()); | |
| 54 | + // 校验父编号的有效性 | |
| 55 | + validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId()); | |
| 56 | + // 校验名字的唯一性 | |
| 57 | + validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); | |
| 58 | + | |
| 59 | + // 更新 | |
| 60 | + InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class); | |
| 61 | + categoryMapper.updateById(updateObj); | |
| 62 | + } | |
| 63 | + | |
| 64 | + @Override | |
| 65 | + public void deleteCategory(Long id) { | |
| 66 | + // 校验存在 | |
| 67 | + validateCategoryExists(id); | |
| 68 | + // 校验是否有子分类 | |
| 69 | + if (categoryMapper.selectCountByParentId(id) > 0) { | |
| 70 | + throw exception(CATEGORY_EXITS_CHILDREN); | |
| 71 | + } | |
| 72 | + // 删除 | |
| 73 | + categoryMapper.deleteById(id); | |
| 74 | + } | |
| 75 | + | |
| 76 | + | |
| 77 | + private void validateCategoryExists(Long id) { | |
| 78 | + if (categoryMapper.selectById(id) == null) { | |
| 79 | + throw exception(CATEGORY_NOT_EXISTS); | |
| 80 | + } | |
| 81 | + } | |
| 82 | + | |
| 83 | + private void validateParentCategory(Long id, Long parentId) { | |
| 84 | + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { | |
| 85 | + return; | |
| 86 | + } | |
| 87 | + // 1. 不能设置自己为父分类 | |
| 88 | + if (Objects.equals(id, parentId)) { | |
| 89 | + throw exception(CATEGORY_PARENT_ERROR); | |
| 90 | + } | |
| 91 | + // 2. 父分类不存在 | |
| 92 | + CategoryDO parentCategory = categoryMapper.selectById(parentId); | |
| 93 | + if (parentCategory == null) { | |
| 94 | + throw exception(CATEGORY_PARENT_NOT_EXITS); | |
| 95 | + } | |
| 96 | + // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 | |
| 97 | + if (id == null) { // id 为空,说明新增,不需要考虑环路 | |
| 98 | + return; | |
| 99 | + } | |
| 100 | + for (int i = 0; i < Short.MAX_VALUE; i++) { | |
| 101 | + // 3.1 校验环路 | |
| 102 | + parentId = parentCategory.getParentId(); | |
| 103 | + if (Objects.equals(id, parentId)) { | |
| 104 | + throw exception(CATEGORY_PARENT_IS_CHILD); | |
| 105 | + } | |
| 106 | + // 3.2 继续递归下一级父分类 | |
| 107 | + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { | |
| 108 | + break; | |
| 109 | + } | |
| 110 | + parentCategory = categoryMapper.selectById(parentId); | |
| 111 | + if (parentCategory == null) { | |
| 112 | + break; | |
| 113 | + } | |
| 114 | + } | |
| 115 | + } | |
| 116 | + | |
| 117 | + private void validateCategoryNameUnique(Long id, Long parentId, String name) { | |
| 118 | + CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name); | |
| 119 | + if (category == null) { | |
| 120 | + return; | |
| 121 | + } | |
| 122 | + // 如果 id 为空,说明不用比较是否为相同 id 的分类 | |
| 123 | + if (id == null) { | |
| 124 | + throw exception(CATEGORY_NAME_DUPLICATE); | |
| 125 | + } | |
| 126 | + if (!Objects.equals(category.getId(), id)) { | |
| 127 | + throw exception(CATEGORY_NAME_DUPLICATE); | |
| 128 | + } | |
| 129 | + } | |
| 130 | + | |
| 131 | + @Override | |
| 132 | + public InfraCategoryDO getCategory(Long id) { | |
| 133 | + return categoryMapper.selectById(id); | |
| 134 | + } | |
| 135 | + | |
| 136 | + @Override | |
| 137 | + public List<InfraCategoryDO> getCategoryList(InfraCategoryListReqVO listReqVO) { | |
| 138 | + return categoryMapper.selectList(listReqVO); | |
| 139 | + } | |
| 140 | + | |
| 141 | +} | |
| 0 | 142 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | + | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | + | |
| 8 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 9 | + | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; | |
| 12 | +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; | |
| 13 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 14 | + | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import org.springframework.context.annotation.Import; | |
| 17 | +import java.util.*; | |
| 18 | +import java.time.LocalDateTime; | |
| 19 | + | |
| 20 | +import static cn.hutool.core.util.RandomUtil.*; | |
| 21 | +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; | |
| 22 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; | |
| 23 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; | |
| 26 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; | |
| 27 | +import static org.junit.jupiter.api.Assertions.*; | |
| 28 | +import static org.mockito.Mockito.*; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + * {@link InfraCategoryServiceImpl} 的单元测试类 | |
| 32 | + * | |
| 33 | + * @author 芋道源码 | |
| 34 | + */ | |
| 35 | +@Import(InfraCategoryServiceImpl.class) | |
| 36 | +public class InfraCategoryServiceImplTest extends BaseDbUnitTest { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private InfraCategoryServiceImpl categoryService; | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private InfraCategoryMapper categoryMapper; | |
| 43 | + | |
| 44 | + @Test | |
| 45 | + public void testCreateCategory_success() { | |
| 46 | + // 准备参数 | |
| 47 | + InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null); | |
| 48 | + | |
| 49 | + // 调用 | |
| 50 | + Long categoryId = categoryService.createCategory(createReqVO); | |
| 51 | + // 断言 | |
| 52 | + assertNotNull(categoryId); | |
| 53 | + // 校验记录的属性是否正确 | |
| 54 | + InfraCategoryDO category = categoryMapper.selectById(categoryId); | |
| 55 | + assertPojoEquals(createReqVO, category, "id"); | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Test | |
| 59 | + public void testUpdateCategory_success() { | |
| 60 | + // mock 数据 | |
| 61 | + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); | |
| 62 | + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 | |
| 63 | + // 准备参数 | |
| 64 | + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> { | |
| 65 | + o.setId(dbCategory.getId()); // 设置更新的 ID | |
| 66 | + }); | |
| 67 | + | |
| 68 | + // 调用 | |
| 69 | + categoryService.updateCategory(updateReqVO); | |
| 70 | + // 校验是否更新正确 | |
| 71 | + InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 | |
| 72 | + assertPojoEquals(updateReqVO, category); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Test | |
| 76 | + public void testUpdateCategory_notExists() { | |
| 77 | + // 准备参数 | |
| 78 | + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class); | |
| 79 | + | |
| 80 | + // 调用, 并断言异常 | |
| 81 | + assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Test | |
| 85 | + public void testDeleteCategory_success() { | |
| 86 | + // mock 数据 | |
| 87 | + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); | |
| 88 | + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 | |
| 89 | + // 准备参数 | |
| 90 | + Long id = dbCategory.getId(); | |
| 91 | + | |
| 92 | + // 调用 | |
| 93 | + categoryService.deleteCategory(id); | |
| 94 | + // 校验数据不存在了 | |
| 95 | + assertNull(categoryMapper.selectById(id)); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Test | |
| 99 | + public void testDeleteCategory_notExists() { | |
| 100 | + // 准备参数 | |
| 101 | + Long id = randomLongId(); | |
| 102 | + | |
| 103 | + // 调用, 并断言异常 | |
| 104 | + assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); | |
| 105 | + } | |
| 106 | + | |
| 107 | + @Test | |
| 108 | + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 109 | + public void testGetCategoryList() { | |
| 110 | + // mock 数据 | |
| 111 | + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到 | |
| 112 | + o.setName(null); | |
| 113 | + }); | |
| 114 | + categoryMapper.insert(dbCategory); | |
| 115 | + // 测试 name 不匹配 | |
| 116 | + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); | |
| 117 | + // 准备参数 | |
| 118 | + InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO(); | |
| 119 | + reqVO.setName(null); | |
| 120 | + | |
| 121 | + // 调用 | |
| 122 | + List<InfraCategoryDO> list = categoryService.getCategoryList(reqVO); | |
| 123 | + // 断言 | |
| 124 | + assertEquals(1, list.size()); | |
| 125 | + assertPojoEquals(dbCategory, list.get(0)); | |
| 126 | + } | |
| 127 | + | |
| 128 | +} | |
| 0 | 129 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 11 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | +SELECT @parentId := LAST_INSERT_ID(); | |
| 13 | + | |
| 14 | +-- 按钮 SQL | |
| 15 | +INSERT INTO system_menu( | |
| 16 | + name, permission, type, sort, parent_id, | |
| 17 | + path, icon, component, status | |
| 18 | +) | |
| 19 | +VALUES ( | |
| 20 | + '分类查询', 'infra:category:query', 3, 1, @parentId, | |
| 21 | + '', '', '', 0 | |
| 22 | +); | |
| 23 | +INSERT INTO system_menu( | |
| 24 | + name, permission, type, sort, parent_id, | |
| 25 | + path, icon, component, status | |
| 26 | +) | |
| 27 | +VALUES ( | |
| 28 | + '分类创建', 'infra:category:create', 3, 2, @parentId, | |
| 29 | + '', '', '', 0 | |
| 30 | +); | |
| 31 | +INSERT INTO system_menu( | |
| 32 | + name, permission, type, sort, parent_id, | |
| 33 | + path, icon, component, status | |
| 34 | +) | |
| 35 | +VALUES ( | |
| 36 | + '分类更新', 'infra:category:update', 3, 3, @parentId, | |
| 37 | + '', '', '', 0 | |
| 38 | +); | |
| 39 | +INSERT INTO system_menu( | |
| 40 | + name, permission, type, sort, parent_id, | |
| 41 | + path, icon, component, status | |
| 42 | +) | |
| 43 | +VALUES ( | |
| 44 | + '分类删除', 'infra:category:delete', 3, 4, @parentId, | |
| 45 | + '', '', '', 0 | |
| 46 | +); | |
| 47 | +INSERT INTO system_menu( | |
| 48 | + name, permission, type, sort, parent_id, | |
| 49 | + path, icon, component, status | |
| 50 | +) | |
| 51 | +VALUES ( | |
| 52 | + '分类导出', 'infra:category:export', 3, 5, @parentId, | |
| 53 | + '', '', '', 0 | |
| 54 | +); | |
| 0 | 55 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/ts/index
0 → 100644
| 1 | +import { http } from '@/http/http' | |
| 2 | + | |
| 3 | +/** 分类信息 */ | |
| 4 | +export interface Category { | |
| 5 | + id?: number | |
| 6 | + name: string | |
| 7 | + parentId: number | |
| 8 | + children?: Category[] | |
| 9 | +} | |
| 10 | + | |
| 11 | +/** 获取分类列表 */ | |
| 12 | +export function getCategoryList(params?: Record<string, any>) { | |
| 13 | + return http.get<Category[]>('/infra/category/list', params) | |
| 14 | +} | |
| 15 | + | |
| 16 | +/** 获取分类详情 */ | |
| 17 | +export function getCategory(id: number) { | |
| 18 | + return http.get<Category>(`/infra/category/get?id=${id}`) | |
| 19 | +} | |
| 20 | + | |
| 21 | +/** 创建分类 */ | |
| 22 | +export function createCategory(data: Category) { | |
| 23 | + return http.post<number>('/infra/category/create', data) | |
| 24 | +} | |
| 25 | + | |
| 26 | +/** 更新分类 */ | |
| 27 | +export function updateCategory(data: Category) { | |
| 28 | + return http.put<boolean>('/infra/category/update', data) | |
| 29 | +} | |
| 30 | + | |
| 31 | +/** 删除分类 */ | |
| 32 | +export function deleteCategory(id: number) { | |
| 33 | + return http.delete<boolean>(`/infra/category/delete?id=${id}`) | |
| 34 | +} | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/vue/breadcrumb
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="bg-white px-24rpx py-16rpx"> | |
| 3 | + <scroll-view scroll-x class="whitespace-nowrap"> | |
| 4 | + <view class="inline-flex items-center"> | |
| 5 | + <view | |
| 6 | + class="flex items-center text-28rpx" | |
| 7 | + :class="items.length > 0 ? 'text-[#1890ff]' : 'text-[#333]'" | |
| 8 | + @click="handleClick(-1)" | |
| 9 | + > | |
| 10 | + <text>全部分类</text> | |
| 11 | + </view> | |
| 12 | + <template v-for="(item, index) in items" :key="item.id"> | |
| 13 | + <wd-icon name="arrow-right" size="12px" color="#999" custom-class="mx-8rpx" /> | |
| 14 | + <view | |
| 15 | + class="flex items-center text-28rpx" | |
| 16 | + :class="index < items.length - 1 ? 'text-[#1890ff]' : 'text-[#333]'" | |
| 17 | + @click="handleClick(index)" | |
| 18 | + > | |
| 19 | + <text>{{ item.name }}</text> | |
| 20 | + </view> | |
| 21 | + </template> | |
| 22 | + </view> | |
| 23 | + </scroll-view> | |
| 24 | + </view> | |
| 25 | +</template> | |
| 26 | + | |
| 27 | +<script lang="ts" setup> | |
| 28 | +import { ref, watch } from 'vue' | |
| 29 | + | |
| 30 | +interface BreadcrumbItem { | |
| 31 | + id: number | |
| 32 | + name: string | |
| 33 | +} | |
| 34 | + | |
| 35 | +const props = defineProps<{ | |
| 36 | + modelValue: number | |
| 37 | +}>() | |
| 38 | + | |
| 39 | +const emit = defineEmits<{ | |
| 40 | + 'update:modelValue': [value: number] | |
| 41 | +}>() | |
| 42 | + | |
| 43 | +const items = ref<BreadcrumbItem[]>([]) // 面包屑数据 | |
| 44 | + | |
| 45 | +/** 监听外部值变化 */ | |
| 46 | +watch(() => props.modelValue, (value) => { | |
| 47 | + if (value === 0) { | |
| 48 | + items.value = [] | |
| 49 | + } | |
| 50 | +}) | |
| 51 | + | |
| 52 | +/** 点击面包屑 */ | |
| 53 | +function handleClick(index: number) { | |
| 54 | + if (index === -1) { | |
| 55 | + reset() | |
| 56 | + return | |
| 57 | + } | |
| 58 | + if (index < items.value.length - 1) { | |
| 59 | + items.value = items.value.slice(0, index + 1) | |
| 60 | + emit('update:modelValue', items.value[index].id) | |
| 61 | + } | |
| 62 | +} | |
| 63 | + | |
| 64 | +/** 进入子层级 */ | |
| 65 | +function enter(item: BreadcrumbItem) { | |
| 66 | + items.value.push(item) | |
| 67 | + emit('update:modelValue', item.id) | |
| 68 | +} | |
| 69 | + | |
| 70 | +/** 返回上一层级 */ | |
| 71 | +function back(): boolean { | |
| 72 | + if (items.value.length === 0) { | |
| 73 | + return false | |
| 74 | + } | |
| 75 | + items.value.pop() | |
| 76 | + emit('update:modelValue', items.value.at(-1)?.id ?? 0) | |
| 77 | + return true | |
| 78 | +} | |
| 79 | + | |
| 80 | +/** 重置面包屑 */ | |
| 81 | +function reset() { | |
| 82 | + items.value = [] | |
| 83 | + emit('update:modelValue', 0) | |
| 84 | +} | |
| 85 | + | |
| 86 | +defineExpose({ back, enter, reset }) | |
| 87 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/vue/detail/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="分类详情" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 详情内容 --> | |
| 11 | + <view> | |
| 12 | + <wd-cell-group border> | |
| 13 | + <wd-cell title="编号" :value="formData?.id ?? '-'" /> | |
| 14 | + <wd-cell title="名字" :value="formData?.name ?? '-'" /> | |
| 15 | + <wd-cell title="父编号" :value="formData?.parentId ?? '-'" /> | |
| 16 | + </wd-cell-group> | |
| 17 | + </view> | |
| 18 | + | |
| 19 | + <!-- 底部操作按钮 --> | |
| 20 | + <view class="yd-detail-footer"> | |
| 21 | + <view class="yd-detail-footer-actions"> | |
| 22 | + <wd-button | |
| 23 | + v-if="hasAccessByCodes(['infra:category:update'])" | |
| 24 | + class="flex-1" type="warning" @click="handleEdit" | |
| 25 | + > | |
| 26 | + 编辑 | |
| 27 | + </wd-button> | |
| 28 | + <wd-button | |
| 29 | + v-if="hasAccessByCodes(['infra:category:delete'])" | |
| 30 | + class="flex-1" type="danger" :loading="deleting" @click="handleDelete" | |
| 31 | + > | |
| 32 | + 删除 | |
| 33 | + </wd-button> | |
| 34 | + </view> | |
| 35 | + </view> | |
| 36 | + </view> | |
| 37 | +</template> | |
| 38 | + | |
| 39 | +<script lang="ts" setup> | |
| 40 | +import type { Category } from '@/api/infra/demo' | |
| 41 | +import { onUnload } from '@dcloudio/uni-app' | |
| 42 | +import { onMounted, ref } from 'vue' | |
| 43 | +import { useDialog } from '@wot-ui/ui/components/wd-dialog' | |
| 44 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 45 | +import { deleteCategory, getCategory } from '@/api/infra/demo' | |
| 46 | +import { useAccess } from '@/hooks/useAccess' | |
| 47 | +import { delay, navigateBackPlus } from '@/utils' | |
| 48 | + | |
| 49 | +const props = defineProps<{ | |
| 50 | + id?: number | |
| 51 | +}>() | |
| 52 | + | |
| 53 | +definePage({ | |
| 54 | + style: { | |
| 55 | + navigationBarTitleText: '', | |
| 56 | + navigationStyle: 'custom', | |
| 57 | + }, | |
| 58 | +}) | |
| 59 | + | |
| 60 | +const { hasAccessByCodes } = useAccess() | |
| 61 | +const dialog = useDialog() | |
| 62 | +const toast = useToast() | |
| 63 | +const formData = ref<Category>() // 详情数据 | |
| 64 | +const deleting = ref(false) // 删除状态 | |
| 65 | + | |
| 66 | +/** 返回上一页 */ | |
| 67 | +function handleBack() { | |
| 68 | + navigateBackPlus('/pages-infra/demo/index') | |
| 69 | +} | |
| 70 | + | |
| 71 | +/** 加载分类详情 */ | |
| 72 | +async function getDetail() { | |
| 73 | + if (!props.id || deleting.value) { | |
| 74 | + return | |
| 75 | + } | |
| 76 | + try { | |
| 77 | + toast.loading('加载中...') | |
| 78 | + formData.value = await getCategory(props.id) | |
| 79 | + } finally { | |
| 80 | + toast.close() | |
| 81 | + } | |
| 82 | +} | |
| 83 | + | |
| 84 | +/** 编辑分类 */ | |
| 85 | +function handleEdit() { | |
| 86 | + uni.navigateTo({ | |
| 87 | + url: `/pages-infra/demo/form/index?id=${props.id}`, | |
| 88 | + }) | |
| 89 | +} | |
| 90 | + | |
| 91 | +/** 删除分类 */ | |
| 92 | +async function handleDelete() { | |
| 93 | + if (!props.id) { | |
| 94 | + return | |
| 95 | + } | |
| 96 | + try { | |
| 97 | + await dialog.confirm({ | |
| 98 | + title: '提示', | |
| 99 | + msg: '确定要删除该分类吗?', | |
| 100 | + }) | |
| 101 | + } catch { | |
| 102 | + return | |
| 103 | + } | |
| 104 | + deleting.value = true | |
| 105 | + try { | |
| 106 | + await deleteCategory(props.id) | |
| 107 | + toast.success('删除成功') | |
| 108 | + uni.$emit('infra:demo:reload') | |
| 109 | + delay(handleBack) | |
| 110 | + } finally { | |
| 111 | + deleting.value = false | |
| 112 | + } | |
| 113 | +} | |
| 114 | + | |
| 115 | +/** 初始化 */ | |
| 116 | +onMounted(() => { | |
| 117 | + uni.$on('infra:demo:reload', getDetail) | |
| 118 | + getDetail() | |
| 119 | +}) | |
| 120 | + | |
| 121 | +/** 卸载 */ | |
| 122 | +onUnload(() => { | |
| 123 | + uni.$off('infra:demo:reload', getDetail) | |
| 124 | +}) | |
| 125 | +</script> | |
| 126 | + | |
| 127 | +<style lang="scss" scoped> | |
| 128 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/vue/form/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + :title="getTitle" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 表单区域 --> | |
| 11 | + <view> | |
| 12 | + <wd-form ref="formRef" :model="formData" :schema="formSchema"> | |
| 13 | + <wd-cell-group border> | |
| 14 | + <wd-form-item title="名字" title-width="180rpx" prop="name"> | |
| 15 | + <wd-input | |
| 16 | + v-model="formData.name" | |
| 17 | + clearable | |
| 18 | + placeholder="请输入名字" | |
| 19 | + /> | |
| 20 | + </wd-form-item> | |
| 21 | + <yd-tree-select | |
| 22 | + v-model="formData.parentId" | |
| 23 | + :data="treeData" | |
| 24 | + label="父编号" | |
| 25 | + prop="parentId" | |
| 26 | + :props="{ label: 'name', value: 'id' }" | |
| 27 | + placeholder="请选择父编号" | |
| 28 | + /> | |
| 29 | + </wd-cell-group> | |
| 30 | + </wd-form> | |
| 31 | + </view> | |
| 32 | + | |
| 33 | + <!-- 底部保存按钮 --> | |
| 34 | + <view class="yd-detail-footer"> | |
| 35 | + <wd-button | |
| 36 | + type="primary" | |
| 37 | + block | |
| 38 | + :loading="formLoading" | |
| 39 | + @click="handleSubmit" | |
| 40 | + > | |
| 41 | + 保存 | |
| 42 | + </wd-button> | |
| 43 | + </view> | |
| 44 | + </view> | |
| 45 | +</template> | |
| 46 | + | |
| 47 | +<script lang="ts" setup> | |
| 48 | +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types' | |
| 49 | +import type { | |
| 50 | + Category, | |
| 51 | +} from '@/api/infra/demo' | |
| 52 | +import { computed, onMounted, ref } from 'vue' | |
| 53 | +import { useToast } from '@wot-ui/ui/components/wd-toast' | |
| 54 | +import { createCategory, getCategory, getCategoryList, updateCategory } from '@/api/infra/demo' | |
| 55 | +import { delay, navigateBackPlus } from '@/utils' | |
| 56 | +import { createFormSchema } from '@/utils/wot' | |
| 57 | +import { handleTree } from '@/utils/tree' | |
| 58 | + | |
| 59 | +const props = defineProps<{ | |
| 60 | + id?: number | |
| 61 | + parentId?: number | |
| 62 | +}>() | |
| 63 | + | |
| 64 | +definePage({ | |
| 65 | + style: { | |
| 66 | + navigationBarTitleText: '', | |
| 67 | + navigationStyle: 'custom', | |
| 68 | + }, | |
| 69 | +}) | |
| 70 | + | |
| 71 | +const toast = useToast() | |
| 72 | +const getTitle = computed(() => props.id ? '编辑分类' : '新增分类') | |
| 73 | +const formLoading = ref(false) // 表单提交状态 | |
| 74 | +const formData = ref<Category>({ | |
| 75 | + id: undefined, | |
| 76 | + name: '', | |
| 77 | + parentId: props.parentId ?? 0, | |
| 78 | +}) // 表单数据 | |
| 79 | +const formSchema = createFormSchema({ | |
| 80 | + name: [{ required: true, message: '名字不能为空' }], | |
| 81 | + parentId: [{ required: true, message: '父编号不能为空' }], | |
| 82 | +}) // 表单校验规则 | |
| 83 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 84 | +const treeData = ref<Category[]>([]) // 父级树形选项 | |
| 85 | + | |
| 86 | +/** 返回上一页 */ | |
| 87 | +function handleBack() { | |
| 88 | + navigateBackPlus('/pages-infra/demo/index') | |
| 89 | +} | |
| 90 | + | |
| 91 | +/** 加载分类详情 */ | |
| 92 | +async function getDetail() { | |
| 93 | + if (!props.id) { | |
| 94 | + return | |
| 95 | + } | |
| 96 | + formData.value = await getCategory(props.id) | |
| 97 | +} | |
| 98 | + | |
| 99 | +/** 提交表单 */ | |
| 100 | +async function handleSubmit() { | |
| 101 | + const { valid } = await formRef.value.validate() | |
| 102 | + if (!valid) { | |
| 103 | + return | |
| 104 | + } | |
| 105 | + | |
| 106 | + formLoading.value = true | |
| 107 | + try { | |
| 108 | + const data = formData.value | |
| 109 | + if (props.id) { | |
| 110 | + await updateCategory(data) | |
| 111 | + toast.success('修改成功') | |
| 112 | + } else { | |
| 113 | + await createCategory(data) | |
| 114 | + toast.success('新增成功') | |
| 115 | + } | |
| 116 | + uni.$emit('infra:demo:reload') | |
| 117 | + delay(handleBack) | |
| 118 | + } finally { | |
| 119 | + formLoading.value = false | |
| 120 | + } | |
| 121 | +} | |
| 122 | + | |
| 123 | +/** 初始化 */ | |
| 124 | +onMounted(async () => { | |
| 125 | + const data = await getCategoryList() | |
| 126 | + treeData.value = [{ | |
| 127 | + id: 0, | |
| 128 | + name: '顶级分类', | |
| 129 | + parentId: 0, | |
| 130 | + children: handleTree(data, 'id', 'parentId'), | |
| 131 | + } as Category] | |
| 132 | + getDetail() | |
| 133 | +}) | |
| 134 | +</script> | |
| 135 | + | |
| 136 | +<style lang="scss" scoped> | |
| 137 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/vue/index
0 → 100644
| 1 | +<template> | |
| 2 | + <view class="yd-page-container"> | |
| 3 | + <!-- 顶部导航栏 --> | |
| 4 | + <wd-navbar | |
| 5 | + title="分类管理" | |
| 6 | + left-arrow placeholder safe-area-inset-top fixed | |
| 7 | + @click-left="handleBack" | |
| 8 | + /> | |
| 9 | + | |
| 10 | + <!-- 搜索组件 --> | |
| 11 | + <SearchForm @search="handleQuery" @reset="handleReset" /> | |
| 12 | + | |
| 13 | + <!-- 面包屑导航 --> | |
| 14 | + <Breadcrumb ref="breadcrumbRef" v-model="currentParentId" /> | |
| 15 | + | |
| 16 | + <!-- 分类列表 --> | |
| 17 | + <view class="p-24rpx"> | |
| 18 | + <view | |
| 19 | + v-for="item in currentList" | |
| 20 | + :key="item.id" | |
| 21 | + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 22 | + > | |
| 23 | + <view class="p-24rpx" @click="handleDetail(item)"> | |
| 24 | + <view class="flex items-center justify-between"> | |
| 25 | + <view class="flex min-w-0 items-center"> | |
| 26 | + <view class="mr-16rpx h-48rpx w-48rpx shrink-0 flex items-center justify-center rounded-8rpx bg-[#1890ff]"> | |
| 27 | + <wd-icon name="folder" size="20px" color="#fff" /> | |
| 28 | + </view> | |
| 29 | + <view class="truncate text-32rpx text-[#333] font-semibold"> | |
| 30 | + {{ item.name }} | |
| 31 | + </view> | |
| 32 | + </view> | |
| 33 | + <view | |
| 34 | + v-if="item.children?.length" | |
| 35 | + class="ml-16rpx shrink-0 flex items-center" | |
| 36 | + @click.stop="handleEnterChildren(item)" | |
| 37 | + > | |
| 38 | + <text class="text-24rpx text-[#1890ff]">子级 ({{ item.children.length }})</text> | |
| 39 | + <wd-icon name="arrow-right" size="12px" color="#1890ff" /> | |
| 40 | + </view> | |
| 41 | + </view> | |
| 42 | + </view> | |
| 43 | + </view> | |
| 44 | + | |
| 45 | + <!-- 空状态 --> | |
| 46 | + <view v-if="!loading && currentList.length === 0" class="py-100rpx text-center"> | |
| 47 | + <wd-empty icon="content" tip="暂无分类数据" /> | |
| 48 | + </view> | |
| 49 | + </view> | |
| 50 | + | |
| 51 | + <!-- 新增按钮 --> | |
| 52 | + <wd-fab | |
| 53 | + v-if="hasAccessByCodes(['infra:category:create'])" | |
| 54 | + position="right-bottom" | |
| 55 | + type="primary" | |
| 56 | + :expandable="false" | |
| 57 | + @click="handleAdd" | |
| 58 | + /> | |
| 59 | + </view> | |
| 60 | +</template> | |
| 61 | + | |
| 62 | +<script lang="ts" setup> | |
| 63 | +import type { Category } from '@/api/infra/demo' | |
| 64 | +import { onUnload } from '@dcloudio/uni-app' | |
| 65 | +import { computed, onMounted, ref } from 'vue' | |
| 66 | +import { getCategoryList } from '@/api/infra/demo' | |
| 67 | +import { useAccess } from '@/hooks/useAccess' | |
| 68 | +import { navigateBackPlus } from '@/utils' | |
| 69 | +import { findChildren, handleTree } from '@/utils/tree' | |
| 70 | +import Breadcrumb from './components/breadcrumb.vue' | |
| 71 | +import SearchForm from './components/search-form.vue' | |
| 72 | + | |
| 73 | +definePage({ | |
| 74 | + style: { | |
| 75 | + navigationBarTitleText: '', | |
| 76 | + navigationStyle: 'custom', | |
| 77 | + }, | |
| 78 | +}) | |
| 79 | + | |
| 80 | +const { hasAccessByCodes } = useAccess() | |
| 81 | +const loading = ref(false) // 列表加载状态 | |
| 82 | +const list = ref<Category[]>([]) // 完整树形列表 | |
| 83 | +const currentParentId = ref<number>(0) // 当前层级的父节点编号 | |
| 84 | +const currentList = computed(() => currentParentId.value === 0 | |
| 85 | + ? list.value | |
| 86 | + : findChildren(list.value, currentParentId.value, 'id')) // 当前层级列表 | |
| 87 | +const breadcrumbRef = ref<InstanceType<typeof Breadcrumb>>() // 面包屑组件引用 | |
| 88 | +const queryParams = ref<Record<string, any>>({}) // 查询参数 | |
| 89 | + | |
| 90 | +/** 返回上一页或上一层级 */ | |
| 91 | +function handleBack() { | |
| 92 | + if (!breadcrumbRef.value?.back()) { | |
| 93 | + navigateBackPlus() | |
| 94 | + } | |
| 95 | +} | |
| 96 | + | |
| 97 | +/** 进入子层级 */ | |
| 98 | +function handleEnterChildren(item: Category) { | |
| 99 | + breadcrumbRef.value?.enter({ id: item.id!, name: item.name }) | |
| 100 | +} | |
| 101 | + | |
| 102 | +/** 查询分类列表 */ | |
| 103 | +async function getList() { | |
| 104 | + loading.value = true | |
| 105 | + try { | |
| 106 | + list.value = handleTree(await getCategoryList(queryParams.value), 'id', 'parentId') | |
| 107 | + } finally { | |
| 108 | + loading.value = false | |
| 109 | + } | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** 搜索按钮操作 */ | |
| 113 | +function handleQuery(data?: Record<string, any>) { | |
| 114 | + queryParams.value = { ...data } | |
| 115 | + currentParentId.value = 0 | |
| 116 | + breadcrumbRef.value?.reset() | |
| 117 | + getList() | |
| 118 | +} | |
| 119 | + | |
| 120 | +/** 重置按钮操作 */ | |
| 121 | +function handleReset() { | |
| 122 | + handleQuery() | |
| 123 | +} | |
| 124 | + | |
| 125 | +/** 新增分类 */ | |
| 126 | +function handleAdd() { | |
| 127 | + uni.navigateTo({ url: `/pages-infra/demo/form/index?parentId=${currentParentId.value}` }) | |
| 128 | +} | |
| 129 | + | |
| 130 | +/** 查看详情 */ | |
| 131 | +function handleDetail(item: Category) { | |
| 132 | + uni.navigateTo({ url: `/pages-infra/demo/detail/index?id=${item.id}` }) | |
| 133 | +} | |
| 134 | + | |
| 135 | +/** 初始化 */ | |
| 136 | +onMounted(() => { | |
| 137 | + getList() | |
| 138 | + uni.$on('infra:demo:reload', getList) | |
| 139 | +}) | |
| 140 | + | |
| 141 | +/** 卸载 */ | |
| 142 | +onUnload(() => { | |
| 143 | + uni.$off('infra:demo:reload', getList) | |
| 144 | +}) | |
| 145 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_tree/vue/search-form
0 → 100644
| 1 | +<template> | |
| 2 | + <!-- 搜索框入口 --> | |
| 3 | + <view @click="visible = true"> | |
| 4 | + <wd-search :placeholder="placeholder" hide-cancel disabled /> | |
| 5 | + </view> | |
| 6 | + | |
| 7 | + <!-- 搜索弹窗 --> | |
| 8 | + <wd-popup v-model="visible" position="top" @close="visible = false"> | |
| 9 | + <view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }"> | |
| 10 | + <view class="yd-search-form-item"> | |
| 11 | + <view class="yd-search-form-label"> | |
| 12 | + 名字 | |
| 13 | + </view> | |
| 14 | + <wd-input | |
| 15 | + v-model="formData.name" | |
| 16 | + placeholder="请输入名字" | |
| 17 | + clearable | |
| 18 | + /> | |
| 19 | + </view> | |
| 20 | + <view class="yd-search-form-actions"> | |
| 21 | + <wd-button class="flex-1" plain @click="handleReset"> | |
| 22 | + 重置 | |
| 23 | + </wd-button> | |
| 24 | + <wd-button class="flex-1" type="primary" @click="handleSearch"> | |
| 25 | + 搜索 | |
| 26 | + </wd-button> | |
| 27 | + </view> | |
| 28 | + </view> | |
| 29 | + </wd-popup> | |
| 30 | +</template> | |
| 31 | + | |
| 32 | +<script lang="ts" setup> | |
| 33 | +import { computed, reactive, ref } from 'vue' | |
| 34 | +import { getNavbarHeight } from '@/utils' | |
| 35 | + | |
| 36 | +const emit = defineEmits<{ | |
| 37 | + search: [data: Record<string, any>] | |
| 38 | + reset: [] | |
| 39 | +}>() | |
| 40 | + | |
| 41 | +const visible = ref(false) // 搜索弹窗显示状态 | |
| 42 | +const formData = reactive({ | |
| 43 | + name: undefined as string | undefined, | |
| 44 | +}) // 搜索表单数据 | |
| 45 | + | |
| 46 | +/** 搜索条件 placeholder 拼接 */ | |
| 47 | +const placeholder = computed(() => { | |
| 48 | + const conditions: string[] = [] | |
| 49 | + if (formData.name) { | |
| 50 | + conditions.push(`名字:${formData.name}`) | |
| 51 | + } | |
| 52 | + return conditions.length > 0 ? conditions.join(' | ') : '搜索分类' | |
| 53 | +}) | |
| 54 | + | |
| 55 | +/** 搜索按钮操作 */ | |
| 56 | +function handleSearch() { | |
| 57 | + visible.value = false | |
| 58 | + emit('search', { | |
| 59 | + ...formData, | |
| 60 | + }) | |
| 61 | +} | |
| 62 | + | |
| 63 | +/** 重置按钮操作 */ | |
| 64 | +function handleReset() { | |
| 65 | + formData.name = undefined | |
| 66 | + visible.value = false | |
| 67 | + emit('reset') | |
| 68 | +} | |
| 69 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_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 | 13 | \ No newline at end of file | ... | ... |